|
|
|
|
Quelle gob_tools.go
Sprache: unbekannt
|
|
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]
// Copyright 2024 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2. 0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2. 0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gobtools
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"reflect"
"unsafe"
"github.com/google/blueprint/dbtools"
"github.com/google/blueprint/proptools"
"github.com/google/blueprint/syncmap"
)
type EncContext interface {
ReferenceEnc
}
type ReferenceEnc interface {
EncodeReferences() error
EncodeReference(value any, buf *bytes.Buffer, typ string, encode func(value any, buf *byte s.Buffer) error) error
DecodeReference(buf *bytes.Reader, decode func(buf *bytes.Reader) (any, error)) (any, error)
}
func NewEncContext(db dbtools.KeyValueStore) EncContext {
return NewReferencesEncoder(db)
}
type ReferencesEncoder struct {
encodedReferences syncmap.SyncMap[any, *encodedReference]
decodedReferences syncmap.SyncMap[proptools.Hash, any]
db dbtools.KeyValueStore
}
// NewReferencesEncoder creates and initializes a new ReferencesEncoder.
func NewReferencesEncoder(db dbtools.KeyValueStore) *ReferencesEncoder {
ctx := &ReferencesEncoder{
db: db,
}
return ctx
}
func NewReferencesEncoderForTest() *ReferencesEncoder {
ctx := &ReferencesEncoder{
db: &dbtools.InMemKeyValueStore{},
}
return ctx
}
// encodedReference stores information about an encoded value reference.
type encodedReference struct {
valueRefId proptools.Hash // The unique hash ID for the value.
valueEncodingBuffer *bytes.Buffer // The buffer containing the encoded actual value (including its own ref ID and length).
}
func (b *ReferencesEncoder) openForTests() error {
if b.db != nil {
panic(fmt.Errorf("db is already open"))
}
b.db = &dbtools.InMemKeyValueStore{}
return nil
}
func (c *ReferencesEncoder) EncodeReferences() error {
var err error
c.encodedReferences.Range(func(_ any, value *encodedReference) bool {
if err = c.db.Put(hashToBytes(value.valueRefId), value.valueEncodingBuffer.Bytes()); err != nil {
return false
}
return true
})
return err
}
func (c *ReferencesEncoder) EncodeReference(value any, buf *bytes.Buffer, typ string, encode func(value any, buf *bytes.Buffer) error) error {
var encStruct *encodedReference
var ok bool
// Check if the value has already been encoded.
if encStruct, ok = c.encodedReferences.Load(value); !ok {
// If the value is encountered for the first time:
// Calculate a unique hash for the value using the type's specific hash seed.
ref, err := proptools.CalculateHashReflection(valueHashConfig{
typ: typ,
value: value,
})
if err != nil {
return err
}
// Encode the calculated reference ID into the main buffer (where the value's full encoded data will reside).
if err := EncodeUint64(buf, ref[0]); err != nil {
return err
}
// Create a new encodedReference struct to store the value's details.
encStruct = &encodedReference{
valueRefId: ref,
valueEncodingBuffer: new(bytes.Buffer), // Buffer to hold the actual value's encoded data.
}
if err := encode(value, encStruct.valueEncodingBuffer); err != nil {
return err
}
// Store the newly created encodedReference in the type-specific map.
c.encodedReferences.LoadOrStore(value, encStruct)
return nil // Successfully encoded and stored the new value reference.
}
// If the value has been encoded before, just encode its existing reference ID
// into the output buffer. This optimizes for repeated values.
return EncodeUint64(buf, encStruct.valueRefId[0])
}
func (c *ReferencesEncoder) DecodeReference(buf *bytes.Reader, decode func(buf *bytes.Reader) (any, error)) (any, error) {
var refVal uint64 // Variable to store the decoded reference ID.
// Decode the reference ID of the value from the input stream.
if err := DecodeUint64(buf, &refVal); err != nil {
return nil, err // Return error if decoding the reference fails.
}
ref := proptools.Hash{refVal}
// Try to load the value using its reference ID from the decoded values cache.
if v, ok := c.decodedReferences.Load(ref); !ok {
data, err := c.db.Get(hashToBytes(ref))
if err != nil {
panic(fmt.Errorf("failed to Get from db: %v", err))
return nil, err
}
// Decode the actual value from its raw byte slice using the provided 'decode' function.
var value any
if value, err = decode(bytes.NewReader(data)); err != nil {
return nil, err // Return error if decoding the value from raw bytes fails.
}
// Store the newly decoded value in the cache for future lookups.
c.decodedReferences.LoadOrStore(ref, value)
return value, nil // Return the decoded value.
} else {
// If the value is found in the cache, return the cached value.
return v, nil // Assert type and return.
}
}
type valueHashConfig struct {
typ string
value any
}
func hashToBytes(value proptools.Hash) []byte {
var ret [proptools.HashSize]byte
value.PutBigEndian(ret[:])
return ret[:]
}
func EncodeReference[T comparable](c ReferenceEnc, value T, buf *bytes.Buffer, encode func(v T, buf *bytes.Buffer) error) error {
var defValue T
typ := reflect.TypeOf(defValue)
return c.EncodeReference(value, buf, typ.String(), func(v any, buf *bytes.Buffer) error {
return encode(v.(T), buf)
})
}
// DecodeReference handles the decoding of a value by reference.
// It reads a reference ID from the buffer, then looks up the corresponding
// value. If not already decoded, it decodes from raw data and caches the result.
func DecodeReference[T comparable](c ReferenceEnc, value T, buf *bytes.Reader, decode func(v T, buf *bytes.Reader) error) (T, error) {
v, _ := c.DecodeReference(buf, func(buf *bytes.Reader) (any, error) {
if err := decode(value, buf); err != nil {
return nil, err
}
return value, nil
})
return v.(T), nil // Assert type and return.
}
// To decode an interface type, we need to store the info about the underlying
// concreate type of the value during encoding. When decoding we will use that
// info and the registry map below to recreate a default value of the concrete
// type and then deserialize the stored data into it.
var typeRegistry = make(map[int16]func() CustomDec)
// Enum to represent the underlying type of the interface field.
type interfaceType int16
const (
nilInterface interfaceType = iota
nilPointerInterface
pointerInterface
valueInterface
)
var typeRegId int16 = 0
// RegisterType registers a concrete type with the type registry, this method should
// be called from an init() function in the package. It incrementally generates an id
// and return to the caller, when encoding an instance of the struct, the id is stored
// along with the actual value of the instance. During decoding the stored id is
// used to look up the function to initiate a default instance of the struct.
func RegisterType(creator func() CustomDec) int16 {
typeRegId++
typeRegistry[typeRegId] = creator
return typeRegId
}
// Interface indicates the struct provides custom encoding logic either thru
// code generation or manual coding.
type CustomEnc interface {
Encode(ctx EncContext, buf *bytes.Buffer) error
GetTypeId() int16
}
// Interface indicates the struct provides custom decoding logic either thru
// code generation or manual coding
type CustomDec interface {
Decode(ctx EncContext, buf *bytes.Reader) error
}
// Encode a string value.
func EncodeString(buf *bytes.Buffer, s string) error {
err := EncodeInt(buf, len(s))
if err != nil {
return err
}
_, err = buf.WriteString(s)
return err
}
// Decode a string value.
func DecodeString(buf *bytes.Reader, s *string) error {
var length int
err := DecodeInt(buf, &length)
if err != nil {
return err
}
b := make([]byte, length)
_, err = io.ReadFull(buf, b)
if err == nil {
*s = unsafe.String(unsafe.SliceData(b), len(b))
}
return err
}
// Encode a primitive value.
func EncodeSimple[T any](buf *bytes.Buffer, b T) error {
return binary.Write(buf, binary.BigEndian, b)
}
// Decode a primitive value.
func DecodeSimple[T any](buf *bytes.Reader, data *T) error {
return binary.Read(buf, binary.BigEndian, data)
}
func EncodeBool(buf *bytes.Buffer, b bool) error {
var c byte = 0
if b {
c = 1
}
_, err := buf.Write([]byte{c})
return err
}
func DecodeBool(buf *bytes.Reader, b *bool) error {
c, err := buf.ReadByte()
if err != nil {
return err
}
*b = c != 0
return nil
}
func EncodeVarint[T int | int16 | int32 | int64](buf *bytes.Buffer, i T) error {
var b [binary.MaxVarintLen64]byte
n := binary.PutVarint(b[:], int64(i))
_, err := buf.Write(b[:n])
return err
}
func EncodeUvarint[T uint | uint16 | uint32 | uint64](buf *bytes.Buffer, i T) error {
var b [binary.MaxVarintLen64]byte
n := binary.PutUvarint(b[:], uint64(i))
_, err := buf.Write(b[:n])
return err
}
func DecodeVarint[T int | int16 | int32 | int64](buf *bytes.Reader, i *T) error {
n, err := binary.ReadVarint(buf)
if err != nil {
return err
}
*i = T(n)
return nil
}
func DecodeUvarint[T uint | uint16 | uint32 | uint64](buf *bytes.Reader, i *T) error {
n, err := binary.ReadUvarint(buf)
if err != nil {
return err
}
*i = T(n)
return nil
}
func EncodeInt16(buf *bytes.Buffer, i int16) error { return EncodeVarint(buf, i) }
func EncodeInt32(buf *bytes.Buffer, i int32) error { return EncodeVarint(buf, i) }
func EncodeInt64(buf *bytes.Buffer, i int64) error { return EncodeVarint(buf, i) }
func EncodeUint16(buf *bytes.Buffer, i uint16) error { return EncodeUvarint(buf, i) }
func EncodeUint32(buf *bytes.Buffer, i uint32) error { return EncodeUvarint(buf, i) }
func EncodeUint64(buf *bytes.Buffer, i uint64) error { return EncodeUvarint(buf, i) }
func DecodeInt16(buf *bytes.Reader, i *int16) error { return DecodeVarint(buf, i) }
func DecodeInt32(buf *bytes.Reader, i *int32) error { return DecodeVarint(buf, i) }
func DecodeInt64(buf *bytes.Reader, i *int64) error { return DecodeVarint(buf, i) }
func DecodeUint16(buf *bytes.Reader, i *uint16) error { return DecodeUvarint(buf, i) }
func DecodeUint32(buf *bytes.Reader, i *uint32) error { return DecodeUvarint(buf, i) }
func DecodeUint64(buf *bytes.Reader, i *uint64) error { return DecodeUvarint(buf, i) }
func EncodeInt(buf *bytes.Buffer, i int) error { return EncodeVarint(buf, int64(i)) }
func DecodeInt(buf *bytes.Reader, i *int) error {
var i64 int64
err := DecodeVarint(buf, &i64)
if err != nil {
return err
}
*i = int(i64)
return nil
}
// Encode a struct. It uses type assert to leverage Gob to encode the value when
// the struct hasn't be converted to use codegen to generate encoding logic, this
// should be removed once all are converted.
func EncodeStruct(c EncContext, buf *bytes.Buffer, val any) error {
// val is pointer to either a struct or an interface{}. If it is the latter the
// type assert below will fail even if the underlying concrete type implements
// the CustomEnc interface. This is intentional in order for ob to handle the
// interface case, where it will store the interface info and is albe to properly
// deserialize it later. Otherwise, it will be serialized as a concrete type,
// then later it can't be deserialized back to an interface field.
if encdec, ok := val.(CustomEnc); ok {
return encdec.Encode(c, buf)
} else {
panic(fmt.Errorf("encoding type is not supported: %T", val))
}
}
// Encode an interface value.
func EncodeInterface(c EncContext, buf *bytes.Buffer, data any) error {
if data == nil {
return EncodeInt16(buf, int16(nilInterface))
}
intfType := valueInterface
if v := reflect.ValueOf(data); v.Kind() == reflect.Ptr {
if v.IsNil() {
return fmt.Errorf("nil pointer is not supported in EncodeInterface")
} else {
intfType = pointerInterface
}
}
if err := EncodeInt16(buf, int16(intfType)); err != nil {
return err
}
val := data.(CustomEnc)
if err := EncodeInt16(buf, val.GetTypeId()); err != nil {
return err
}
return val.Encode(c, buf)
}
// Decode a struct. It uses type assert to leverage Gob to decode the value when
// the struct hasn't be converted to use codegen to generate decoding logic, this
// should be removed once all are converted
func DecodeStruct(c EncContext, buf *bytes.Reader, data any) error {
if encdec, ok := data.(CustomDec); ok {
return encdec.Decode(c, buf)
} else {
panic(fmt.Errorf("decoding type is not supported: %T", data))
}
}
// Decode an interface value.
func DecodeInterface(c EncContext, buf *bytes.Reader) (any, error) {
var intfType int16
if err := DecodeInt16(buf, &intfType); err != nil || intfType == int16(nilInterface) {
return nil, err
}
var typeId int16
if err := DecodeInt16(buf, &typeId); err != nil {
return nil, err
}
if f, ok := typeRegistry[typeId]; !ok {
return nil, fmt.Errorf("type not registered: %d", typeId)
} else {
val := f()
if err := val.Decode(c, buf); err != nil {
return nil, err
} else if intfType == int16(valueInterface) {
return reflect.ValueOf(val).Elem().Interface(), nil
} else {
return val, nil
}
}
}
[Dauer der Verarbeitung: 0.2 Sekunden, vorverarbeitet 2026-06-28]
|
2026-07-09
|
|
|
|
|