go_wireproto/funcs_fieldvaluepair.go
brent saner d4bb259b83
v1.0.0
Initial release.
2024-07-10 00:18:54 -04:00

269 lines
5.8 KiB
Go

package wireproto
import (
`bytes`
`fmt`
`strings`
`github.com/google/uuid`
)
// ConnId returns a copy of the connection ID.
func (f *FieldValuePair) ConnId() (conn uuid.UUID) {
if f.connId == nil {
return
}
conn = *f.connId
return
}
// GetParent returns the parent Record, if set.
func (f *FieldValuePair) GetParent() (rec Record) {
rec = f.parent
return
}
// MarshalBinary renders a FieldValuePair into a byte-packed format.
func (f *FieldValuePair) MarshalBinary() (data []byte, err error) {
var b []byte
var buf *bytes.Buffer = new(bytes.Buffer)
_ = f.Size()
if _, err = buf.Write(PackInt(f.Name.Size())); err != nil {
return
}
if _, err = buf.Write(PackInt(f.Value.Size())); err != nil {
return
}
if b, err = f.Name.MarshalBinary(); err != nil {
return
}
buf.Write(b)
if b, err = f.Value.MarshalBinary(); err != nil {
return
}
buf.Write(b)
data = buf.Bytes()
return
}
// Model returns an indented string representation of the model.
func (f *FieldValuePair) Model() (out string) {
out = f.ModelCustom(IndentChars, SeparatorChars, indentKvp)
return
}
// ModelCustom is like Model with user-defined formatting.
func (f *FieldValuePair) ModelCustom(indent, sep string, level uint) (out string) {
var fnSize int
var fvSize int
var splitFn [][]byte // for multi-chunk hex
var splitFv [][]byte // """
var sb strings.Builder
var maxLen int = 8 // len of size uint32 hex
// This one is the biggest pain because it has to handle arbitrary lengths.
fnSize = f.Name.Size() * 2 // 2x because Hex string
fvSize = f.Value.Size() * 2 // """
if fnSize > maxLen {
if f.Name.Size() <= maxByteLine {
maxLen = fnSize
} else {
maxLen = maxByteLine * 2
}
}
if fvSize > maxLen {
if f.Value.Size() <= maxByteLine {
maxLen = fvSize
} else {
maxLen = maxByteLine * 2
}
}
splitFn = chunkByteLine(f.Name)
splitFv = chunkByteLine(f.Value)
// SIZES
// Field Name
// e.g. "\t\t\t00000003 // Field Name Size (3)"
sb.WriteString(strings.Repeat(indent, int(level))) // \t\t\t
sb.WriteString(padIntRight(f.Name.Size(), maxLen)) // "00000003 "
sb.WriteString(sep) // " "
sb.WriteString(fmt.Sprintf("// Field Name Size (%d)\n", f.Name.Size())) // "// Field Name Size (3)". The extra space is intentional.
// Field Value
// e.g. "\t\t\t00000018 // Field Value Size (24)"
sb.WriteString(strings.Repeat(indent, int(level))) // \t\t\t
sb.WriteString(padIntRight(f.Value.Size(), maxLen)) // "000000018 "
sb.WriteString(sep) // " "
sb.WriteString(fmt.Sprintf("// Field Value Size (%d)\n", f.Value.Size())) // "// Field Value Size (24)".
// VALUES
// Name
// e.g. `\t\t\t666f6f // "foo"`
for idx, chunk := range splitFn {
sb.WriteString(strings.Repeat(indent, int(level))) // "\t\t\t"
if idx == 0 {
sb.WriteString(padBytesRight(chunk, maxLen)) // "666f6f "
sb.WriteString(sep) // " "
sb.WriteString(fmt.Sprintf("// \"%s\"", f.Name)) // `// "foo"`
} else {
sb.WriteString(fmt.Sprintf("%x", chunk)) // "666f6f"
}
sb.WriteString("\n")
}
// Value
/*
e.g.:
\t\t\t736f6d65206c6f6e6765722076616c75 // "some longer value string"
\t\t\t6520737472696e67
*/
for idx, chunk := range splitFv {
sb.WriteString(strings.Repeat(indent, int(level))) // "\t\t\t"
if idx == 0 {
sb.WriteString(padBytesRight(chunk, maxLen)) // "736f6d65206c6f6e6765722076616c75"
sb.WriteString(sep) // " "
sb.WriteString(fmt.Sprintf("// \"%s\"", f.Value)) // `// "some longer value string"`
} else {
sb.WriteString(fmt.Sprintf("%x", chunk)) // "6520737472696e67"
}
sb.WriteString("\n")
}
out = sb.String()
return
}
/*
ToMap returns a map of map[name]value.
While an interface{} in the map, the value is actually a FieldValue.
*/
func (f *FieldValuePair) ToMap() (m map[string]interface{}) {
m = map[string]interface{}{
f.Name.String(): f.Value,
}
return
}
// UnmarshalBinary populates a FieldValuePair from packed bytes.
func (f *FieldValuePair) UnmarshalBinary(data []byte) (err error) {
if data == nil || len(data) == 0 {
return
}
var b []byte
var nmSize, valSize int
var buf *bytes.Reader = bytes.NewReader(data)
if f == nil {
*f = FieldValuePair{}
}
f.common = &common{}
f.size = 0
// Get the name/value sizes.
b = make([]byte, PackedNumSize)
if _, err = buf.Read(b); err != nil {
return
}
nmSize = UnpackInt(b)
b = make([]byte, PackedNumSize)
if _, err = buf.Read(b); err != nil {
return
}
valSize = UnpackInt(b)
// Get the name.
if nmSize != 0 {
b = make([]byte, nmSize)
if _, err = buf.Read(b); err != nil {
return
}
if err = f.Name.UnmarshalBinary(b); err != nil {
return
}
} else {
f.Name = nil
}
// Get the value.
if valSize != 0 {
b = make([]byte, valSize)
if _, err = buf.Read(b); err != nil {
return
}
if err = f.Value.UnmarshalBinary(b); err != nil {
return
}
} else {
f.Value = nil
}
_ = f.Size()
return
}
// Size returns the FieldValuePair's calculated size (in bytes) and updates the size field.
func (f *FieldValuePair) Size() (size int) {
if f == nil {
return
}
// Count and Size uint32s
size += PackedNumSize * 2
size += f.Name.Size()
size += f.Value.Size()
if f.common == nil {
f.common = new(common)
}
f.common.size = uint32(size)
return
}
// getIdx returns the FVP index in the parent Record.
func (f *FieldValuePair) getIdx() (idx int) {
var fvps []FVP
idx = -1
if f == nil || f.parent == nil {
return
}
fvps = f.parent.getFvps()
for i, fv := range fvps {
if fv == f {
idx = i
return
}
}
return
}