github.com/onflow/atree@v0.6.0/encode.go (about)

     1  /*
     2   * Atree - Scalable Arrays and Ordered Maps
     3   *
     4   * Copyright 2021 Dapper Labs, Inc.
     5   *
     6   * Licensed under the Apache License, Version 2.0 (the "License");
     7   * you may not use this file except in compliance with the License.
     8   * You may obtain a copy of the License at
     9   *
    10   *   http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   * Unless required by applicable law or agreed to in writing, software
    13   * distributed under the License is distributed on an "AS IS" BASIS,
    14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15   * See the License for the specific language governing permissions and
    16   * limitations under the License.
    17   */
    18  
    19  package atree
    20  
    21  import (
    22  	"io"
    23  	"math"
    24  
    25  	"github.com/fxamacker/cbor/v2"
    26  )
    27  
    28  // Encoder writes atree slabs to io.Writer.
    29  type Encoder struct {
    30  	io.Writer
    31  	CBOR    *cbor.StreamEncoder
    32  	Scratch [64]byte
    33  }
    34  
    35  func NewEncoder(w io.Writer, encMode cbor.EncMode) *Encoder {
    36  	streamEncoder := encMode.NewStreamEncoder(w)
    37  	return &Encoder{
    38  		Writer: w,
    39  		CBOR:   streamEncoder,
    40  	}
    41  }
    42  
    43  type StorableDecoder func(
    44  	decoder *cbor.StreamDecoder,
    45  	storableSlabStorageID StorageID,
    46  ) (
    47  	Storable,
    48  	error,
    49  )
    50  
    51  func DecodeSlab(
    52  	id StorageID,
    53  	data []byte,
    54  	decMode cbor.DecMode,
    55  	decodeStorable StorableDecoder,
    56  	decodeTypeInfo TypeInfoDecoder,
    57  ) (
    58  	Slab,
    59  	error,
    60  ) {
    61  	if len(data) < versionAndFlagSize {
    62  		return nil, NewDecodingErrorf("data is too short")
    63  	}
    64  
    65  	flag := data[1]
    66  
    67  	dataType := getSlabType(flag)
    68  	switch dataType {
    69  
    70  	case slabArray:
    71  
    72  		switch arrayDataType := getSlabArrayType(flag); arrayDataType {
    73  		case slabArrayData:
    74  			return newArrayDataSlabFromData(id, data, decMode, decodeStorable, decodeTypeInfo)
    75  		case slabArrayMeta:
    76  			return newArrayMetaDataSlabFromData(id, data, decMode, decodeTypeInfo)
    77  		case slabBasicArray:
    78  			return newBasicArrayDataSlabFromData(id, data, decMode, decodeStorable)
    79  		default:
    80  			return nil, NewDecodingErrorf("data has invalid flag 0x%x", flag)
    81  		}
    82  
    83  	case slabMap:
    84  
    85  		switch mapDataType := getSlabMapType(flag); mapDataType {
    86  		case slabMapData:
    87  			return newMapDataSlabFromData(id, data, decMode, decodeStorable, decodeTypeInfo)
    88  		case slabMapMeta:
    89  			return newMapMetaDataSlabFromData(id, data, decMode, decodeTypeInfo)
    90  		case slabMapCollisionGroup:
    91  			return newMapDataSlabFromData(id, data, decMode, decodeStorable, decodeTypeInfo)
    92  		default:
    93  			return nil, NewDecodingErrorf("data has invalid flag 0x%x", flag)
    94  		}
    95  
    96  	case slabStorable:
    97  		cborDec := decMode.NewByteStreamDecoder(data[versionAndFlagSize:])
    98  		storable, err := decodeStorable(cborDec, id)
    99  		if err != nil {
   100  			// Wrap err as external error (if needed) because err is returned by StorableDecoder callback.
   101  			return nil, wrapErrorfAsExternalErrorIfNeeded(err, "failed to decode slab storable")
   102  		}
   103  		return StorableSlab{
   104  			StorageID: id,
   105  			Storable:  storable,
   106  		}, nil
   107  
   108  	default:
   109  		return nil, NewDecodingErrorf("data has invalid flag 0x%x", flag)
   110  	}
   111  }
   112  
   113  // TODO: make it inline
   114  func GetUintCBORSize(n uint64) uint32 {
   115  	if n <= 23 {
   116  		return 1
   117  	}
   118  	if n <= math.MaxUint8 {
   119  		return 2
   120  	}
   121  	if n <= math.MaxUint16 {
   122  		return 3
   123  	}
   124  	if n <= math.MaxUint32 {
   125  		return 5
   126  	}
   127  	return 9
   128  }