202 lines
5.4 KiB
Go
202 lines
5.4 KiB
Go
|
package prefixes
|
||
|
|
||
|
import (
|
||
|
"encoding/binary"
|
||
|
"fmt"
|
||
|
"reflect"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/go-restruct/restruct"
|
||
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||
|
)
|
||
|
|
||
|
func init() {
|
||
|
restruct.EnableExprBeta()
|
||
|
}
|
||
|
|
||
|
// Type OnesComplementEffectiveAmount (uint64) has to be encoded specially
|
||
|
// to get the desired sort ordering.
|
||
|
// Implement the Sizer, Packer, Unpacker interface to handle it manually.
|
||
|
|
||
|
func (amt *OnesComplementEffectiveAmount) SizeOf() int {
|
||
|
return 8
|
||
|
}
|
||
|
|
||
|
func (amt *OnesComplementEffectiveAmount) Pack(buf []byte, order binary.ByteOrder) ([]byte, error) {
|
||
|
binary.BigEndian.PutUint64(buf, OnesCompTwiddle64-uint64(*amt))
|
||
|
return buf[8:], nil
|
||
|
}
|
||
|
|
||
|
func (amt *OnesComplementEffectiveAmount) Unpack(buf []byte, order binary.ByteOrder) ([]byte, error) {
|
||
|
*amt = OnesComplementEffectiveAmount(OnesCompTwiddle64 - binary.BigEndian.Uint64(buf))
|
||
|
return buf[8:], nil
|
||
|
}
|
||
|
|
||
|
// Struct BlockTxsValue has a field TxHashes of type []*chainhash.Hash.
|
||
|
// I haven't been able to figure out the right annotations to make
|
||
|
// restruct.Pack,Unpack work automagically.
|
||
|
// Implement the Sizer, Packer, Unpacker interface to handle it manually.
|
||
|
|
||
|
func (kv *BlockTxsValue) SizeOf() int {
|
||
|
return 32 * len(kv.TxHashes)
|
||
|
}
|
||
|
|
||
|
func (kv *BlockTxsValue) Pack(buf []byte, order binary.ByteOrder) ([]byte, error) {
|
||
|
offset := 0
|
||
|
for _, h := range kv.TxHashes {
|
||
|
offset += copy(buf[offset:], h[:])
|
||
|
}
|
||
|
return buf[offset:], nil
|
||
|
}
|
||
|
|
||
|
func (kv *BlockTxsValue) Unpack(buf []byte, order binary.ByteOrder) ([]byte, error) {
|
||
|
offset := 0
|
||
|
kv.TxHashes = make([]*chainhash.Hash, len(buf)/32)
|
||
|
for i := range kv.TxHashes {
|
||
|
kv.TxHashes[i] = (*chainhash.Hash)(buf[offset:32])
|
||
|
offset += 32
|
||
|
}
|
||
|
return buf[offset:], nil
|
||
|
}
|
||
|
|
||
|
func genericNew(prefix []byte, key bool) (interface{}, error) {
|
||
|
t, ok := prefixRegistry[prefix[0]]
|
||
|
if !ok {
|
||
|
panic(fmt.Sprintf("not handled: prefix=%v", prefix))
|
||
|
}
|
||
|
if key {
|
||
|
return t.newKey(), nil
|
||
|
}
|
||
|
return t.newValue(), nil
|
||
|
}
|
||
|
|
||
|
func GenericPack(kv interface{}, fields int) ([]byte, error) {
|
||
|
// Locate the byte offset of the first excluded field.
|
||
|
offset := 0
|
||
|
if fields > 0 {
|
||
|
v := reflect.ValueOf(kv)
|
||
|
t := v.Type()
|
||
|
// Handle indirection to reach kind=Struct.
|
||
|
switch t.Kind() {
|
||
|
case reflect.Interface, reflect.Pointer:
|
||
|
v = v.Elem()
|
||
|
t = v.Type()
|
||
|
default:
|
||
|
panic(fmt.Sprintf("not handled: %v", t.Kind()))
|
||
|
}
|
||
|
count := 0
|
||
|
for _, sf := range reflect.VisibleFields(t) {
|
||
|
if !sf.IsExported() {
|
||
|
continue
|
||
|
}
|
||
|
if sf.Anonymous && strings.HasPrefix(sf.Name, "LengthEncoded") {
|
||
|
fields += 1 // Skip it but process NameLen and Name instead.
|
||
|
continue
|
||
|
}
|
||
|
if count > fields {
|
||
|
break
|
||
|
}
|
||
|
sz, err := restruct.SizeOf(v.FieldByIndex(sf.Index).Interface())
|
||
|
if err != nil {
|
||
|
panic(fmt.Sprintf("not handled: %v: %v", sf.Name, sf.Type.Kind()))
|
||
|
}
|
||
|
offset += sz
|
||
|
count += 1
|
||
|
}
|
||
|
}
|
||
|
// Pack the struct. No ability to partially pack.
|
||
|
buf, err := restruct.Pack(binary.BigEndian, kv)
|
||
|
if err != nil {
|
||
|
panic(fmt.Sprintf("not handled: %v", err))
|
||
|
}
|
||
|
// Return a prefix if some fields were excluded.
|
||
|
if fields > 0 {
|
||
|
return buf[:offset], nil
|
||
|
}
|
||
|
return buf, nil
|
||
|
}
|
||
|
|
||
|
func GenericUnpack(pfx []byte, key bool, buf []byte) (interface{}, error) {
|
||
|
kv, _ := genericNew(pfx, key)
|
||
|
err := restruct.Unpack(buf, binary.BigEndian, kv)
|
||
|
if err != nil {
|
||
|
panic(fmt.Sprintf("not handled: %v", err))
|
||
|
}
|
||
|
return kv, nil
|
||
|
}
|
||
|
|
||
|
func GetSerializationAPI(prefix []byte) *SerializationAPI {
|
||
|
t, ok := prefixRegistry[prefix[0]]
|
||
|
if !ok {
|
||
|
panic(fmt.Sprintf("not handled: prefix=%v", prefix))
|
||
|
}
|
||
|
if t.API != nil {
|
||
|
return t.API
|
||
|
}
|
||
|
return ProductionAPI
|
||
|
}
|
||
|
|
||
|
type SerializationAPI struct {
|
||
|
PackKey func(key BaseKey) ([]byte, error)
|
||
|
PackPartialKey func(key BaseKey, fields int) ([]byte, error)
|
||
|
PackValue func(value BaseValue) ([]byte, error)
|
||
|
UnpackKey func(key []byte) (BaseKey, error)
|
||
|
UnpackValue func(prefix []byte, value []byte) (BaseValue, error)
|
||
|
}
|
||
|
|
||
|
var ProductionAPI = &SerializationAPI{
|
||
|
PackKey: PackGenericKey,
|
||
|
PackPartialKey: PackPartialGenericKey,
|
||
|
PackValue: PackGenericValue,
|
||
|
UnpackKey: UnpackGenericKey,
|
||
|
UnpackValue: UnpackGenericValue,
|
||
|
}
|
||
|
|
||
|
var RegressionAPI_1 = &SerializationAPI{
|
||
|
PackKey: func(key BaseKey) ([]byte, error) {
|
||
|
return GenericPack(key, -1)
|
||
|
},
|
||
|
PackPartialKey: func(key BaseKey, fields int) ([]byte, error) {
|
||
|
return GenericPack(key, fields)
|
||
|
},
|
||
|
PackValue: func(value BaseValue) ([]byte, error) {
|
||
|
return GenericPack(value, -1)
|
||
|
},
|
||
|
UnpackKey: UnpackGenericKey,
|
||
|
UnpackValue: UnpackGenericValue,
|
||
|
}
|
||
|
|
||
|
var RegressionAPI_2 = &SerializationAPI{
|
||
|
PackKey: PackGenericKey,
|
||
|
PackPartialKey: PackPartialGenericKey,
|
||
|
PackValue: PackGenericValue,
|
||
|
UnpackKey: func(key []byte) (BaseKey, error) {
|
||
|
k, err := GenericUnpack(key, true, key)
|
||
|
return k.(BaseKey), err
|
||
|
},
|
||
|
UnpackValue: func(prefix []byte, value []byte) (BaseValue, error) {
|
||
|
k, err := GenericUnpack(prefix, false, value)
|
||
|
return k.(BaseValue), err
|
||
|
},
|
||
|
}
|
||
|
|
||
|
var RegressionAPI_3 = &SerializationAPI{
|
||
|
PackKey: func(key BaseKey) ([]byte, error) {
|
||
|
return GenericPack(key, -1)
|
||
|
},
|
||
|
PackPartialKey: func(key BaseKey, fields int) ([]byte, error) {
|
||
|
return GenericPack(key, fields)
|
||
|
},
|
||
|
PackValue: func(value BaseValue) ([]byte, error) {
|
||
|
return GenericPack(value, -1)
|
||
|
},
|
||
|
UnpackKey: func(key []byte) (BaseKey, error) {
|
||
|
k, err := GenericUnpack(key, true, key)
|
||
|
return k.(BaseKey), err
|
||
|
},
|
||
|
UnpackValue: func(prefix []byte, value []byte) (BaseValue, error) {
|
||
|
k, err := GenericUnpack(prefix, false, value)
|
||
|
return k.(BaseValue), err
|
||
|
},
|
||
|
}
|