255 lines
4.8 KiB
Go
255 lines
4.8 KiB
Go
|
package wireproto
|
||
|
|
||
|
import (
|
||
|
`bytes`
|
||
|
`fmt`
|
||
|
`io`
|
||
|
`strings`
|
||
|
|
||
|
`github.com/google/uuid`
|
||
|
)
|
||
|
|
||
|
// ConnId returns a copy of the connection ID.
|
||
|
func (r *RequestRecord) ConnId() (conn uuid.UUID) {
|
||
|
|
||
|
conn = r.connId
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// GetParent returns the parent RecordGroup.
|
||
|
func (r *RequestRecord) GetParent() (rg RecordGroup) {
|
||
|
|
||
|
rg = r.parent
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// MarshalBinary renders a RequestRecord into a byte-packed format.
|
||
|
func (r *RequestRecord) MarshalBinary() (data []byte, err error) {
|
||
|
|
||
|
var b []byte
|
||
|
var recSize int
|
||
|
var buf *bytes.Buffer = new(bytes.Buffer)
|
||
|
|
||
|
_ = r.Size()
|
||
|
|
||
|
if _, err = buf.Write(PackInt(len(r.Pairs))); err != nil {
|
||
|
return
|
||
|
}
|
||
|
for _, i := range r.Pairs {
|
||
|
recSize += i.Size()
|
||
|
}
|
||
|
if _, err = buf.Write(PackInt(recSize)); err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
for _, i := range r.Pairs {
|
||
|
if b, err = i.MarshalBinary(); err != nil {
|
||
|
return
|
||
|
}
|
||
|
if _, err = buf.Write(b); err != nil {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
data = buf.Bytes()
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Model returns an indented string representation of the model.
|
||
|
func (r *RequestRecord) Model() (out string) {
|
||
|
|
||
|
out = r.ModelCustom(IndentChars, SeparatorChars, indentRec)
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// ModelCustom is like Model with user-defined formatting.
|
||
|
func (r *RequestRecord) ModelCustom(indent, sep string, level uint) (out string) {
|
||
|
|
||
|
var size int
|
||
|
var sb strings.Builder
|
||
|
|
||
|
for _, p := range r.Pairs {
|
||
|
size += p.Size()
|
||
|
}
|
||
|
|
||
|
// Count
|
||
|
sb.WriteString(strings.Repeat(indent, int(level)))
|
||
|
sb.WriteString(fmt.Sprintf("%x", PackUint32(uint32(len(r.Pairs)))))
|
||
|
sb.WriteString(sep)
|
||
|
sb.WriteString(fmt.Sprintf("// Field/Value Count (%d)\n", len(r.Pairs)))
|
||
|
// Size
|
||
|
sb.WriteString(strings.Repeat(indent, int(level)))
|
||
|
sb.WriteString(fmt.Sprintf("%x", PackUint32(uint32(size))))
|
||
|
sb.WriteString(sep)
|
||
|
sb.WriteString(fmt.Sprintf("// Record Size (%d)\n", size))
|
||
|
|
||
|
// VALUES
|
||
|
for idx, p := range r.Pairs {
|
||
|
sb.WriteString(strings.Repeat(indent, int(level)))
|
||
|
sb.WriteString(
|
||
|
fmt.Sprintf(
|
||
|
"// Record Group %d, Record %d, Field/Value %d (%d bytes)\n",
|
||
|
r.rgIdx+1, r.rIdx+1, idx+1, p.Size(),
|
||
|
),
|
||
|
)
|
||
|
sb.WriteString(p.ModelCustom(indent, sep, level+1))
|
||
|
}
|
||
|
|
||
|
out = sb.String()
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Resolve associates children with parents.
|
||
|
func (r *RequestRecord) Resolve() {
|
||
|
for idx, i := range r.Pairs {
|
||
|
i.parent = r
|
||
|
i.rgIdx = r.rgIdx
|
||
|
i.rIdx = idx
|
||
|
i.fvpidx = idx
|
||
|
// KVP have no Resolve() method.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Size returns the RequestRecord's calculated size (in bytes) and updates the size field if 0.
|
||
|
func (r *RequestRecord) Size() (size int) {
|
||
|
|
||
|
if r == nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Count and Size uint32s
|
||
|
size += PackedNumSize * 2
|
||
|
|
||
|
for _, i := range r.Pairs {
|
||
|
size += i.Size()
|
||
|
}
|
||
|
|
||
|
if r.common == nil || r.size == 0 {
|
||
|
r.common = &common{
|
||
|
size: uint32(size),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
r.size = uint32(size)
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// ToMap returns a slice of FVP maps for this Record.
|
||
|
func (r *RequestRecord) ToMap() (m []map[string]interface{}) {
|
||
|
|
||
|
m = make([]map[string]interface{}, len(r.Pairs))
|
||
|
for idx, p := range r.Pairs {
|
||
|
m[idx] = p.ToMap()
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// UnmarshalBinary populates a RequestRecord from packed bytes.
|
||
|
func (r *RequestRecord) UnmarshalBinary(data []byte) (err error) {
|
||
|
|
||
|
if data == nil || len(data) == 0 {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
var b []byte
|
||
|
var cnt, size int
|
||
|
var fvpNmSize, fvpValSize int
|
||
|
var fvpBuf *bytes.Buffer
|
||
|
var buf *bytes.Reader = bytes.NewReader(data)
|
||
|
|
||
|
if r == nil {
|
||
|
*r = RequestRecord{}
|
||
|
}
|
||
|
if r.common == nil {
|
||
|
r.common = new(common)
|
||
|
}
|
||
|
r.size = 0
|
||
|
|
||
|
// FVP count.
|
||
|
b = make([]byte, PackedNumSize)
|
||
|
if _, err = buf.Read(b); err != nil {
|
||
|
return
|
||
|
}
|
||
|
cnt = UnpackInt(b)
|
||
|
|
||
|
// Size of record.
|
||
|
b = make([]byte, PackedNumSize)
|
||
|
if _, err = buf.Read(b); err != nil {
|
||
|
return
|
||
|
}
|
||
|
size = UnpackInt(b)
|
||
|
|
||
|
// And now we handle the FVPs themselves.
|
||
|
b = make([]byte, size)
|
||
|
if _, err = buf.Read(b); err != nil {
|
||
|
return
|
||
|
}
|
||
|
buf = bytes.NewReader(b)
|
||
|
|
||
|
r.Pairs = make([]*FieldValuePair, cnt)
|
||
|
|
||
|
for idx := 0; idx < cnt; idx++ {
|
||
|
fvpBuf = new(bytes.Buffer)
|
||
|
|
||
|
// Unlike parents, the FVP needs both allocators because they're both size allocators.
|
||
|
// FVP Name
|
||
|
b = make([]byte, PackedNumSize)
|
||
|
if _, err = buf.Read(b); err != nil {
|
||
|
return
|
||
|
}
|
||
|
if _, err = fvpBuf.Write(b); err != nil {
|
||
|
return
|
||
|
}
|
||
|
fvpNmSize = UnpackInt(b)
|
||
|
// FVP Value
|
||
|
b = make([]byte, PackedNumSize)
|
||
|
if _, err = buf.Read(b); err != nil {
|
||
|
return
|
||
|
}
|
||
|
if _, err = fvpBuf.Write(b); err != nil {
|
||
|
return
|
||
|
}
|
||
|
fvpValSize = UnpackInt(b)
|
||
|
|
||
|
if _, err = io.CopyN(fvpBuf, buf, int64(fvpNmSize+fvpValSize)); err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
r.Pairs[idx] = new(FieldValuePair)
|
||
|
if err = r.Pairs[idx].UnmarshalBinary(fvpBuf.Bytes()); err != nil {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_ = r.Size()
|
||
|
_ = size
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// getFvps returns this Record's FVP.
|
||
|
func (r *RequestRecord) getFvps() (fvp []FVP) {
|
||
|
|
||
|
fvp = make([]FVP, len(r.Pairs))
|
||
|
for idx, p := range r.Pairs {
|
||
|
fvp[idx] = p
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// getIdx returns the Record index in the parent RecordGroup.
|
||
|
func (r *RequestRecord) getIdx() (idx int) {
|
||
|
|
||
|
idx = r.rIdx
|
||
|
|
||
|
return
|
||
|
}
|