github.com/iotexproject/iotex-core@v1.14.1-rc1/db/trie/mptrie/extensionnode.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 "google.golang.org/protobuf/proto" 10 11 "github.com/iotexproject/iotex-core/db/trie" 12 "github.com/iotexproject/iotex-core/db/trie/triepb" 13 ) 14 15 // extensionNode defines a node with a path and point to a child node 16 type extensionNode struct { 17 cacheNode 18 path []byte 19 child node 20 } 21 22 func newExtensionNode( 23 cli client, 24 path []byte, 25 child node, 26 ) (node, error) { 27 e := &extensionNode{ 28 cacheNode: cacheNode{ 29 dirty: true, 30 }, 31 path: path, 32 child: child, 33 } 34 e.cacheNode.serializable = e 35 36 if !cli.asyncMode() { 37 if err := e.store(cli); err != nil { 38 return nil, err 39 } 40 } 41 if err := logNode(_nodeTypeExtension, _actionTypeNew, e, cli); err != nil { 42 return nil, err 43 } 44 return e, nil 45 } 46 47 func newExtensionNodeFromProtoPb(pb *triepb.ExtendPb, hashVal []byte) *extensionNode { 48 e := &extensionNode{ 49 cacheNode: cacheNode{ 50 hashVal: hashVal, 51 dirty: false, 52 }, 53 path: pb.Path, 54 child: newHashNode(pb.Value), 55 } 56 e.cacheNode.serializable = e 57 if err := logNode(_nodeTypeExtension, _actionTypeNew, e, nil); err != nil { 58 panic(err) 59 } 60 return e 61 } 62 63 func (e *extensionNode) Delete(cli client, key keyType, offset uint8) (node, error) { 64 if err := logNode(_nodeTypeExtension, _actionTypeDelete, e, cli); err != nil { 65 return nil, err 66 } 67 matched := e.commonPrefixLength(key[offset:]) 68 if matched != uint8(len(e.path)) { 69 return nil, trie.ErrNotExist 70 } 71 newChild, err := e.child.Delete(cli, key, offset+matched) 72 if err != nil { 73 return nil, err 74 } 75 if newChild == nil { 76 return nil, e.delete(cli) 77 } 78 if hn, ok := newChild.(*hashNode); ok { 79 if newChild, err = hn.LoadNode(cli); err != nil { 80 return nil, err 81 } 82 } 83 switch node := newChild.(type) { 84 case *extensionNode: 85 return node.updatePath(cli, append(e.path, node.path...)) 86 case *branchNode: 87 return e.updateChild(cli, node) 88 default: 89 if err := e.delete(cli); err != nil { 90 return nil, err 91 } 92 return node, nil 93 } 94 } 95 96 func (e *extensionNode) Upsert(cli client, key keyType, offset uint8, value []byte) (node, error) { 97 if err := logNode(_nodeTypeExtension, _actionTypeUpsert, e, cli); err != nil { 98 return nil, err 99 } 100 matched := e.commonPrefixLength(key[offset:]) 101 if matched == uint8(len(e.path)) { 102 newChild, err := e.child.Upsert(cli, key, offset+matched, value) 103 if err != nil { 104 return nil, err 105 } 106 return e.updateChild(cli, newChild) 107 } 108 eb := e.path[matched] 109 enode, err := e.updatePath(cli, e.path[matched+1:]) 110 if err != nil { 111 return nil, err 112 } 113 lnode, err := newLeafNode(cli, key, value) 114 if err != nil { 115 return nil, err 116 } 117 bnode, err := newBranchNode( 118 cli, 119 map[byte]node{ 120 eb: enode, 121 key[offset+matched]: lnode, 122 }, 123 nil, 124 ) 125 if err != nil { 126 return nil, err 127 } 128 if matched == 0 { 129 return bnode, nil 130 } 131 return newExtensionNode(cli, key[offset:offset+matched], bnode) 132 } 133 134 func (e *extensionNode) Search(cli client, key keyType, offset uint8) (node, error) { 135 if err := logNode(_nodeTypeExtension, _actionTypeSearch, e, cli); err != nil { 136 return nil, err 137 } 138 matched := e.commonPrefixLength(key[offset:]) 139 if matched != uint8(len(e.path)) { 140 return nil, trie.ErrNotExist 141 } 142 143 return e.child.Search(cli, key, offset+matched) 144 } 145 146 func (e *extensionNode) proto(cli client, flush bool) (proto.Message, error) { 147 if flush { 148 if sn, ok := e.child.(serializable); ok { 149 if err := sn.store(cli); err != nil { 150 return nil, err 151 } 152 } 153 } 154 h, err := e.child.Hash(cli) 155 if err != nil { 156 return nil, err 157 } 158 return &triepb.NodePb{ 159 Node: &triepb.NodePb_Extend{ 160 Extend: &triepb.ExtendPb{ 161 Path: e.path, 162 Value: h, 163 }, 164 }, 165 }, nil 166 } 167 168 func (e *extensionNode) Child() node { 169 return e.child 170 } 171 172 func (e *extensionNode) commonPrefixLength(key []byte) uint8 { 173 return commonPrefixLength(e.path, key) 174 } 175 176 func (e *extensionNode) Flush(cli client) error { 177 if !e.dirty { 178 return nil 179 } 180 if err := e.child.Flush(cli); err != nil { 181 return err 182 } 183 184 return e.store(cli) 185 } 186 187 func (e *extensionNode) updatePath(cli client, path []byte) (node, error) { 188 if err := e.delete(cli); err != nil { 189 return nil, err 190 } 191 return newExtensionNode(cli, path, e.child) 192 } 193 194 func (e *extensionNode) updateChild(cli client, newChild node) (node, error) { 195 err := e.delete(cli) 196 if err != nil { 197 return nil, err 198 } 199 path := make([]byte, len(e.path)) 200 copy(path, e.path) 201 return newExtensionNode(cli, path, newChild) 202 }