From d718306fb5fd57bd8caa94c797984f44748d21c6 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Sun, 22 Oct 2023 17:51:53 -0500 Subject: [PATCH 1/3] Remove ContainerStorable.EncodeAsElement Currently, EncodeAsElement is used to encode inlined array and map, while Encode is used to encode standalone array and map. This commit simplifies encoding API by using Encode() to encode both inlined/standalone array/map. --- array.go | 29 +++++++++---------- encode.go | 38 ++++++------------------ map.go | 75 ++++++++++++++++++++++-------------------------- storable.go | 9 ------ storable_test.go | 11 ------- 5 files changed, 56 insertions(+), 106 deletions(-) diff --git a/array.go b/array.go index 62b2aedd..b9c5a3d4 100644 --- a/array.go +++ b/array.go @@ -698,14 +698,14 @@ func DecodeInlinedArrayStorable( }, nil } -// EncodeAsElement encodes inlined array data slab. Encoding is +// encodeAsInlined encodes inlined array data slab. Encoding is // version 1 with CBOR tag having tag number CBORTagInlinedArray, // and tag contant as 3-element array: // // +------------------+----------------+----------+ // | extra data index | value ID index | elements | // +------------------+----------------+----------+ -func (a *ArrayDataSlab) EncodeAsElement(enc *Encoder, inlinedTypeInfo *InlinedExtraData) error { +func (a *ArrayDataSlab) encodeAsInlined(enc *Encoder) error { if a.extraData == nil { return NewEncodingError( fmt.Errorf("failed to encode non-root array data slab as inlined")) @@ -716,7 +716,7 @@ func (a *ArrayDataSlab) EncodeAsElement(enc *Encoder, inlinedTypeInfo *InlinedEx fmt.Errorf("failed to encode standalone array data slab as inlined")) } - extraDataIndex := inlinedTypeInfo.addArrayExtraData(a.extraData) + extraDataIndex := enc.inlinedExtraData.addArrayExtraData(a.extraData) if extraDataIndex > maxInlinedExtraDataIndex { return NewEncodingError( @@ -753,7 +753,7 @@ func (a *ArrayDataSlab) EncodeAsElement(enc *Encoder, inlinedTypeInfo *InlinedEx } // element 2: array elements - err = a.encodeElements(enc, inlinedTypeInfo) + err = a.encodeElements(enc) if err != nil { // err is already categorized by ArrayDataSlab.encodeElements(). return err @@ -784,8 +784,7 @@ func (a *ArrayDataSlab) EncodeAsElement(enc *Encoder, inlinedTypeInfo *InlinedEx func (a *ArrayDataSlab) Encode(enc *Encoder) error { if a.inlined { - return NewEncodingError( - fmt.Errorf("failed to encode inlined array data slab as standalone slab")) + return a.encodeAsInlined(enc) } // Encoding is done in two steps: @@ -793,15 +792,13 @@ func (a *ArrayDataSlab) Encode(enc *Encoder) error { // 1. Encode array elements using a new buffer while collecting inlined extra data from inlined elements. // 2. Encode slab with deduplicated inlined extra data and copy encoded elements from previous buffer. - inlinedTypes := newInlinedExtraData() - // Get a buffer from a pool to encode elements. elementBuf := getBuffer() defer putBuffer(elementBuf) elementEnc := NewEncoder(elementBuf, enc.encMode) - err := a.encodeElements(elementEnc, inlinedTypes) + err := a.encodeElements(elementEnc) if err != nil { // err is already categorized by Array.encodeElements(). return err @@ -831,7 +828,7 @@ func (a *ArrayDataSlab) Encode(enc *Encoder) error { h.setRoot() } - if !inlinedTypes.empty() { + if !elementEnc.inlinedExtraData.empty() { h.setHasInlinedSlabs() } @@ -851,8 +848,8 @@ func (a *ArrayDataSlab) Encode(enc *Encoder) error { } // Encode inlined extra data - if !inlinedTypes.empty() { - err = inlinedTypes.Encode(enc) + if !elementEnc.inlinedExtraData.empty() { + err = elementEnc.inlinedExtraData.Encode(enc) if err != nil { // err is already categorized by inlinedExtraData.Encode(). return err @@ -887,7 +884,7 @@ func (a *ArrayDataSlab) Encode(enc *Encoder) error { return nil } -func (a *ArrayDataSlab) encodeElements(enc *Encoder, inlinedTypeInfo *InlinedExtraData) error { +func (a *ArrayDataSlab) encodeElements(enc *Encoder) error { // Encode CBOR array size manually for fix-sized encoding enc.Scratch[0] = 0x80 | 25 @@ -908,10 +905,10 @@ func (a *ArrayDataSlab) encodeElements(enc *Encoder, inlinedTypeInfo *InlinedExt // Encode data slab content (array of elements) for _, e := range a.elements { - err = EncodeStorableAsElement(enc, e, inlinedTypeInfo) + err = e.Encode(enc) if err != nil { - // err is already categorized by encodeStorableAsElement(). - return err + // Wrap err as external error (if needed) because err is returned by Storable interface. + return wrapErrorfAsExternalErrorIfNeeded(err, "failed to encode array element") } } diff --git a/encode.go b/encode.go index 7d8eb3c4..213e7316 100644 --- a/encode.go +++ b/encode.go @@ -28,44 +28,22 @@ import ( // Encoder writes atree slabs to io.Writer. type Encoder struct { io.Writer - CBOR *cbor.StreamEncoder - Scratch [64]byte - encMode cbor.EncMode + CBOR *cbor.StreamEncoder + Scratch [64]byte + encMode cbor.EncMode + inlinedExtraData *InlinedExtraData } func NewEncoder(w io.Writer, encMode cbor.EncMode) *Encoder { streamEncoder := encMode.NewStreamEncoder(w) return &Encoder{ - Writer: w, - CBOR: streamEncoder, - encMode: encMode, + Writer: w, + CBOR: streamEncoder, + encMode: encMode, + inlinedExtraData: newInlinedExtraData(), } } -// EncodeStorableAsElement encodes storable as Array or OrderedMap element. -// Storable is encode as an inlined ArrayDataSlab or MapDataSlab if it is ArrayDataSlab or MapDataSlab. -func EncodeStorableAsElement(enc *Encoder, storable Storable, inlinedTypeInfo *InlinedExtraData) error { - - switch storable := storable.(type) { - - case ContainerStorable: - err := storable.EncodeAsElement(enc, inlinedTypeInfo) - if err != nil { - // Wrap err as external error (if needed) because err is returned by ContainerStorable interface. - return wrapErrorfAsExternalErrorIfNeeded(err, "failed to encode container storable as element") - } - - default: - err := storable.Encode(enc) - if err != nil { - // Wrap err as external error (if needed) because err is returned by Storable interface. - return wrapErrorfAsExternalErrorIfNeeded(err, "failed to encode storable as element") - } - } - - return nil -} - type StorableDecoder func( decoder *cbor.StreamDecoder, storableSlabID SlabID, diff --git a/map.go b/map.go index a4b52e81..3d23752b 100644 --- a/map.go +++ b/map.go @@ -148,7 +148,7 @@ type element interface { key Value, ) (MapKey, MapValue, element, error) - Encode(*Encoder, *InlinedExtraData) error + Encode(*Encoder) error hasPointer() bool @@ -215,7 +215,7 @@ type elements interface { Element(int) (element, error) - Encode(*Encoder, *InlinedExtraData) error + Encode(*Encoder) error hasPointer() bool @@ -586,7 +586,7 @@ func newSingleElementFromData(cborDec *cbor.StreamDecoder, decodeStorable Storab // Encode encodes singleElement to the given encoder. // // CBOR encoded array of 2 elements (key, value). -func (e *singleElement) Encode(enc *Encoder, inlinedTypeInfo *InlinedExtraData) error { +func (e *singleElement) Encode(enc *Encoder) error { // Encode CBOR array head for 2 elements err := enc.CBOR.EncodeRawBytes([]byte{0x82}) @@ -595,17 +595,17 @@ func (e *singleElement) Encode(enc *Encoder, inlinedTypeInfo *InlinedExtraData) } // Encode key - err = EncodeStorableAsElement(enc, e.key, inlinedTypeInfo) + err = e.key.Encode(enc) if err != nil { - // Don't need to wrap error as external error because err is already categorized by encodeStorableAsElement(). - return err + // Wrap err as external error (if needed) because err is returned by Storable interface. + return wrapErrorfAsExternalErrorIfNeeded(err, "failed to encode map key storable") } // Encode value - err = EncodeStorableAsElement(enc, e.value, inlinedTypeInfo) + err = e.value.Encode(enc) if err != nil { - // Don't need to wrap error as external error because err is already categorized by encodeStorableAsElement(). - return err + // Wrap err as external error (if needed) because err is returned by Storable interface. + return wrapErrorfAsExternalErrorIfNeeded(err, "failed to encode map value storable") } err = enc.CBOR.Flush() @@ -763,7 +763,7 @@ func newInlineCollisionGroupFromData(cborDec *cbor.StreamDecoder, decodeStorable // Encode encodes inlineCollisionGroup to the given encoder. // // CBOR tag (number: CBORTagInlineCollisionGroup, content: elements) -func (e *inlineCollisionGroup) Encode(enc *Encoder, inlinedTypeInfo *InlinedExtraData) error { +func (e *inlineCollisionGroup) Encode(enc *Encoder) error { err := enc.CBOR.EncodeRawBytes([]byte{ // tag number CBORTagInlineCollisionGroup @@ -773,7 +773,7 @@ func (e *inlineCollisionGroup) Encode(enc *Encoder, inlinedTypeInfo *InlinedExtr return NewEncodingError(err) } - err = e.elements.Encode(enc, inlinedTypeInfo) + err = e.elements.Encode(enc) if err != nil { // Don't need to wrap error as external error because err is already categorized by elements.Encode(). return err @@ -953,7 +953,7 @@ func newExternalCollisionGroupFromData(cborDec *cbor.StreamDecoder, decodeStorab // Encode encodes externalCollisionGroup to the given encoder. // // CBOR tag (number: CBORTagExternalCollisionGroup, content: slab ID) -func (e *externalCollisionGroup) Encode(enc *Encoder, _ *InlinedExtraData) error { +func (e *externalCollisionGroup) Encode(enc *Encoder) error { err := enc.CBOR.EncodeRawBytes([]byte{ // tag number CBORTagExternalCollisionGroup 0xd8, CBORTagExternalCollisionGroup, @@ -1259,7 +1259,7 @@ func newHkeyElementsWithElement(level uint, hkey Digest, elem element) *hkeyElem // 1: hkeys (byte string) // 2: elements (array) // ] -func (e *hkeyElements) Encode(enc *Encoder, inlinedTypeInfo *InlinedExtraData) error { +func (e *hkeyElements) Encode(enc *Encoder) error { if e.level > maxDigestLevel { return NewFatalError(fmt.Errorf("hash level %d exceeds max digest level %d", e.level, maxDigestLevel)) @@ -1313,7 +1313,7 @@ func (e *hkeyElements) Encode(enc *Encoder, inlinedTypeInfo *InlinedExtraData) e // Encode each element for _, e := range e.elems { - err = e.Encode(enc, inlinedTypeInfo) + err = e.Encode(enc) if err != nil { // Don't need to wrap error as external error because err is already categorized by element.Encode(). return err @@ -1921,7 +1921,7 @@ func newSingleElementsWithElement(level uint, elem *singleElement) *singleElemen // 1: hkeys (0 length byte string) // 2: elements (array) // ] -func (e *singleElements) Encode(enc *Encoder, inlinedTypeInfo *InlinedExtraData) error { +func (e *singleElements) Encode(enc *Encoder) error { if e.level > maxDigestLevel { return NewFatalError(fmt.Errorf("digest level %d exceeds max digest level %d", e.level, maxDigestLevel)) @@ -1952,7 +1952,7 @@ func (e *singleElements) Encode(enc *Encoder, inlinedTypeInfo *InlinedExtraData) // Encode each element for _, e := range e.elems { - err = e.Encode(enc, inlinedTypeInfo) + err = e.Encode(enc) if err != nil { // Don't need to wrap error as external error because err is already categorized by singleElement.Encode(). return err @@ -2671,8 +2671,7 @@ func DecodeInlinedMapStorable( func (m *MapDataSlab) Encode(enc *Encoder) error { if m.inlined { - return NewEncodingError( - fmt.Errorf("failed to encode inlined map data slab as standalone slab")) + return m.encodeAsInlined(enc) } // Encoding is done in two steps: @@ -2680,15 +2679,13 @@ func (m *MapDataSlab) Encode(enc *Encoder) error { // 1. Encode map elements using a new buffer while collecting inlined extra data from inlined elements. // 2. Encode slab with deduplicated inlined extra data and copy encoded elements from previous buffer. - inlinedTypes := newInlinedExtraData() - // Get a buffer from a pool to encode elements. elementBuf := getBuffer() defer putBuffer(elementBuf) elemEnc := NewEncoder(elementBuf, enc.encMode) - err := m.encodeElements(elemEnc, inlinedTypes) + err := m.encodeElements(elemEnc) if err != nil { return err } @@ -2721,7 +2718,7 @@ func (m *MapDataSlab) Encode(enc *Encoder) error { h.setRoot() } - if !inlinedTypes.empty() { + if !elemEnc.inlinedExtraData.empty() { h.setHasInlinedSlabs() } @@ -2741,8 +2738,8 @@ func (m *MapDataSlab) Encode(enc *Encoder) error { } // Encode inlined types - if !inlinedTypes.empty() { - err = inlinedTypes.Encode(enc) + if !elemEnc.inlinedExtraData.empty() { + err = elemEnc.inlinedExtraData.Encode(enc) if err != nil { return NewEncodingError(err) } @@ -2777,8 +2774,8 @@ func (m *MapDataSlab) Encode(enc *Encoder) error { return nil } -func (m *MapDataSlab) encodeElements(enc *Encoder, inlinedTypes *InlinedExtraData) error { - err := m.elements.Encode(enc, inlinedTypes) +func (m *MapDataSlab) encodeElements(enc *Encoder) error { + err := m.elements.Encode(enc) if err != nil { // Don't need to wrap error as external error because err is already categorized by elements.Encode(). return err @@ -2792,14 +2789,14 @@ func (m *MapDataSlab) encodeElements(enc *Encoder, inlinedTypes *InlinedExtraDat return nil } -// EncodeAsElement encodes inlined map data slab. Encoding is +// encodeAsInlined encodes inlined map data slab. Encoding is // version 1 with CBOR tag having tag number CBORTagInlinedMap, // and tag contant as 3-element array: // // +------------------+----------------+----------+ // | extra data index | value ID index | elements | // +------------------+----------------+----------+ -func (m *MapDataSlab) EncodeAsElement(enc *Encoder, inlinedTypeInfo *InlinedExtraData) error { +func (m *MapDataSlab) encodeAsInlined(enc *Encoder) error { if m.extraData == nil { return NewEncodingError( fmt.Errorf("failed to encode non-root map data slab as inlined")) @@ -2811,15 +2808,15 @@ func (m *MapDataSlab) EncodeAsElement(enc *Encoder, inlinedTypeInfo *InlinedExtr } if hkeys, keys, values, ok := m.canBeEncodedAsCompactMap(); ok { - return encodeAsInlinedCompactMap(enc, m.header.slabID, m.extraData, hkeys, keys, values, inlinedTypeInfo) + return encodeAsInlinedCompactMap(enc, m.header.slabID, m.extraData, hkeys, keys, values) } - return m.encodeAsInlinedMap(enc, inlinedTypeInfo) + return m.encodeAsInlinedMap(enc) } -func (m *MapDataSlab) encodeAsInlinedMap(enc *Encoder, inlinedTypeInfo *InlinedExtraData) error { +func (m *MapDataSlab) encodeAsInlinedMap(enc *Encoder) error { - extraDataIndex := inlinedTypeInfo.addMapExtraData(m.extraData) + extraDataIndex := enc.inlinedExtraData.addMapExtraData(m.extraData) if extraDataIndex > maxInlinedExtraDataIndex { return NewEncodingError(fmt.Errorf("extra data index %d exceeds limit %d", extraDataIndex, maxInlinedExtraDataIndex)) @@ -2855,7 +2852,7 @@ func (m *MapDataSlab) encodeAsInlinedMap(enc *Encoder, inlinedTypeInfo *InlinedE } // element 2: map elements - err = m.elements.Encode(enc, inlinedTypeInfo) + err = m.elements.Encode(enc) if err != nil { // Don't need to wrap error as external error because err is already categorized by elements.Encode(). return err @@ -2877,10 +2874,9 @@ func encodeAsInlinedCompactMap( hkeys []Digest, keys []ComparableStorable, values []Storable, - inlinedTypeInfo *InlinedExtraData, ) error { - extraDataIndex, cachedKeys := inlinedTypeInfo.addCompactMapExtraData(extraData, hkeys, keys) + extraDataIndex, cachedKeys := enc.inlinedExtraData.addCompactMapExtraData(extraData, hkeys, keys) if len(keys) != len(cachedKeys) { return NewEncodingError(fmt.Errorf("number of elements %d is different from number of elements in cached compact map type %d", len(keys), len(cachedKeys))) @@ -2921,7 +2917,7 @@ func encodeAsInlinedCompactMap( } // element 2: compact map values in the order of cachedKeys - err = encodeCompactMapValues(enc, cachedKeys, keys, values, inlinedTypeInfo) + err = encodeCompactMapValues(enc, cachedKeys, keys, values) if err != nil { // err is already categorized by encodeCompactMapValues(). return err @@ -2941,7 +2937,6 @@ func encodeCompactMapValues( cachedKeys []ComparableStorable, keys []ComparableStorable, values []Storable, - inlinedTypeInfo *InlinedExtraData, ) error { var err error @@ -2967,10 +2962,10 @@ func encodeCompactMapValues( found = true keyIndexes[i], keyIndexes[j] = keyIndexes[j], keyIndexes[i] - err = EncodeStorableAsElement(enc, values[index], inlinedTypeInfo) + err = values[index].Encode(enc) if err != nil { - // Don't need to wrap error as external error because err is already categorized by encodeStorableAsElement(). - return err + // Wrap err as external error (if needed) because err is returned by Storable interface. + return wrapErrorfAsExternalErrorIfNeeded(err, "failed to encode map value storable") } break diff --git a/storable.go b/storable.go index 34f83648..a59529c8 100644 --- a/storable.go +++ b/storable.go @@ -58,11 +58,6 @@ type ComparableStorable interface { type ContainerStorable interface { Storable - // EncodeAsElement encodes ContainerStorable and its child storables as an element - // of parent array/map. Since child storable can be inlined array or map, - // encoding inlined array or map requires extra parameter InlinedExtraData. - EncodeAsElement(*Encoder, *InlinedExtraData) error - // HasPointer returns true if any of its child storables is SlabIDStorable // (references to another slab). This function is used during encoding. HasPointer() bool @@ -156,10 +151,6 @@ func (v SlabIDStorable) Encode(enc *Encoder) error { return nil } -func (v SlabIDStorable) EncodeAsElement(enc *Encoder, _ *InlinedExtraData) error { - return v.Encode(enc) -} - func (v SlabIDStorable) ByteSize() uint32 { // tag number (2 bytes) + byte string header (1 byte) + slab id (16 bytes) return 2 + 1 + slabIDSize diff --git a/storable_test.go b/storable_test.go index 1e747120..4f705ab6 100644 --- a/storable_test.go +++ b/storable_test.go @@ -723,17 +723,6 @@ func (v SomeStorable) Encode(enc *Encoder) error { return v.Storable.Encode(enc) } -func (v SomeStorable) EncodeAsElement(enc *Encoder, inlinedExtraData *InlinedExtraData) error { - err := enc.CBOR.EncodeRawBytes([]byte{ - // tag number - 0xd8, cborTagSomeValue, - }) - if err != nil { - return err - } - return EncodeStorableAsElement(enc, v.Storable, inlinedExtraData) -} - func (v SomeStorable) ChildStorables() []Storable { return []Storable{v.Storable} } From d88bd127864796d7197b710c227cc5fbbf7ba1e4 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 24 Oct 2023 15:01:05 -0500 Subject: [PATCH 2/3] Create Encoder.inlinedExtraData lazily --- array.go | 8 ++++---- encode.go | 22 ++++++++++++++-------- map.go | 10 +++++----- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/array.go b/array.go index b9c5a3d4..715d639d 100644 --- a/array.go +++ b/array.go @@ -716,7 +716,7 @@ func (a *ArrayDataSlab) encodeAsInlined(enc *Encoder) error { fmt.Errorf("failed to encode standalone array data slab as inlined")) } - extraDataIndex := enc.inlinedExtraData.addArrayExtraData(a.extraData) + extraDataIndex := enc.inlinedExtraData().addArrayExtraData(a.extraData) if extraDataIndex > maxInlinedExtraDataIndex { return NewEncodingError( @@ -828,7 +828,7 @@ func (a *ArrayDataSlab) Encode(enc *Encoder) error { h.setRoot() } - if !elementEnc.inlinedExtraData.empty() { + if !elementEnc.inlinedExtraData().empty() { h.setHasInlinedSlabs() } @@ -848,8 +848,8 @@ func (a *ArrayDataSlab) Encode(enc *Encoder) error { } // Encode inlined extra data - if !elementEnc.inlinedExtraData.empty() { - err = elementEnc.inlinedExtraData.Encode(enc) + if !elementEnc.inlinedExtraData().empty() { + err = elementEnc.inlinedExtraData().Encode(enc) if err != nil { // err is already categorized by inlinedExtraData.Encode(). return err diff --git a/encode.go b/encode.go index 213e7316..00b7e08f 100644 --- a/encode.go +++ b/encode.go @@ -28,22 +28,28 @@ import ( // Encoder writes atree slabs to io.Writer. type Encoder struct { io.Writer - CBOR *cbor.StreamEncoder - Scratch [64]byte - encMode cbor.EncMode - inlinedExtraData *InlinedExtraData + CBOR *cbor.StreamEncoder + Scratch [64]byte + encMode cbor.EncMode + _inlinedExtraData *InlinedExtraData } func NewEncoder(w io.Writer, encMode cbor.EncMode) *Encoder { streamEncoder := encMode.NewStreamEncoder(w) return &Encoder{ - Writer: w, - CBOR: streamEncoder, - encMode: encMode, - inlinedExtraData: newInlinedExtraData(), + Writer: w, + CBOR: streamEncoder, + encMode: encMode, } } +func (enc *Encoder) inlinedExtraData() *InlinedExtraData { + if enc._inlinedExtraData == nil { + enc._inlinedExtraData = newInlinedExtraData() + } + return enc._inlinedExtraData +} + type StorableDecoder func( decoder *cbor.StreamDecoder, storableSlabID SlabID, diff --git a/map.go b/map.go index 3d23752b..fff1c671 100644 --- a/map.go +++ b/map.go @@ -2718,7 +2718,7 @@ func (m *MapDataSlab) Encode(enc *Encoder) error { h.setRoot() } - if !elemEnc.inlinedExtraData.empty() { + if !elemEnc.inlinedExtraData().empty() { h.setHasInlinedSlabs() } @@ -2738,8 +2738,8 @@ func (m *MapDataSlab) Encode(enc *Encoder) error { } // Encode inlined types - if !elemEnc.inlinedExtraData.empty() { - err = elemEnc.inlinedExtraData.Encode(enc) + if !elemEnc.inlinedExtraData().empty() { + err = elemEnc.inlinedExtraData().Encode(enc) if err != nil { return NewEncodingError(err) } @@ -2816,7 +2816,7 @@ func (m *MapDataSlab) encodeAsInlined(enc *Encoder) error { func (m *MapDataSlab) encodeAsInlinedMap(enc *Encoder) error { - extraDataIndex := enc.inlinedExtraData.addMapExtraData(m.extraData) + extraDataIndex := enc.inlinedExtraData().addMapExtraData(m.extraData) if extraDataIndex > maxInlinedExtraDataIndex { return NewEncodingError(fmt.Errorf("extra data index %d exceeds limit %d", extraDataIndex, maxInlinedExtraDataIndex)) @@ -2876,7 +2876,7 @@ func encodeAsInlinedCompactMap( values []Storable, ) error { - extraDataIndex, cachedKeys := enc.inlinedExtraData.addCompactMapExtraData(extraData, hkeys, keys) + extraDataIndex, cachedKeys := enc.inlinedExtraData().addCompactMapExtraData(extraData, hkeys, keys) if len(keys) != len(cachedKeys) { return NewEncodingError(fmt.Errorf("number of elements %d is different from number of elements in cached compact map type %d", len(keys), len(cachedKeys))) From acdb685d6dcd585f6e697cae1fb3f30623b63e54 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue, 24 Oct 2023 15:19:11 -0500 Subject: [PATCH 3/3] Fail StorableSlab.Encode() with inlined array/map This commit makes StorableSlab.Encode() return error if it contains inlined array or inlined map. --- array.go | 4 ++-- encode.go | 7 +++++++ map.go | 4 ++-- storable_slab.go | 4 ++++ 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/array.go b/array.go index 715d639d..cf3217c8 100644 --- a/array.go +++ b/array.go @@ -828,7 +828,7 @@ func (a *ArrayDataSlab) Encode(enc *Encoder) error { h.setRoot() } - if !elementEnc.inlinedExtraData().empty() { + if elementEnc.hasInlinedExtraData() { h.setHasInlinedSlabs() } @@ -848,7 +848,7 @@ func (a *ArrayDataSlab) Encode(enc *Encoder) error { } // Encode inlined extra data - if !elementEnc.inlinedExtraData().empty() { + if elementEnc.hasInlinedExtraData() { err = elementEnc.inlinedExtraData().Encode(enc) if err != nil { // err is already categorized by inlinedExtraData.Encode(). diff --git a/encode.go b/encode.go index 00b7e08f..d82d56aa 100644 --- a/encode.go +++ b/encode.go @@ -50,6 +50,13 @@ func (enc *Encoder) inlinedExtraData() *InlinedExtraData { return enc._inlinedExtraData } +func (enc *Encoder) hasInlinedExtraData() bool { + if enc._inlinedExtraData == nil { + return false + } + return !enc._inlinedExtraData.empty() +} + type StorableDecoder func( decoder *cbor.StreamDecoder, storableSlabID SlabID, diff --git a/map.go b/map.go index fff1c671..63db77ab 100644 --- a/map.go +++ b/map.go @@ -2718,7 +2718,7 @@ func (m *MapDataSlab) Encode(enc *Encoder) error { h.setRoot() } - if !elemEnc.inlinedExtraData().empty() { + if elemEnc.hasInlinedExtraData() { h.setHasInlinedSlabs() } @@ -2738,7 +2738,7 @@ func (m *MapDataSlab) Encode(enc *Encoder) error { } // Encode inlined types - if !elemEnc.inlinedExtraData().empty() { + if elemEnc.hasInlinedExtraData() { err = elemEnc.inlinedExtraData().Encode(enc) if err != nil { return NewEncodingError(err) diff --git a/storable_slab.go b/storable_slab.go index 9cc6d7bd..07db18f4 100644 --- a/storable_slab.go +++ b/storable_slab.go @@ -93,6 +93,10 @@ func (s *StorableSlab) Encode(enc *Encoder) error { return wrapErrorfAsExternalErrorIfNeeded(err, "failed to encode storable") } + if enc.hasInlinedExtraData() { + return NewEncodingError(fmt.Errorf("failed to encode storable slab because storable contains inlined array/map")) + } + return nil }