github.com/iotexproject/iotex-core@v1.14.1-rc1/state/factory/patchstore.go (about)

     1  // Copyright (c) 2021 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 factory
     7  
     8  import (
     9  	"encoding/csv"
    10  	"encoding/hex"
    11  	"io"
    12  	"os"
    13  	"path/filepath"
    14  	"strconv"
    15  
    16  	"github.com/pkg/errors"
    17  )
    18  
    19  const (
    20  	_Put patchType = iota
    21  	_Delete
    22  )
    23  
    24  type (
    25  	patchType uint8
    26  	patch     struct {
    27  		Type      patchType
    28  		Namespace string
    29  		Key       []byte
    30  		Value     []byte
    31  	}
    32  	patchStore struct {
    33  		patchs map[uint64][]*patch
    34  	}
    35  )
    36  
    37  /**
    38   * patch format:
    39   *   height,type,namespace,key,value
    40   *     height: uint64, the height the record will be applied
    41   *     type: PUT or DELETE
    42   *     namespace: text string
    43   *     key: hex string
    44   *     value: hex string
    45   */
    46  func newPatchStore(fpath string) (*patchStore, error) {
    47  	store := &patchStore{
    48  		patchs: map[uint64][]*patch{},
    49  	}
    50  	if fpath == "" {
    51  		return store, nil
    52  	}
    53  	file, err := os.Open(filepath.Clean(fpath))
    54  	if err != nil {
    55  		return nil, errors.Wrapf(err, "failed to open kvstore patch, %s", fpath)
    56  	}
    57  	reader := csv.NewReader(file)
    58  	reader.FieldsPerRecord = -1
    59  	for {
    60  		record, err := reader.Read()
    61  		if err == io.EOF {
    62  			break
    63  		}
    64  		if err != nil {
    65  			return nil, errors.Wrap(err, "failed to read kvstore patch")
    66  		}
    67  		if len(record) < 4 {
    68  			return nil, errors.Errorf("wrong format %+v", record)
    69  		}
    70  		height, err := strconv.ParseUint(record[0], 10, 64)
    71  		if err != nil {
    72  			return nil, errors.Wrapf(err, "failed to parse height, %s", record[0])
    73  		}
    74  		if _, ok := store.patchs[height]; !ok {
    75  			store.patchs[height] = []*patch{}
    76  		}
    77  		var t patchType
    78  		var value []byte
    79  		switch record[1] {
    80  		case "PUT":
    81  			t = _Put
    82  			if len(record) != 5 {
    83  				return nil, errors.Errorf("wrong put format %+v", record)
    84  			}
    85  			value, err = hex.DecodeString(record[4])
    86  			if err != nil {
    87  				return nil, errors.Wrapf(err, "failed to parse value, %s", record[4])
    88  			}
    89  		case "DELETE":
    90  			t = _Delete
    91  		default:
    92  			return nil, errors.Errorf("invalid patch type, %s", record[1])
    93  		}
    94  		key, err := hex.DecodeString(record[3])
    95  		if err != nil {
    96  			return nil, errors.Wrapf(err, "failed to parse key, %s", record[3])
    97  		}
    98  		store.patchs[height] = append(store.patchs[height], &patch{
    99  			Type:      t,
   100  			Namespace: record[2],
   101  			Key:       key,
   102  			Value:     value,
   103  		})
   104  	}
   105  
   106  	return store, nil
   107  }
   108  
   109  func (ps *patchStore) Get(height uint64) []*patch {
   110  	if ps == nil {
   111  		return nil
   112  	}
   113  	return ps.patchs[height]
   114  }