Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update for Cadence integration for atree inlining and deduplication #352

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions array.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ func (a *ArrayDataSlab) StoredValue(storage SlabStorage) (Value, error) {
}

var _ ArraySlab = &ArrayDataSlab{}
var _ ContainerStorable = &ArrayDataSlab{}

// ArrayMetaDataSlab is internal node, implementing ArraySlab.
type ArrayMetaDataSlab struct {
Expand Down Expand Up @@ -697,14 +698,14 @@ func DecodeInlinedArrayStorable(
}, nil
}

// encodeAsInlined encodes inlined array data slab. Encoding is
// EncodeAsElement 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) encodeAsInlined(enc *Encoder, inlinedTypeInfo *inlinedExtraData) error {
func (a *ArrayDataSlab) EncodeAsElement(enc *Encoder, inlinedTypeInfo *InlinedExtraData) error {
if a.extraData == nil {
return NewEncodingError(
fmt.Errorf("failed to encode non-root array data slab as inlined"))
Expand Down Expand Up @@ -754,7 +755,8 @@ func (a *ArrayDataSlab) encodeAsInlined(enc *Encoder, inlinedTypeInfo *inlinedEx
// element 2: array elements
err = a.encodeElements(enc, inlinedTypeInfo)
if err != nil {
return NewEncodingError(err)
// err is already categorized by ArrayDataSlab.encodeElements().
return err
}

err = enc.CBOR.Flush()
Expand Down Expand Up @@ -817,7 +819,7 @@ func (a *ArrayDataSlab) Encode(enc *Encoder) error {
return NewEncodingError(err)
}

if a.hasPointer() {
if a.HasPointer() {
h.setHasPointers()
}

Expand Down Expand Up @@ -885,7 +887,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, inlinedTypeInfo *InlinedExtraData) error {
// Encode CBOR array size manually for fix-sized encoding

enc.Scratch[0] = 0x80 | 25
Expand All @@ -906,10 +908,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 = EncodeStorableAsElement(enc, e, inlinedTypeInfo)
if err != nil {
// Wrap err as external error (if needed) because err is returned by Storable interface.
return wrapErrorfAsExternalErrorIfNeeded(err, "failed to encode array element")
// err is already categorized by encodeStorableAsElement().
return err
}
}

Expand Down Expand Up @@ -1000,7 +1002,7 @@ func (a *ArrayDataSlab) Uninline(storage SlabStorage) error {
return nil
}

func (a *ArrayDataSlab) hasPointer() bool {
func (a *ArrayDataSlab) HasPointer() bool {
for _, e := range a.elements {
if hasPointer(e) {
return true
Expand Down
21 changes: 17 additions & 4 deletions array_debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,8 +345,16 @@ func (v *arrayVerifier) verifyDataSlab(
}

// Verify that only root data slab can be inlined
if level > 0 && dataSlab.Inlined() {
return 0, nil, nil, NewFatalError(fmt.Errorf("non-root slab %s is inlined", id))
if dataSlab.Inlined() {
if level > 0 {
return 0, nil, nil, NewFatalError(fmt.Errorf("non-root slab %s is inlined", id))
}
if dataSlab.extraData == nil {
return 0, nil, nil, NewFatalError(fmt.Errorf("inlined slab %s doesn't have extra data", id))
}
if dataSlab.next != SlabIDUndefined {
return 0, nil, nil, NewFatalError(fmt.Errorf("inlined slab %s has next slab ID", id))
}
}

// Verify that aggregated element size + slab prefix is the same as header.size
Expand Down Expand Up @@ -524,6 +532,11 @@ func VerifyArraySerialization(
decodeTypeInfo TypeInfoDecoder,
compare StorableComparator,
) error {
// Skip verification of inlined array serialization.
if a.Inlined() {
return nil
}

Comment on lines +535 to +539
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are inlined arrays verified somewhere else? If this skips verification of them in general, maybe it would be good to add that too, just to be safe (maybe just in a follow-up PR, not a blocker)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are inlined arrays verified somewhere else?

Yes, inlined arrays and maps are verified when their parent is verified. The serialization verification is done at slab level so round-trip serialization is tested, as well as elements in the slabs.

v := &serializationVerifier{
storage: a.Storage,
cborDecMode: cborDecMode,
Expand All @@ -550,7 +563,7 @@ func (v *serializationVerifier) verifyArraySlab(slab ArraySlab) error {
id := slab.SlabID()

// Encode slab
data, err := Encode(slab, v.cborEncMode)
data, err := EncodeSlab(slab, v.cborEncMode)
if err != nil {
// Don't need to wrap error as external error because err is already categorized by Encode().
return err
Expand All @@ -564,7 +577,7 @@ func (v *serializationVerifier) verifyArraySlab(slab ArraySlab) error {
}

// Re-encode decoded slab
dataFromDecodedSlab, err := Encode(decodedSlab, v.cborEncMode)
dataFromDecodedSlab, err := EncodeSlab(decodedSlab, v.cborEncMode)
if err != nil {
// Don't need to wrap error as external error because err is already categorized by Encode().
return err
Expand Down
24 changes: 24 additions & 0 deletions array_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,30 @@ func _testArray(
require.Equal(t, 1, len(rootIDs))
require.Equal(t, array.SlabID(), rootIDs[0])

// Encode all non-nil slab
encodedSlabs := make(map[SlabID][]byte)
for id, slab := range storage.deltas {
if slab != nil {
b, err := EncodeSlab(slab, storage.cborEncMode)
require.NoError(t, err)
encodedSlabs[id] = b
}
}

// Test decoded array from new storage to force slab decoding
decodedArray, err := NewArrayWithRootID(
newTestPersistentStorageWithBaseStorageAndDeltas(t, storage.baseStorage, encodedSlabs),
array.SlabID())
require.NoError(t, err)

// Verify decoded array elements
for i, expected := range expectedValues {
actual, err := decodedArray.Get(uint64(i))
require.NoError(t, err)

valueEqual(t, expected, actual)
}

if !hasNestedArrayMapElement {
// Need to call Commit before calling storage.Count() for PersistentSlabStorage.
err = storage.Commit()
Expand Down
6 changes: 3 additions & 3 deletions cmd/stress/typeinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (i arrayTypeInfo) IsComposite() bool {
return false
}

func (i arrayTypeInfo) ID() string {
func (i arrayTypeInfo) Identifier() string {
return fmt.Sprintf("array(%d)", i)
}

Expand Down Expand Up @@ -88,7 +88,7 @@ func (i mapTypeInfo) IsComposite() bool {
return false
}

func (i mapTypeInfo) ID() string {
func (i mapTypeInfo) Identifier() string {
return fmt.Sprintf("map(%d)", i)
}

Expand Down Expand Up @@ -153,7 +153,7 @@ func (i compositeTypeInfo) IsComposite() bool {
return true
}

func (i compositeTypeInfo) ID() string {
func (i compositeTypeInfo) Identifier() string {
return fmt.Sprintf("composite(%d_%d)", i.fieldStartIndex, i.fieldEndIndex)
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/stress/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -540,5 +540,5 @@ func (v mapValue) Storable(atree.SlabStorage, atree.Address, uint64) (atree.Stor
}

var typeInfoComparator = func(a atree.TypeInfo, b atree.TypeInfo) bool {
return a.ID() == b.ID()
return a.Identifier() == b.Identifier()
}
17 changes: 9 additions & 8 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,23 +42,24 @@ func NewEncoder(w io.Writer, encMode cbor.EncMode) *Encoder {
}
}

// encodeStorableAsElement encodes storable as Array or OrderedMap element.
// 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 {
func EncodeStorableAsElement(enc *Encoder, storable Storable, inlinedTypeInfo *InlinedExtraData) error {

switch storable := storable.(type) {

case *ArrayDataSlab:
return storable.encodeAsInlined(enc, inlinedTypeInfo)

case *MapDataSlab:
return storable.encodeAsInlined(enc, inlinedTypeInfo)
case ContainerStorable:
turbolent marked this conversation as resolved.
Show resolved Hide resolved
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 map value")
return wrapErrorfAsExternalErrorIfNeeded(err, "failed to encode storable as element")
}
}

Expand Down
Loading