github.com/iotexproject/iotex-core@v1.14.1-rc1/db/trie/mptrie/leafnode.go (about)

     1  // Copyright (c) 2019 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package mptrie
     7  
     8  import (
     9  	"bytes"
    10  
    11  	"google.golang.org/protobuf/proto"
    12  
    13  	"github.com/iotexproject/iotex-core/db/trie"
    14  	"github.com/iotexproject/iotex-core/db/trie/triepb"
    15  )
    16  
    17  type leafNode struct {
    18  	cacheNode
    19  	key   keyType
    20  	value []byte
    21  }
    22  
    23  func newLeafNode(
    24  	cli client,
    25  	key keyType,
    26  	value []byte,
    27  ) (node, error) {
    28  	l := &leafNode{
    29  		cacheNode: cacheNode{
    30  			dirty: true,
    31  		},
    32  		key:   key,
    33  		value: value,
    34  	}
    35  	l.cacheNode.serializable = l
    36  	if !cli.asyncMode() {
    37  		if err := l.store(cli); err != nil {
    38  			return nil, err
    39  		}
    40  	}
    41  	if err := logNode(_nodeTypeLeaf, _actionTypeNew, l, cli); err != nil {
    42  		return nil, err
    43  	}
    44  	return l, nil
    45  }
    46  
    47  func newLeafNodeFromProtoPb(pb *triepb.LeafPb, hashVal []byte) *leafNode {
    48  	l := &leafNode{
    49  		cacheNode: cacheNode{
    50  			hashVal: hashVal,
    51  			dirty:   false,
    52  		},
    53  		key:   pb.Path,
    54  		value: pb.Value,
    55  	}
    56  	l.cacheNode.serializable = l
    57  	if err := logNode(_nodeTypeLeaf, _actionTypeNew, l, nil); err != nil {
    58  		panic(err)
    59  	}
    60  	return l
    61  }
    62  
    63  func (l *leafNode) Key() []byte {
    64  	return l.key
    65  }
    66  
    67  func (l *leafNode) Value() []byte {
    68  	return l.value
    69  }
    70  
    71  func (l *leafNode) Delete(cli client, key keyType, offset uint8) (node, error) {
    72  	if err := logNode(_nodeTypeLeaf, _actionTypeDelete, l, cli); err != nil {
    73  		return nil, err
    74  	}
    75  	if !bytes.Equal(l.key[offset:], key[offset:]) {
    76  		return nil, trie.ErrNotExist
    77  	}
    78  	return nil, l.delete(cli)
    79  }
    80  
    81  func (l *leafNode) Upsert(cli client, key keyType, offset uint8, value []byte) (node, error) {
    82  	if err := logNode(_nodeTypeLeaf, _actionTypeUpsert, l, cli); err != nil {
    83  		return nil, err
    84  	}
    85  	matched := commonPrefixLength(l.key[offset:], key[offset:])
    86  	if offset+matched == uint8(len(key)) {
    87  		if err := l.delete(cli); err != nil {
    88  			return nil, err
    89  		}
    90  		return newLeafNode(cli, key, value)
    91  	}
    92  	// split into another leaf node and create branch/extension node
    93  	newl, err := newLeafNode(cli, key, value)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	oldLeaf := l
    98  	if !cli.asyncMode() {
    99  		if err := l.store(cli); err != nil {
   100  			return nil, err
   101  		}
   102  	}
   103  	bnode, err := newBranchNode(
   104  		cli,
   105  		map[byte]node{
   106  			key[offset+matched]:   newl,
   107  			l.key[offset+matched]: oldLeaf,
   108  		},
   109  		nil,
   110  	)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	if matched == 0 {
   115  		return bnode, nil
   116  	}
   117  
   118  	return newExtensionNode(cli, l.key[offset:offset+matched], bnode)
   119  }
   120  
   121  func (l *leafNode) Search(cli client, key keyType, offset uint8) (node, error) {
   122  	if err := logNode(_nodeTypeLeaf, _actionTypeSearch, l, cli); err != nil {
   123  		return nil, err
   124  	}
   125  	if !bytes.Equal(l.key[offset:], key[offset:]) {
   126  		return nil, trie.ErrNotExist
   127  	}
   128  
   129  	return l, nil
   130  }
   131  
   132  func (l *leafNode) proto(_ client, _ bool) (proto.Message, error) {
   133  	return &triepb.NodePb{
   134  		Node: &triepb.NodePb_Leaf{
   135  			Leaf: &triepb.LeafPb{
   136  				Path:  l.key[:],
   137  				Value: l.value,
   138  			},
   139  		},
   140  	}, nil
   141  }
   142  
   143  func (l *leafNode) Flush(cli client) error {
   144  	return l.store(cli)
   145  }