| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317 |
- ////////////////////////////////////////////////////////////////////////////////
- //
- // DO NOT MODIFY
- //
- // ┻━┻ ︵ヽ(`Д´)ノ︵ ┻━┻
- //
- //
- // This source file was automatically generated by bingen.
- //
- ////////////////////////////////////////////////////////////////////////////////
- package metric
- import (
- "cmp"
- "fmt"
- "io"
- "iter"
- "os"
- "reflect"
- "slices"
- "strings"
- "sync"
- "time"
- "unsafe"
- util "github.com/opencost/opencost/core/pkg/util"
- )
- const (
- // GeneratorPackageName is the package the generator is targetting
- GeneratorPackageName string = "metric"
- StringHeaderSize = int64(unsafe.Sizeof(""))
- // BinaryTagStringTable is written and/or read prior to the existence of a string
- // table (where each index is encoded as a string entry in the resource
- BinaryTagStringTable string = "BGST"
- // DefaultCodecVersion is used for any resources listed in the Default version set
- DefaultCodecVersion uint8 = 1
- )
- //--------------------------------------------------------------------------
- // Configuration
- //--------------------------------------------------------------------------
- var (
- bingenConfigLock sync.RWMutex
- bingenConfig *BingenConfiguration = DefaultBingenConfiguration()
- )
- // BingenConfiguration is used to set any custom configuration in the way files are encoded
- // or decoded.
- type BingenConfiguration struct {
- // FileBackedStringTableEnabled enables the use of file-backed string tables for streaming
- // bingen decoding.
- FileBackedStringTableEnabled bool
- // FileBackedStringTableDir is the directory to write the string table files for reading.
- FileBackedStringTableDir string
- // FileBackedStringTableMemoMaxBytes limits in-memory memoization for file-backed table lookups.
- // 0 disables memoization.
- FileBackedStringTableMemoMaxBytes int64
- }
- // DefaultBingenConfiguration creates the default implementation of the bingen configuration
- // and returns it.
- func DefaultBingenConfiguration() *BingenConfiguration {
- return &BingenConfiguration{
- FileBackedStringTableEnabled: false,
- FileBackedStringTableDir: os.TempDir(),
- FileBackedStringTableMemoMaxBytes: 0,
- }
- }
- // ConfigureBingen accepts a new *BingenConfiguration instance which updates the internal decoder
- // and encoder behavior.
- func ConfigureBingen(config *BingenConfiguration) {
- bingenConfigLock.Lock()
- defer bingenConfigLock.Unlock()
- if config == nil {
- config = DefaultBingenConfiguration()
- }
- bingenConfig = config
- }
- // IsBingenFileBackedStringTableEnabled accessor for file backed string table configuration
- func IsBingenFileBackedStringTableEnabled() bool {
- bingenConfigLock.RLock()
- defer bingenConfigLock.RUnlock()
- return bingenConfig.FileBackedStringTableEnabled
- }
- // BingenFileBackedStringTableDir returns the directory configured for file backed string tables.
- func BingenFileBackedStringTableDir() string {
- bingenConfigLock.RLock()
- defer bingenConfigLock.RUnlock()
- return bingenConfig.FileBackedStringTableDir
- }
- // BingenFileBackedStringTableMemoMaxBytes returns the maximum bytes used for file-backed memo cache.
- func BingenFileBackedStringTableMemoMaxBytes() int64 {
- bingenConfigLock.RLock()
- defer bingenConfigLock.RUnlock()
- return bingenConfig.FileBackedStringTableMemoMaxBytes
- }
- //--------------------------------------------------------------------------
- // Type Map
- //--------------------------------------------------------------------------
- // Generated type map for resolving interface implementations to to concrete types
- var typeMap map[string]reflect.Type = map[string]reflect.Type{
- "Update": reflect.TypeFor[Update](),
- "UpdateSet": reflect.TypeFor[UpdateSet](),
- }
- //--------------------------------------------------------------------------
- // Type Helpers
- //--------------------------------------------------------------------------
- // isBinaryTag returns true when the first bytes in the provided binary matches the tag
- func isBinaryTag(data []byte, tag string) bool {
- if len(data) < len(tag) {
- return false
- }
- return string(data[:len(tag)]) == tag
- }
- // isReaderBinaryTag is used to peek the header for an io.Reader Buffer
- func isReaderBinaryTag(buff *util.Buffer, tag string) bool {
- data, err := buff.Peek(len(tag))
- if err != nil && err != io.EOF {
- panic(fmt.Sprintf("called Peek() on a non buffered reader: %s", err))
- }
- if len(data) < len(tag) {
- return false
- }
- return string(data[:len(tag)]) == tag
- }
- // typeToString determines the basic properties of the type, the qualifier, package path, and
- // type name, and returns the qualified type
- func typeToString(f interface{}) string {
- qual := ""
- t := reflect.TypeOf(f)
- if t.Kind() == reflect.Ptr {
- t = t.Elem()
- qual = "*"
- }
- return fmt.Sprintf("%s%s.%s", qual, t.PkgPath(), t.Name())
- }
- // resolveType uses the name of a type and returns the package, base type name, and whether
- // or not it's a pointer.
- func resolveType(t string) (pkg string, name string, isPtr bool) {
- isPtr = t[:1] == "*"
- if isPtr {
- t = t[1:]
- }
- slashIndex := strings.LastIndex(t, "/")
- if slashIndex >= 0 {
- t = t[slashIndex+1:]
- }
- parts := strings.Split(t, ".")
- if parts[0] == GeneratorPackageName {
- parts[0] = ""
- }
- pkg = parts[0]
- name = parts[1]
- return
- }
- //--------------------------------------------------------------------------
- // Stream Helpers
- //--------------------------------------------------------------------------
- // StreamFactoryFunc is an alias for a func that creates a BingenStream implementation.
- type StreamFactoryFunc func(io.Reader) BingenStream
- // Generated streamable factory map for finding the specific new stream methods
- // by T type
- var streamFactoryMap map[reflect.Type]StreamFactoryFunc = map[reflect.Type]StreamFactoryFunc{
- reflect.TypeFor[UpdateSet](): NewUpdateSetStream,
- }
- // NewStreamFor accepts an io.Reader, and returns a new BingenStream for the generic T
- // type provided _if_ it is a registered bingen type that is annotated as 'streamable'. See
- // the streamFactoryMap for generated type listings.
- func NewStreamFor[T any](reader io.Reader) (BingenStream, error) {
- typeKey := reflect.TypeFor[T]()
- factory, ok := streamFactoryMap[typeKey]
- if !ok {
- return nil, fmt.Errorf("the type: %s is not a registered bingen streamable type", typeKey.Name())
- }
- return factory(reader), nil
- }
- // BingenStream is the stream interface for all streamable types
- type BingenStream interface {
- // Stream returns the iterator which will stream each field of the target type and
- // return the field info as well as the value.
- Stream() iter.Seq2[BingenFieldInfo, *BingenValue]
- // Close will close any dynamic io.Reader used to stream in the fields
- Close()
- // Error returns an error if one occurred during the process of streaming the type's fields.
- // This can be checked after iterating through the Stream().
- Error() error
- }
- // BingenValue contains the value of a field as well as any index/key associated with that value.
- type BingenValue struct {
- Value any
- Index any
- }
- // IsNil is just a method accessor way to check to see if the value returned was nil
- func (bv *BingenValue) IsNil() bool {
- return bv == nil
- }
- // creates a single BingenValue instance without a key or index
- func singleV(value any) *BingenValue {
- return &BingenValue{
- Value: value,
- }
- }
- // creates a pair of key/index and value.
- func pairV(index any, value any) *BingenValue {
- return &BingenValue{
- Value: value,
- Index: index,
- }
- }
- // BingenFieldInfo contains the type of the field being streamed as well as the name of the field.
- type BingenFieldInfo struct {
- Type reflect.Type
- Name string
- }
- //--------------------------------------------------------------------------
- // String Table Writer
- //--------------------------------------------------------------------------
- // StringTableWriter is the interface used to write the string table for encoding.
- type StringTableWriter interface {
- // AddOrGet adds a string to the string table and returns the new index or
- // an existing index.
- AddOrGet(s string) int
- // WriteTo will write the StringTable data (with the header) to the provided
- // Buffer starting a the current write position
- WriteTo(b *util.Buffer)
- }
- // IndexedStringTableWriter maps strings to specific indices for encoding
- type IndexedStringTableWriter struct {
- indices map[string]int
- next int
- }
- // NewIndexedStringTableWriter Creates a new IndexedStringTableWriter instance.
- func NewIndexedStringTableWriter() *IndexedStringTableWriter {
- return &IndexedStringTableWriter{
- indices: make(map[string]int),
- next: 0,
- }
- }
- // AddOrGet retrieves a string entry's index if it exists. Otherwise, it adds the entry and returns the new index.
- func (st *IndexedStringTableWriter) AddOrGet(s string) int {
- if ind, ok := st.indices[s]; ok {
- return ind
- }
- current := st.next
- st.next++
- st.indices[s] = current
- return current
- }
- // ToSlice Converts the contents to a string array for encoding.
- func (st *IndexedStringTableWriter) ToSlice() []string {
- if st.next == 0 {
- return []string{}
- }
- sl := make([]string, st.next)
- for s, i := range st.indices {
- sl[i] = s
- }
- return sl
- }
- // ToBytes Converts the contents to a binary encoded representation
- func (st *IndexedStringTableWriter) ToBytes() []byte {
- buff := util.NewBuffer()
- st.WriteTo(buff)
- return buff.Bytes()
- }
- // WriteTo will write the StringTable data (with the header) to the provided
- // Buffer starting a the current write position
- func (st *IndexedStringTableWriter) WriteTo(buff *util.Buffer) {
- // bingen string table header
- buff.WriteBytes([]byte(BinaryTagStringTable))
- // get an ordered string slice to encode
- strs := st.ToSlice()
- buff.WriteInt(len(strs)) // table length
- for _, s := range strs {
- buff.WriteString(s)
- }
- }
- type indexed struct {
- s string
- count uint64
- index int
- }
- func newIndexed(s string, index int) *indexed {
- return &indexed{
- s: s,
- count: 1,
- index: index,
- }
- }
- // PrepassStringTableWriter maps strings to specific indices for encoding, sorted by the total
- // number of times they're accessed
- type PrepassStringTableWriter struct {
- prepass map[string]*indexed
- next int
- }
- // NewPrepassStringTableWriter creates a new PrepassStringTableWriter instance.
- func NewPrepassStringTableWriter() *PrepassStringTableWriter {
- return &PrepassStringTableWriter{
- prepass: make(map[string]*indexed),
- }
- }
- // AddOrGet retrieves a string entry's index if it exists. Otherwise, it adds the entry and returns the new index.
- func (st *PrepassStringTableWriter) AddOrGet(s string) int {
- if ind, ok := st.prepass[s]; ok {
- ind.count += 1
- return ind.index
- }
- current := st.next
- st.next++
- st.prepass[s] = newIndexed(s, current)
- return current
- }
- // WriteSortedTo sorts the string table by the number of accesses, writes the table in that
- // order, then returns a new StringTableWriter implementation that can be used for the new
- // sorted order index lookups.
- func (st *PrepassStringTableWriter) WriteSortedTo(buff *util.Buffer) StringTableWriter {
- sl := make([]*indexed, st.next)
- for _, ind := range st.prepass {
- sl[ind.index] = ind
- }
- slices.SortFunc(sl, func(a *indexed, b *indexed) int {
- return -cmp.Compare(a.count, b.count)
- })
- sti := NewIndexedStringTableWriter()
- for _, ind := range sl {
- sti.AddOrGet(ind.s)
- }
- sti.WriteTo(buff)
- return sti
- }
- // WriteTo will write the StringTable data (with the header) to the provided
- // Buffer starting a the current write position
- func (st *PrepassStringTableWriter) WriteTo(buff *util.Buffer) {
- panic("Prepass StringTableWriter cannot write directly")
- }
- //--------------------------------------------------------------------------
- // String Table Reader
- //--------------------------------------------------------------------------
- // StringTableReader is the interface used to read the string table from the decoding.
- type StringTableReader interface {
- // At returns the string entry at a specific index, or panics on out of bounds.
- At(index int) string
- // Len returns the total number of strings loaded in the string table.
- Len() int
- // Close will clear the loaded table, and drop any external resources used.
- Close() error
- }
- // SliceStringTableReader is a basic pre-loaded []string that provides index-based access.
- // The cost of this implementation is holding all strings in memory, which provides faster
- // lookup performance at the expense of memory usage.
- type SliceStringTableReader struct {
- table []string
- }
- // NewSliceStringTableReaderFrom creates a new SliceStringTableReader instance loading
- // data directly from the buffer. The buffer's position should start at the table length.
- func NewSliceStringTableReaderFrom(buffer *util.Buffer) StringTableReader {
- // table length
- tl := buffer.ReadInt()
- var table []string
- if tl > 0 {
- table = make([]string, tl)
- for i := range tl {
- table[i] = buffer.ReadString()
- }
- }
- return &SliceStringTableReader{
- table: table,
- }
- }
- // At returns the string entry at a specific index, or panics on out of bounds.
- func (sstr *SliceStringTableReader) At(index int) string {
- if index < 0 || index >= len(sstr.table) {
- panic(fmt.Errorf("%s: string table index out of bounds: %d", GeneratorPackageName, index))
- }
- return sstr.table[index]
- }
- // Len returns the total number of strings loaded in the string table.
- func (sstr *SliceStringTableReader) Len() int {
- if sstr == nil {
- return 0
- }
- return len(sstr.table)
- }
- // Close for the slice tables just nils out the slice and returns
- func (sstr *SliceStringTableReader) Close() error {
- sstr.table = nil
- return nil
- }
- // fileStringRef maps a bingen string-table index to a payload stored in a temp file.
- type fileStringRef struct {
- off int64
- length int
- }
- // FileStringTableReader leverages a local file to write string table data for lookup. On
- // memory focused systems, this allows a slower parse with a significant decrease in memory
- // usage. This implementation is often pair with streaming readers for high throughput with
- // reduced memory usage.
- type FileStringTableReader struct {
- f *os.File
- refs []fileStringRef
- memo []string
- }
- // NewFileStringTableFromBuffer reads exactly tl length-prefixed (uint16) string payloads from buffer
- // and appends each payload to a new temp file. It does not retain full strings in memory.
- func NewFileStringTableReaderFrom(buffer *util.Buffer, dir string, memoMaxBytes int64) StringTableReader {
- // helper func to cast a string in-place to a byte slice.
- // NOTE: Return value is READ-ONLY. DO NOT MODIFY!
- byteSliceFor := func(s string) []byte {
- return unsafe.Slice(unsafe.StringData(s), len(s))
- }
- err := os.MkdirAll(dir, 0755)
- if err != nil {
- panic(fmt.Errorf("%s: failed to create string table directory: %w", GeneratorPackageName, err))
- }
- f, err := os.CreateTemp(dir, fmt.Sprintf("%s-bgst-*", GeneratorPackageName))
- if err != nil {
- panic(fmt.Errorf("%s: failed to create string table file: %w", GeneratorPackageName, err))
- }
- var writeErr error
- defer func() {
- if writeErr != nil {
- _ = f.Close()
- }
- }()
- // table length
- tl := buffer.ReadInt()
- var refs []fileStringRef
- if tl > 0 {
- refs = make([]fileStringRef, tl)
- for i := range tl {
- payload := byteSliceFor(buffer.ReadString())
- var off int64
- if len(payload) > 0 {
- off, err = f.Seek(0, io.SeekEnd)
- if err != nil {
- writeErr = fmt.Errorf("%s: failed to seek string table file: %w", GeneratorPackageName, err)
- panic(writeErr)
- }
- if _, err := f.Write(payload); err != nil {
- writeErr = fmt.Errorf("%s: failed to write string table entry %d: %w", GeneratorPackageName, i, err)
- panic(writeErr)
- }
- }
- refs[i] = fileStringRef{
- off: off,
- length: len(payload),
- }
- }
- }
- var memo []string
- // Pre-load cache with strings up to memoMaxBytes, respecting string boundaries
- if memoMaxBytes > 0 && len(refs) > 0 {
- memo = make([]string, len(refs))
- var cumulativeSize int64
- for i, ref := range refs {
- // Check if adding this string would exceed the limit
- if cumulativeSize+int64(ref.length)+StringHeaderSize > memoMaxBytes {
- // Would exceed limit, stop here
- break
- }
- // Read string from file and cache it
- if ref.length > 0 {
- b := make([]byte, ref.length)
- _, err := f.ReadAt(b, ref.off)
- if err != nil {
- // If we can't read, skip this entry but continue
- continue
- }
- // Cast the allocated bytes to a string in-place
- str := unsafe.String(unsafe.SliceData(b), len(b))
- memo[i] = str
- cumulativeSize += int64(ref.length) + StringHeaderSize
- }
- }
- }
- return &FileStringTableReader{
- f: f,
- refs: refs,
- memo: memo,
- }
- }
- // At returns the string from the internal file using the reference's offset and length.
- func (fstr *FileStringTableReader) At(index int) string {
- if fstr == nil || fstr.f == nil {
- panic(fmt.Errorf("%s: failed to read file string table data", GeneratorPackageName))
- }
- if index < 0 || index >= len(fstr.refs) {
- panic(fmt.Errorf("%s: string table index out of bounds: %d", GeneratorPackageName, index))
- }
- ref := fstr.refs[index]
- if ref.length == 0 {
- return ""
- }
- // Check cache first
- if fstr.memo != nil && len(fstr.memo) > index && fstr.memo[index] != "" {
- return fstr.memo[index]
- }
- // Cache miss - read from file
- b := make([]byte, ref.length)
- _, err := fstr.f.ReadAt(b, ref.off)
- if err != nil {
- return ""
- }
- // Cast the allocated bytes to a string in-place, as we were the ones that allocated the bytes
- return unsafe.String(unsafe.SliceData(b), len(b))
- }
- // Len returns the total number of strings loaded in the string table.
- func (fstr *FileStringTableReader) Len() int {
- if fstr == nil {
- return 0
- }
- return len(fstr.refs)
- }
- // Close for the file string table reader closes the file and deletes it.
- func (fstr *FileStringTableReader) Close() error {
- if fstr == nil || fstr.f == nil {
- return nil
- }
- path := fstr.f.Name()
- err := fstr.f.Close()
- fstr.f = nil
- fstr.refs = nil
- fstr.memo = nil
- if path != "" {
- _ = os.Remove(path)
- }
- return err
- }
- //--------------------------------------------------------------------------
- // Codec Context
- //--------------------------------------------------------------------------
- // EncodingContext is a context object passed to the encoders to ensure reuse of buffer
- // and table data
- type EncodingContext struct {
- Buffer *util.Buffer
- Table StringTableWriter
- }
- // NewEncodingContext creates a new EncodingContext instance that will create a new []byte buffer
- // for writing, and return the context
- func NewEncodingContext(tableWriter StringTableWriter) *EncodingContext {
- return &EncodingContext{
- Buffer: util.NewBuffer(),
- Table: tableWriter,
- }
- }
- // NewEncodingContextFromWriter creates a new EncodingContext instance that will create a new Buffer
- // from the provided io.Writer and StringTableWriter.
- func NewEncodingContextFromWriter(writer io.Writer, tableWriter StringTableWriter) *EncodingContext {
- return &EncodingContext{
- Buffer: util.NewBufferFromWriter(writer),
- Table: tableWriter,
- }
- }
- // NewEncodingContextFromBuffer creates a new EncodingContext instance that will leverage an existing
- // Buffer and StringTableWriter.
- func NewEncodingContextFromBuffer(buffer *util.Buffer, tableWriter StringTableWriter) *EncodingContext {
- return &EncodingContext{
- Buffer: buffer,
- Table: tableWriter,
- }
- }
- // ToBytes returns the encoded string table bytes (if applicable) combined with the encoded buffer bytes. If
- // a string table is being used, the string table bytes will be written first to ensure correct ordering for
- // decoding.
- func (ec *EncodingContext) ToBytes() []byte {
- encBytes := ec.Buffer.Bytes()
- if ec.Table != nil {
- buff := util.NewBuffer()
- ec.Table.WriteTo(buff)
- buff.WriteBytes(encBytes)
- return buff.Bytes()
- }
- return encBytes
- }
- // IsStringTable returns true if the table is available
- func (ec *EncodingContext) IsStringTable() bool {
- return ec.Table != nil
- }
- // DecodingContext is a context object passed to the decoders to ensure parent objects
- // reuse as much data as possible
- type DecodingContext struct {
- Buffer *util.Buffer
- Table StringTableReader
- }
- // NewDecodingContextFromBytes creates a new DecodingContext instance using an byte slice
- func NewDecodingContextFromBytes(data []byte) *DecodingContext {
- var table StringTableReader
- buff := util.NewBufferFromBytes(data)
- // string table header validation
- if isBinaryTag(data, BinaryTagStringTable) {
- buff.ReadBytes(len(BinaryTagStringTable)) // strip tag length
- // always use a slice string table with a byte array since the
- // data is already in memory
- table = NewSliceStringTableReaderFrom(buff)
- }
- return &DecodingContext{
- Buffer: buff,
- Table: table,
- }
- }
- // NewDecodingContextFromReader creates a new DecodingContext instance using an io.Reader
- // implementation
- func NewDecodingContextFromReader(reader io.Reader) *DecodingContext {
- var table StringTableReader
- buff := util.NewBufferFromReader(reader)
- if isReaderBinaryTag(buff, BinaryTagStringTable) {
- buff.ReadBytes(len(BinaryTagStringTable)) // strip tag length
- // create correct string table implementation
- if IsBingenFileBackedStringTableEnabled() {
- table = NewFileStringTableReaderFrom(buff, BingenFileBackedStringTableDir(), BingenFileBackedStringTableMemoMaxBytes())
- } else {
- table = NewSliceStringTableReaderFrom(buff)
- }
- }
- return &DecodingContext{
- Buffer: buff,
- Table: table,
- }
- }
- // IsStringTable returns true if the table is available
- func (dc *DecodingContext) IsStringTable() bool {
- return dc.Table != nil && dc.Table.Len() > 0
- }
- // Close will ensure that any string table resources and buffer resources are
- // cleaned up.
- func (dc *DecodingContext) Close() {
- if dc.Table != nil {
- _ = dc.Table.Close()
- dc.Table = nil
- }
- }
- //--------------------------------------------------------------------------
- // Binary Codec
- //--------------------------------------------------------------------------
- // BinEncoder is an encoding interface which defines a context based marshal contract.
- type BinEncoder interface {
- MarshalBinaryWithContext(*EncodingContext) error
- }
- // BinDecoder is a decoding interface which defines a context based unmarshal contract.
- type BinDecoder interface {
- UnmarshalBinaryWithContext(*DecodingContext) error
- }
- //--------------------------------------------------------------------------
- // Update
- //--------------------------------------------------------------------------
- // MarshalBinary serializes the internal properties of this Update instance
- // into a byte array
- func (target *Update) MarshalBinary() (data []byte, err error) {
- ctx := NewEncodingContext(nil)
- e := target.MarshalBinaryWithContext(ctx)
- if e != nil {
- return nil, e
- }
- return ctx.ToBytes(), nil
- }
- // MarshalBinary serializes the internal properties of this Update instance
- // into an io.Writer.
- func (target *Update) MarshalBinaryTo(writer io.Writer) error {
- buff := util.NewBufferFromWriter(writer)
- defer buff.Flush()
- ctx := NewEncodingContextFromBuffer(buff, nil)
- return target.MarshalBinaryWithContext(ctx)
- }
- // MarshalBinaryWithContext serializes the internal properties of this Update instance
- // into a byte array leveraging a predefined context.
- func (target *Update) MarshalBinaryWithContext(ctx *EncodingContext) (err error) {
- // panics are recovered and propagated as errors
- defer func() {
- if r := recover(); r != nil {
- if e, ok := r.(error); ok {
- err = e
- } else if s, ok := r.(string); ok {
- err = fmt.Errorf("unexpected panic: %s", s)
- } else {
- err = fmt.Errorf("unexpected panic: %+v", r)
- }
- }
- }()
- buff := ctx.Buffer
- buff.WriteUInt8(DefaultCodecVersion) // version
- if ctx.IsStringTable() {
- a := ctx.Table.AddOrGet(target.Name)
- buff.WriteInt(a) // write table index
- } else {
- buff.WriteString(target.Name) // write string
- }
- if target.Labels == nil {
- buff.WriteUInt8(uint8(0)) // write nil byte
- } else {
- buff.WriteUInt8(uint8(1)) // write non-nil byte
- // --- [begin][write][map](map[string]string) ---
- buff.WriteInt(len(target.Labels)) // map length
- for v, z := range target.Labels {
- if ctx.IsStringTable() {
- b := ctx.Table.AddOrGet(v)
- buff.WriteInt(b) // write table index
- } else {
- buff.WriteString(v) // write string
- }
- if ctx.IsStringTable() {
- c := ctx.Table.AddOrGet(z)
- buff.WriteInt(c) // write table index
- } else {
- buff.WriteString(z) // write string
- }
- }
- // --- [end][write][map](map[string]string) ---
- }
- buff.WriteFloat64(target.Value) // write float64
- if target.AdditionalInfo == nil {
- buff.WriteUInt8(uint8(0)) // write nil byte
- } else {
- buff.WriteUInt8(uint8(1)) // write non-nil byte
- // --- [begin][write][map](map[string]string) ---
- buff.WriteInt(len(target.AdditionalInfo)) // map length
- for vv, zz := range target.AdditionalInfo {
- if ctx.IsStringTable() {
- d := ctx.Table.AddOrGet(vv)
- buff.WriteInt(d) // write table index
- } else {
- buff.WriteString(vv) // write string
- }
- if ctx.IsStringTable() {
- e := ctx.Table.AddOrGet(zz)
- buff.WriteInt(e) // write table index
- } else {
- buff.WriteString(zz) // write string
- }
- }
- // --- [end][write][map](map[string]string) ---
- }
- return nil
- }
- // UnmarshalBinary uses the data passed byte array to set all the internal properties of
- // the Update type
- func (target *Update) UnmarshalBinary(data []byte) error {
- ctx := NewDecodingContextFromBytes(data)
- defer ctx.Close()
- err := target.UnmarshalBinaryWithContext(ctx)
- if err != nil {
- return err
- }
- return nil
- }
- // UnmarshalBinaryFromReader uses the io.Reader data to set all the internal properties of
- // the Update type
- func (target *Update) UnmarshalBinaryFromReader(reader io.Reader) error {
- ctx := NewDecodingContextFromReader(reader)
- defer ctx.Close()
- err := target.UnmarshalBinaryWithContext(ctx)
- if err != nil {
- return err
- }
- return nil
- }
- // UnmarshalBinaryWithContext uses the context containing a string table and binary buffer to set all the internal properties of
- // the Update type
- func (target *Update) UnmarshalBinaryWithContext(ctx *DecodingContext) (err error) {
- // panics are recovered and propagated as errors
- defer func() {
- if r := recover(); r != nil {
- if e, ok := r.(error); ok {
- err = e
- } else if s, ok := r.(string); ok {
- err = fmt.Errorf("unexpected panic: %s", s)
- } else {
- err = fmt.Errorf("unexpected panic: %+v", r)
- }
- }
- }()
- buff := ctx.Buffer
- version := buff.ReadUInt8()
- if version > DefaultCodecVersion {
- return fmt.Errorf("Invalid Version Unmarshalling Update. Expected %d or less, got %d", DefaultCodecVersion, version)
- }
- var b string
- if ctx.IsStringTable() {
- c := buff.ReadInt() // read string index
- b = ctx.Table.At(c)
- } else {
- b = buff.ReadString() // read string
- }
- a := b
- target.Name = a
- if buff.ReadUInt8() == uint8(0) {
- target.Labels = nil
- } else {
- // --- [begin][read][map](map[string]string) ---
- e := buff.ReadInt() // map len
- d := make(map[string]string, e)
- for range e {
- var v string
- var g string
- if ctx.IsStringTable() {
- h := buff.ReadInt() // read string index
- g = ctx.Table.At(h)
- } else {
- g = buff.ReadString() // read string
- }
- f := g
- v = f
- var z string
- var m string
- if ctx.IsStringTable() {
- n := buff.ReadInt() // read string index
- m = ctx.Table.At(n)
- } else {
- m = buff.ReadString() // read string
- }
- l := m
- z = l
- d[v] = z
- }
- target.Labels = d
- // --- [end][read][map](map[string]string) ---
- }
- o := buff.ReadFloat64() // read float64
- target.Value = o
- if buff.ReadUInt8() == uint8(0) {
- target.AdditionalInfo = nil
- } else {
- // --- [begin][read][map](map[string]string) ---
- q := buff.ReadInt() // map len
- p := make(map[string]string, q)
- for range q {
- var vv string
- var s string
- if ctx.IsStringTable() {
- t := buff.ReadInt() // read string index
- s = ctx.Table.At(t)
- } else {
- s = buff.ReadString() // read string
- }
- r := s
- vv = r
- var zz string
- var w string
- if ctx.IsStringTable() {
- x := buff.ReadInt() // read string index
- w = ctx.Table.At(x)
- } else {
- w = buff.ReadString() // read string
- }
- u := w
- zz = u
- p[vv] = zz
- }
- target.AdditionalInfo = p
- // --- [end][read][map](map[string]string) ---
- }
- return nil
- }
- //--------------------------------------------------------------------------
- // UpdateSet
- //--------------------------------------------------------------------------
- // MarshalBinary serializes the internal properties of this UpdateSet instance
- // into a byte array
- func (target *UpdateSet) MarshalBinary() (data []byte, err error) {
- ctx := NewEncodingContext(NewIndexedStringTableWriter())
- e := target.MarshalBinaryWithContext(ctx)
- if e != nil {
- return nil, e
- }
- return ctx.ToBytes(), nil
- }
- // MarshalBinary serializes the internal properties of this UpdateSet instance
- // into an io.Writer.
- func (target *UpdateSet) MarshalBinaryTo(writer io.Writer) error {
- buff := util.NewBufferFromWriter(writer)
- defer buff.Flush()
- // run a pre-pass to collect all strings into the string table and discard all writes to the main
- // buffer. Then, we write the string table, sorted by number of repeated uses (descending), to the
- // main buffer, and use the resulting table as part of the context for the main pass.
- prepass := NewPrepassStringTableWriter()
- prepassCtx := NewEncodingContextFromWriter(io.Discard, prepass)
- e := target.MarshalBinaryWithContext(prepassCtx)
- if e != nil {
- return e
- }
- tableWriter := prepass.WriteSortedTo(buff)
- ctx := NewEncodingContextFromBuffer(buff, tableWriter)
- return target.MarshalBinaryWithContext(ctx)
- }
- // MarshalBinaryWithContext serializes the internal properties of this UpdateSet instance
- // into a byte array leveraging a predefined context.
- func (target *UpdateSet) MarshalBinaryWithContext(ctx *EncodingContext) (err error) {
- // panics are recovered and propagated as errors
- defer func() {
- if r := recover(); r != nil {
- if e, ok := r.(error); ok {
- err = e
- } else if s, ok := r.(string); ok {
- err = fmt.Errorf("unexpected panic: %s", s)
- } else {
- err = fmt.Errorf("unexpected panic: %+v", r)
- }
- }
- }()
- buff := ctx.Buffer
- buff.WriteUInt8(DefaultCodecVersion) // version
- // --- [begin][write][reference](time.Time) ---
- a, errA := target.Timestamp.MarshalBinary()
- if errA != nil {
- return errA
- }
- buff.WriteInt(len(a))
- buff.WriteBytes(a)
- // --- [end][write][reference](time.Time) ---
- if target.Updates == nil {
- buff.WriteUInt8(uint8(0)) // write nil byte
- } else {
- buff.WriteUInt8(uint8(1)) // write non-nil byte
- // --- [begin][write][slice]([]Update) ---
- buff.WriteInt(len(target.Updates)) // slice length
- for i := range target.Updates {
- // --- [begin][write][struct](Update) ---
- buff.WriteInt(0) // [compatibility, unused]
- errB := target.Updates[i].MarshalBinaryWithContext(ctx)
- if errB != nil {
- return errB
- }
- // --- [end][write][struct](Update) ---
- }
- // --- [end][write][slice]([]Update) ---
- }
- return nil
- }
- // UnmarshalBinary uses the data passed byte array to set all the internal properties of
- // the UpdateSet type
- func (target *UpdateSet) UnmarshalBinary(data []byte) error {
- ctx := NewDecodingContextFromBytes(data)
- defer ctx.Close()
- err := target.UnmarshalBinaryWithContext(ctx)
- if err != nil {
- return err
- }
- return nil
- }
- // UnmarshalBinaryFromReader uses the io.Reader data to set all the internal properties of
- // the UpdateSet type
- func (target *UpdateSet) UnmarshalBinaryFromReader(reader io.Reader) error {
- ctx := NewDecodingContextFromReader(reader)
- defer ctx.Close()
- err := target.UnmarshalBinaryWithContext(ctx)
- if err != nil {
- return err
- }
- return nil
- }
- // UnmarshalBinaryWithContext uses the context containing a string table and binary buffer to set all the internal properties of
- // the UpdateSet type
- func (target *UpdateSet) UnmarshalBinaryWithContext(ctx *DecodingContext) (err error) {
- // panics are recovered and propagated as errors
- defer func() {
- if r := recover(); r != nil {
- if e, ok := r.(error); ok {
- err = e
- } else if s, ok := r.(string); ok {
- err = fmt.Errorf("unexpected panic: %s", s)
- } else {
- err = fmt.Errorf("unexpected panic: %+v", r)
- }
- }
- }()
- buff := ctx.Buffer
- version := buff.ReadUInt8()
- if version > DefaultCodecVersion {
- return fmt.Errorf("Invalid Version Unmarshalling UpdateSet. Expected %d or less, got %d", DefaultCodecVersion, version)
- }
- // --- [begin][read][reference](time.Time) ---
- a := new(time.Time)
- b := buff.ReadInt() // byte array length
- c := buff.ReadBytes(b)
- errA := a.UnmarshalBinary(c)
- if errA != nil {
- return errA
- }
- target.Timestamp = *a
- // --- [end][read][reference](time.Time) ---
- if buff.ReadUInt8() == uint8(0) {
- target.Updates = nil
- } else {
- // --- [begin][read][slice]([]Update) ---
- e := buff.ReadInt() // slice len
- d := make([]Update, e)
- for i := range e {
- // --- [begin][read][struct](Update) ---
- g := new(Update)
- buff.ReadInt() // [compatibility, unused]
- errB := g.UnmarshalBinaryWithContext(ctx)
- if errB != nil {
- return errB
- }
- f := *g
- // --- [end][read][struct](Update) ---
- d[i] = f
- }
- target.Updates = d
- // --- [end][read][slice]([]Update) ---
- }
- return nil
- }
- //--------------------------------------------------------------------------
- // UpdateSetStream
- //--------------------------------------------------------------------------
- // UpdateSetStream is a single use field stream for the contents of an UpdateSet instance. Instead of creating an instance and populating
- // the fields on that instance, we provide a streaming iterator which yields (BingenFieldInfo, *BingenValue) tuples for each
- // streamable element. All slices and maps will be flattened one depth and each element streamed individually.
- type UpdateSetStream struct {
- reader io.Reader
- ctx *DecodingContext
- err error
- }
- // Closes closes the internal io.Reader used to read and parse the UpdateSet fields.
- // This should be called once the stream is no longer needed.
- func (stream *UpdateSetStream) Close() {
- if closer, ok := stream.reader.(io.Closer); ok {
- closer.Close()
- }
- stream.ctx.Close()
- }
- // Error returns an error if one occurred during the process of streaming the UpdateSet
- // This can be checked after iterating through the Stream().
- func (stream *UpdateSetStream) Error() error {
- return stream.err
- }
- // NewUpdateSetStream creates a new UpdateSetStream, which uses the io.Reader data to stream all internal fields of an UpdateSet instance
- func NewUpdateSetStream(reader io.Reader) BingenStream {
- ctx := NewDecodingContextFromReader(reader)
- return &UpdateSetStream{
- ctx: ctx,
- reader: reader,
- }
- }
- // Stream returns the iterator which will stream each field of the target type.
- func (stream *UpdateSetStream) Stream() iter.Seq2[BingenFieldInfo, *BingenValue] {
- return func(yield func(BingenFieldInfo, *BingenValue) bool) {
- var fi BingenFieldInfo
- ctx := stream.ctx
- buff := ctx.Buffer
- version := buff.ReadUInt8()
- if version > DefaultCodecVersion {
- stream.err = fmt.Errorf("Invalid Version Unmarshalling UpdateSet. Expected %d or less, got %d", DefaultCodecVersion, version)
- return
- }
- fi = BingenFieldInfo{
- Type: reflect.TypeFor[time.Time](),
- Name: "Timestamp",
- }
- // --- [begin][read][reference](time.Time) ---
- b := new(time.Time)
- c := buff.ReadInt() // byte array length
- d := buff.ReadBytes(c)
- errA := b.UnmarshalBinary(d)
- if errA != nil {
- stream.err = errA
- return
- }
- a := *b
- // --- [end][read][reference](time.Time) ---
- if !yield(fi, singleV(a)) {
- return
- }
- fi = BingenFieldInfo{
- Type: reflect.TypeFor[[]Update](),
- Name: "Updates",
- }
- if buff.ReadUInt8() == uint8(0) {
- if !yield(fi, nil) {
- return
- }
- } else {
- // --- [begin][read][streaming-slice]([]Update) ---
- e := buff.ReadInt() // slice len
- for i := range e {
- // --- [begin][read][struct](Update) ---
- g := new(Update)
- buff.ReadInt() // [compatibility, unused]
- errB := g.UnmarshalBinaryWithContext(ctx)
- if errB != nil {
- stream.err = errB
- return
- }
- f := *g
- // --- [end][read][struct](Update) ---
- if !yield(fi, pairV(i, f)) {
- return
- }
- }
- // --- [end][read][streaming-slice]([]Update) ---
- }
- }
- }
|