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  }