Skip to content

Commit

Permalink
Merge pull request #324 from onflow/fxamacker/refactor-new-storableslab
Browse files Browse the repository at this point in the history
Refactor creating new StorableSlab
  • Loading branch information
fxamacker authored Jul 6, 2023
2 parents 33b8013 + 37f2201 commit 5224515
Show file tree
Hide file tree
Showing 12 changed files with 78 additions and 59 deletions.
5 changes: 2 additions & 3 deletions array.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ func (a *ArrayMetaDataSlab) StoredValue(storage SlabStorage) (Value, error) {

type ArraySlab interface {
Slab
fmt.Stringer

Get(storage SlabStorage, index uint64) (Storable, error)
Set(storage SlabStorage, address Address, index uint64, value Value) (Storable, error)
Expand Down Expand Up @@ -2373,10 +2372,10 @@ func (a *Array) SlabID() SlabID {
return a.root.SlabID()
}

func (a *Array) ID() ID {
func (a *Array) ValueID() ValueID {
sid := a.SlabID()

var id ID
var id ValueID
copy(id[:], sid.address[:])
copy(id[8:], sid.index[:])

Expand Down
2 changes: 1 addition & 1 deletion array_debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func DumpArraySlabs(a *Array) ([]string, error) {
if !found {
return nil, NewSlabNotFoundErrorf(id, "slab not found during array slab dump")
}
dumps = append(dumps, fmt.Sprintf("overflow: %s", slab))
dumps = append(dumps, slab.String())
}

return dumps, nil
Expand Down
4 changes: 2 additions & 2 deletions array_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2563,7 +2563,7 @@ func TestArraySlabDump(t *testing.T) {

want := []string{
"level 1, ArrayDataSlab id:0x102030405060708.1 size:24 count:1 elements: [SlabIDStorable({[1 2 3 4 5 6 7 8] [0 0 0 0 0 0 0 2]})]",
"overflow: &{0x102030405060708.2 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}",
"StorableSlab id:0x102030405060708.2 storable:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
}

dumps, err := DumpArraySlabs(array)
Expand Down Expand Up @@ -2599,7 +2599,7 @@ func TestArrayID(t *testing.T) {
require.NoError(t, err)

sid := array.SlabID()
id := array.ID()
id := array.ValueID()

require.Equal(t, sid.address[:], id[:8])
require.Equal(t, sid.index[:], id[8:])
Expand Down
25 changes: 3 additions & 22 deletions cmd/stress/storable.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,30 +348,11 @@ func (v StringValue) StoredValue(_ atree.SlabStorage) (atree.Value, error) {
}

func (v StringValue) Storable(storage atree.SlabStorage, address atree.Address, maxInlineSize uint64) (atree.Storable, error) {
if uint64(v.ByteSize()) > maxInlineSize {

// Create StorableSlab
id, err := storage.GenerateSlabID(address)
if err != nil {
return nil, err
}

slab := &atree.StorableSlab{
ID: id,
Storable: v,
}

// Store StorableSlab in storage
err = storage.Store(id, slab)
if err != nil {
return nil, err
}

// Return slab id as storable
return atree.SlabIDStorable(id), nil
if uint64(v.ByteSize()) <= maxInlineSize {
return v, nil
}

return v, nil
return atree.NewStorableSlab(storage, address, v)
}

func (v StringValue) Encode(enc *atree.Encoder) error {
Expand Down
6 changes: 3 additions & 3 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ func DecodeSlab(
// Wrap err as external error (if needed) because err is returned by StorableDecoder callback.
return nil, wrapErrorfAsExternalErrorIfNeeded(err, "failed to decode slab storable")
}
return StorableSlab{
ID: id,
Storable: storable,
return &StorableSlab{
slabID: id,
storable: storable,
}, nil

default:
Expand Down
5 changes: 2 additions & 3 deletions map.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,6 @@ var _ MapSlab = &MapMetaDataSlab{}

type MapSlab interface {
Slab
fmt.Stringer

Get(storage SlabStorage, digester Digester, level uint, hkey Digest, comparator ValueComparator, key Value) (MapValue, error)
Set(storage SlabStorage, b DigesterBuilder, digester Digester, level uint, hkey Digest, comparator ValueComparator, hip HashInputProvider, key Value, value Value) (existingValue MapValue, err error)
Expand Down Expand Up @@ -3862,10 +3861,10 @@ func (m *OrderedMap) SlabID() SlabID {
return m.root.SlabID()
}

func (m *OrderedMap) ID() ID {
func (m *OrderedMap) ValueID() ValueID {
sid := m.SlabID()

var id ID
var id ValueID
copy(id[:], sid.address[:])
copy(id[8:], sid.index[:])

Expand Down
2 changes: 1 addition & 1 deletion map_debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ func DumpMapSlabs(m *OrderedMap) ([]string, error) {
if !found {
return nil, NewSlabNotFoundErrorf(id, "slab not found during map slab dump")
}
dumps = append(dumps, fmt.Sprintf("overflow: %s", slab))
dumps = append(dumps, slab.String())
}

return dumps, nil
Expand Down
6 changes: 3 additions & 3 deletions map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3909,7 +3909,7 @@ func TestMapSlabDump(t *testing.T) {

want := []string{
"level 1, MapDataSlab id:0x102030405060708.1 size:102 firstkey:0 elements: [0:SlabIDStorable({[1 2 3 4 5 6 7 8] [0 0 0 0 0 0 0 2]}):bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb]",
"overflow: &{0x102030405060708.2 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}",
"StorableSlab id:0x102030405060708.2 storable:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
}
dumps, err := DumpMapSlabs(m)
require.NoError(t, err)
Expand All @@ -3936,7 +3936,7 @@ func TestMapSlabDump(t *testing.T) {

want := []string{
"level 1, MapDataSlab id:0x102030405060708.1 size:100 firstkey:0 elements: [0:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:SlabIDStorable({[1 2 3 4 5 6 7 8] [0 0 0 0 0 0 0 2]})]",
"overflow: &{0x102030405060708.2 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb}",
"StorableSlab id:0x102030405060708.2 storable:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
}
dumps, err := DumpMapSlabs(m)
require.NoError(t, err)
Expand Down Expand Up @@ -4227,7 +4227,7 @@ func TestMapID(t *testing.T) {
require.NoError(t, err)

sid := m.SlabID()
id := m.ID()
id := m.ValueID()

require.Equal(t, sid.address[:], id[:8])
require.Equal(t, sid.index[:], id[8:])
Expand Down
3 changes: 3 additions & 0 deletions slab.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@

package atree

import "fmt"

type Slab interface {
Storable
fmt.Stringer

SlabID() SlabID
Split(SlabStorage) (Slab, Slab, error)
Expand Down
69 changes: 51 additions & 18 deletions storable_slab.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,64 @@

package atree

import "fmt"

// StorableSlab allows storing storables (CBOR encoded data) directly in a slab.
// Eventually we will only have a dictionary at the account storage root,
// so this won't be needed, but during the refactor we have the need to store
// other non-dictionary values (e.g. strings, integers, etc.) directly in accounts
// (i.e. directly in slabs aka registers)
type StorableSlab struct {
ID SlabID
Storable Storable
slabID SlabID
storable Storable
}

var _ Slab = StorableSlab{}
var _ Slab = &StorableSlab{}

func NewStorableSlab(storage SlabStorage, address Address, storable Storable) (Storable, error) {
id, err := storage.GenerateSlabID(address)
if err != nil {
// Wrap err as external error (if needed) because err is returned by SlabStorage interface.
return nil, wrapErrorfAsExternalErrorIfNeeded(
err,
fmt.Sprintf(
"failed to generate slab ID for address 0x%x",
address,
),
)
}

slab := &StorableSlab{
slabID: id,
storable: storable,
}

err = storage.Store(id, slab)
if err != nil {
// Wrap err as external error (if needed) because err is returned by SlabStorage interface.
return nil, wrapErrorfAsExternalErrorIfNeeded(err, fmt.Sprintf("failed to store slab %s", id))
}

return SlabIDStorable(id), nil
}

func (s *StorableSlab) String() string {
return fmt.Sprintf("StorableSlab id:%s storable:%s", s.slabID, s.storable)
}

func (s StorableSlab) ChildStorables() []Storable {
return []Storable{s.Storable}
func (s *StorableSlab) ChildStorables() []Storable {
return []Storable{s.storable}
}

func (s StorableSlab) Encode(enc *Encoder) error {
func (s *StorableSlab) Encode(enc *Encoder) error {
// Encode version
enc.Scratch[0] = 0

// Encode flag
flag := maskStorable
flag = setNoSizeLimit(flag)

if _, ok := s.Storable.(SlabIDStorable); ok {
if _, ok := s.storable.(SlabIDStorable); ok {
flag = setHasPointers(flag)
}

Expand All @@ -53,7 +86,7 @@ func (s StorableSlab) Encode(enc *Encoder) error {
return NewEncodingError(err)
}

err = s.Storable.Encode(enc)
err = s.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")
Expand All @@ -62,35 +95,35 @@ func (s StorableSlab) Encode(enc *Encoder) error {
return nil
}

func (s StorableSlab) ByteSize() uint32 {
return versionAndFlagSize + s.Storable.ByteSize()
func (s *StorableSlab) ByteSize() uint32 {
return versionAndFlagSize + s.storable.ByteSize()
}

func (s StorableSlab) SlabID() SlabID {
return s.ID
func (s *StorableSlab) SlabID() SlabID {
return s.slabID
}

func (s StorableSlab) StoredValue(storage SlabStorage) (Value, error) {
value, err := s.Storable.StoredValue(storage)
func (s *StorableSlab) StoredValue(storage SlabStorage) (Value, error) {
value, err := s.storable.StoredValue(storage)
if err != nil {
// Wrap err as external error (if needed) because err is returned by Storable interface.
return nil, wrapErrorfAsExternalErrorIfNeeded(err, "failed to get storable's stored value")
}
return value, nil
}

func (StorableSlab) Split(_ SlabStorage) (Slab, Slab, error) {
func (*StorableSlab) Split(_ SlabStorage) (Slab, Slab, error) {
return nil, nil, NewNotApplicableError("StorableSlab", "Slab", "Split")
}

func (StorableSlab) Merge(_ Slab) error {
func (*StorableSlab) Merge(_ Slab) error {
return NewNotApplicableError("StorableSlab", "Slab", "Merge")
}

func (StorableSlab) LendToRight(_ Slab) error {
func (*StorableSlab) LendToRight(_ Slab) error {
return NewNotApplicableError("StorableSlab", "Slab", "LendToRight")
}

func (StorableSlab) BorrowFromRight(_ Slab) error {
func (*StorableSlab) BorrowFromRight(_ Slab) error {
return NewNotApplicableError("StorableSlab", "Slab", "BorrowFromRight")
}
4 changes: 2 additions & 2 deletions storable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,8 @@ func (v StringValue) Storable(storage SlabStorage, address Address, maxInlineSiz
}

slab := &StorableSlab{
ID: id,
Storable: v,
slabID: id,
storable: v,
}

// Store StorableSlab in storage
Expand Down
6 changes: 5 additions & 1 deletion storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,16 @@ import (

const LedgerBaseStorageSlabPrefix = "$"

type ID [16]byte
// ValueID identifies Array and OrderedMap.
type ValueID [16]byte

type (
Address [8]byte
SlabIndex [8]byte

// SlabID identifies slab in storage.
// SlabID should only be used to retrieve,
// store, and remove slab in storage.
SlabID struct {
address Address
index SlabIndex
Expand Down

0 comments on commit 5224515

Please sign in to comment.