Skip to content

Commit

Permalink
Merge pull request #338 from onflow/fxamacker/refactor-encoding-versi…
Browse files Browse the repository at this point in the history
…on-and-flag

Refactor encoding version and flag to add more flags
  • Loading branch information
fxamacker authored Sep 12, 2023
2 parents 055a453 + 79e86d9 commit 1e6ec55
Show file tree
Hide file tree
Showing 13 changed files with 778 additions and 401 deletions.
215 changes: 104 additions & 111 deletions array.go

Large diffs are not rendered by default.

75 changes: 49 additions & 26 deletions array_debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -446,17 +446,15 @@ func validArraySlabSerialization(
}

// Extra check: encoded data size == header.size
encodedExtraDataSize, err := getEncodedArrayExtraDataSize(slab.ExtraData(), cborEncMode)
encodedSlabSize, err := computeSlabSize(data)
if err != nil {
// Don't need to wrap error as external error because err is already categorized by getEncodedArrayExtraDataSize().
// Don't need to wrap error as external error because err is already categorized by computeSlabSize().
return err
}

// Need to exclude extra data size from encoded data size.
encodedSlabSize := uint32(len(data) - encodedExtraDataSize)
if slab.Header().size != encodedSlabSize {
return NewFatalError(fmt.Errorf("slab %d encoded size %d != header.size %d (encoded extra data size %d)",
id, encodedSlabSize, slab.Header().size, encodedExtraDataSize))
if slab.Header().size != uint32(encodedSlabSize) {
return NewFatalError(fmt.Errorf("slab %d encoded size %d != header.size %d",
id, encodedSlabSize, slab.Header().size))
}

// Compare encoded data of original slab with encoded data of decoded slab
Expand Down Expand Up @@ -640,25 +638,6 @@ func arrayExtraDataEqual(expected, actual *ArrayExtraData) error {
return nil
}

func getEncodedArrayExtraDataSize(extraData *ArrayExtraData, cborEncMode cbor.EncMode) (int, error) {
if extraData == nil {
return 0, nil
}

var buf bytes.Buffer
enc := NewEncoder(&buf, cborEncMode)

// Normally the flag shouldn't be 0. But in this case we just need the encoded data size
// so the content of the flag doesn't matter.
err := extraData.Encode(enc)
if err != nil {
// Don't need to wrap error as external error because err is already categorized by ArrayExtraData.Encode().
return 0, err
}

return len(buf.Bytes()), nil
}

func ValidValueSerialization(
value Value,
cborDecMode cbor.DecMode,
Expand Down Expand Up @@ -690,3 +669,47 @@ func ValidValueSerialization(
}
return nil
}

func computeSlabSize(data []byte) (int, error) {
if len(data) < versionAndFlagSize {
return 0, NewDecodingError(fmt.Errorf("data is too short"))
}

h, err := newHeadFromData(data[:versionAndFlagSize])
if err != nil {
return 0, NewDecodingError(err)
}

slabExtraDataSize, err := getExtraDataSize(h, data[versionAndFlagSize:])
if err != nil {
return 0, err
}

// Computed slab size (slab header size):
// - excludes slab extra data size
// - adds next slab ID for non-root data slab if not encoded
size := len(data) - slabExtraDataSize

isDataSlab := h.getSlabArrayType() == slabArrayData ||
h.getSlabMapType() == slabMapData ||
h.getSlabMapType() == slabMapCollisionGroup

if !h.isRoot() && isDataSlab && !h.hasNextSlabID() {
size += slabIDSize
}

return size, nil
}

func getExtraDataSize(h head, data []byte) (int, error) {
if h.isRoot() {
dec := cbor.NewStreamDecoder(bytes.NewBuffer(data))
b, err := dec.DecodeRawBytes()
if err != nil {
return 0, NewDecodingError(err)
}
return len(b), nil
}

return 0, nil
}
14 changes: 6 additions & 8 deletions array_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1753,7 +1753,7 @@ func TestArrayEncodeDecode(t *testing.T) {

expectedData := []byte{
// version
0x01,
0x10,
// flag
0x80,

Expand Down Expand Up @@ -1797,7 +1797,7 @@ func TestArrayEncodeDecode(t *testing.T) {

expectedData := []byte{
// version
0x01,
0x10,
// flag
0x80,

Expand Down Expand Up @@ -1872,7 +1872,7 @@ func TestArrayEncodeDecode(t *testing.T) {
// (metadata slab) headers: [{id:2 size:228 count:9} {id:3 size:270 count:11} ]
id1: {
// version
0x01,
0x10,
// flag
0x81,

Expand All @@ -1899,7 +1899,7 @@ func TestArrayEncodeDecode(t *testing.T) {
// (data slab) next: 3, data: [aaaaaaaaaaaaaaaaaaaaaa ... aaaaaaaaaaaaaaaaaaaaaa]
id2: {
// version
0x01,
0x12,
// array data slab flag
0x00,
// next slab id
Expand All @@ -1921,11 +1921,9 @@ func TestArrayEncodeDecode(t *testing.T) {
// (data slab) next: 0, data: [aaaaaaaaaaaaaaaaaaaaaa ... SlabID(...)]
id3: {
// version
0x01,
0x10,
// array data slab flag
0x40,
// next slab id
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// CBOR encoded array head (fixed size 3 byte)
0x99, 0x00, 0x0b,
// CBOR encoded array elements
Expand All @@ -1945,7 +1943,7 @@ func TestArrayEncodeDecode(t *testing.T) {
// (data slab) next: 0, data: [0]
id4: {
// version
0x01,
0x10,
// extra data flag
0x80,

Expand Down
11 changes: 8 additions & 3 deletions basicarray.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,16 @@ func newBasicArrayDataSlabFromData(
return nil, NewDecodingErrorf("data is too short for basic array slab")
}

h, err := newHeadFromData(data[:versionAndFlagSize])
if err != nil {
return nil, NewDecodingError(err)
}

// Check flag
if getSlabArrayType(data[1]) != slabBasicArray {
if h.getSlabArrayType() != slabBasicArray {
return nil, NewDecodingErrorf(
"data has invalid flag 0x%x, want 0x%x",
data[0],
"data has invalid head 0x%x, want 0x%x",
h[:],
maskBasicArray,
)
}
Expand Down
22 changes: 14 additions & 8 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,35 +62,41 @@ func DecodeSlab(
return nil, NewDecodingErrorf("data is too short")
}

flag := data[1]
h, err := newHeadFromData(data[:versionAndFlagSize])
if err != nil {
return nil, NewDecodingError(err)
}

dataType := getSlabType(flag)
switch dataType {
switch h.getSlabType() {

case slabArray:

switch arrayDataType := getSlabArrayType(flag); arrayDataType {
arrayDataType := h.getSlabArrayType()

switch arrayDataType {
case slabArrayData:
return newArrayDataSlabFromData(id, data, decMode, decodeStorable, decodeTypeInfo)
case slabArrayMeta:
return newArrayMetaDataSlabFromData(id, data, decMode, decodeTypeInfo)
case slabBasicArray:
return newBasicArrayDataSlabFromData(id, data, decMode, decodeStorable)
default:
return nil, NewDecodingErrorf("data has invalid flag 0x%x", flag)
return nil, NewDecodingErrorf("data has invalid head 0x%x", h[:])
}

case slabMap:

switch mapDataType := getSlabMapType(flag); mapDataType {
mapDataType := h.getSlabMapType()

switch mapDataType {
case slabMapData:
return newMapDataSlabFromData(id, data, decMode, decodeStorable, decodeTypeInfo)
case slabMapMeta:
return newMapMetaDataSlabFromData(id, data, decMode, decodeTypeInfo)
case slabMapCollisionGroup:
return newMapDataSlabFromData(id, data, decMode, decodeStorable, decodeTypeInfo)
default:
return nil, NewDecodingErrorf("data has invalid flag 0x%x", flag)
return nil, NewDecodingErrorf("data has invalid head 0x%x", h[:])
}

case slabStorable:
Expand All @@ -106,7 +112,7 @@ func DecodeSlab(
}, nil

default:
return nil, NewDecodingErrorf("data has invalid flag 0x%x", flag)
return nil, NewDecodingErrorf("data has invalid head 0x%x", h[:])
}
}

Expand Down
Loading

0 comments on commit 1e6ec55

Please sign in to comment.