v1.0.0
Initial release.
This commit is contained in:
421
funcs_request.go
Normal file
421
funcs_request.go
Normal file
@@ -0,0 +1,421 @@
|
||||
package wireproto
|
||||
|
||||
import (
|
||||
`bytes`
|
||||
`cmp`
|
||||
`fmt`
|
||||
`hash/crc32`
|
||||
`io`
|
||||
`strings`
|
||||
|
||||
`github.com/google/uuid`
|
||||
)
|
||||
|
||||
// ConnId returns a copy of the connection ID.
|
||||
func (r *Request) ConnId() (conn uuid.UUID) {
|
||||
|
||||
conn = r.connId
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalBinary renders a Request into a byte-packed format.
|
||||
func (r *Request) MarshalBinary() (data []byte, err error) {
|
||||
|
||||
var b []byte
|
||||
var msgSize int
|
||||
var cksum uint32
|
||||
var buf *bytes.Buffer = new(bytes.Buffer)
|
||||
var msgBuf *bytes.Buffer = new(bytes.Buffer)
|
||||
|
||||
_ = r.Size()
|
||||
|
||||
for _, i := range r.RecordGroups {
|
||||
msgSize += i.Size()
|
||||
}
|
||||
|
||||
// The message "body" - we do this first so we can checksum (if not nil).
|
||||
if _, err = msgBuf.Write(hdrBODYSTART); err != nil {
|
||||
return
|
||||
}
|
||||
// Record group count
|
||||
if _, err = msgBuf.Write(PackInt(len(r.RecordGroups))); err != nil {
|
||||
return
|
||||
}
|
||||
// And size.
|
||||
if _, err = msgBuf.Write(PackInt(msgSize)); err != nil {
|
||||
return
|
||||
}
|
||||
for _, i := range r.RecordGroups {
|
||||
if b, err = i.MarshalBinary(); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = msgBuf.Write(b); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if _, err = msgBuf.Write(hdrBODYEND); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Now the surrounding request.
|
||||
|
||||
// Checksum - update and serialize if not null.
|
||||
if r.Checksum != nil {
|
||||
cksum = crc32.ChecksumIEEE(msgBuf.Bytes())
|
||||
*r.Checksum = cksum
|
||||
if _, err = buf.Write(hdrCKSUM); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = buf.Write(cksumBytes(*r.Checksum)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Message start
|
||||
if _, err = buf.Write(hdrMSGSTART); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Protocol version
|
||||
if _, err = buf.Write(PackUint32(r.ProtocolVersion)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Then copy the msgBuf in.
|
||||
if _, err = msgBuf.WriteTo(buf); err != nil {
|
||||
return
|
||||
}
|
||||
// And then the message end.
|
||||
if _, err = buf.Write(hdrMSGEND); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data = buf.Bytes()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Model returns an indented string representation of the model.
|
||||
func (r *Request) Model() (out string) {
|
||||
|
||||
out = r.ModelCustom(IndentChars, SeparatorChars, indentR)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Request) ModelCustom(indent, sep string, level uint) (out string) {
|
||||
|
||||
var maxFtr int
|
||||
var size int
|
||||
var sb strings.Builder
|
||||
|
||||
for _, rg := range r.RecordGroups {
|
||||
size += rg.Size()
|
||||
}
|
||||
|
||||
// Checksum (optional for Request)
|
||||
sb.WriteString(strings.Repeat(indent, int(level)))
|
||||
if r.Checksum == nil {
|
||||
sb.WriteString(strings.Repeat("-", 8))
|
||||
sb.WriteString(sep)
|
||||
sb.WriteString("// (No Checksum Present)\n")
|
||||
} else {
|
||||
// HDR: CKSUM
|
||||
sb.WriteString(padBytesRight(hdrCKSUM, 8))
|
||||
sb.WriteString(sep)
|
||||
sb.WriteString("// HDR:CKSUM\n")
|
||||
// Checksum
|
||||
sb.WriteString(strings.Repeat(indent, int(level)))
|
||||
sb.WriteString(padBytesRight(cksumBytes(*r.Checksum), 8))
|
||||
sb.WriteString(sep)
|
||||
sb.WriteString(fmt.Sprintf("// Checksum Value (%d)\n", *r.Checksum))
|
||||
}
|
||||
|
||||
// Header: MSGSTART
|
||||
sb.WriteString(strings.Repeat(indent, int(level)))
|
||||
sb.WriteString(padBytesRight(hdrMSGSTART, 8))
|
||||
sb.WriteString(sep)
|
||||
sb.WriteString("// HDR:MSGSTART\n")
|
||||
|
||||
// Protocol Version
|
||||
sb.WriteString(strings.Repeat(indent, int(level)))
|
||||
sb.WriteString(padIntRight(int(r.ProtocolVersion), 8))
|
||||
sb.WriteString(sep)
|
||||
sb.WriteString(fmt.Sprintf("// Protocol Version (%d)\n", r.ProtocolVersion))
|
||||
|
||||
// Header: BODYSTART
|
||||
sb.WriteString(strings.Repeat(indent, int(level)))
|
||||
sb.WriteString(padBytesRight(hdrBODYSTART, 8))
|
||||
sb.WriteString(sep)
|
||||
sb.WriteString("// HDR:BODYSTART\n")
|
||||
|
||||
// Count
|
||||
sb.WriteString(strings.Repeat(indent, int(level)))
|
||||
sb.WriteString(padIntRight(len(r.RecordGroups), 8))
|
||||
sb.WriteString(sep)
|
||||
sb.WriteString(fmt.Sprintf("// Record Group Count (%d)\n", len(r.RecordGroups)))
|
||||
// Size
|
||||
sb.WriteString(strings.Repeat(indent, int(level)))
|
||||
sb.WriteString(padIntRight(size, 8))
|
||||
sb.WriteString(sep)
|
||||
sb.WriteString(fmt.Sprintf("// Record Groups Size (%d)\n", size))
|
||||
|
||||
// VALUES
|
||||
for idx, rg := range r.RecordGroups {
|
||||
sb.WriteString(strings.Repeat(indent, int(level)))
|
||||
sb.WriteString(fmt.Sprintf("// Record Group %d (%d bytes)\n", idx+1, rg.Size()))
|
||||
sb.WriteString(rg.ModelCustom(indent, sep, level+1))
|
||||
}
|
||||
|
||||
// Make the footers a little more nicely aligned.
|
||||
switch cmp.Compare(len(hdrBODYEND), len(hdrMSGEND)) {
|
||||
case -1:
|
||||
maxFtr = len(hdrMSGEND)
|
||||
case 1, 0:
|
||||
maxFtr = len(hdrBODYEND)
|
||||
}
|
||||
|
||||
// Footer: BODYEND
|
||||
sb.WriteString(strings.Repeat(indent, int(level)))
|
||||
sb.WriteString(padBytesRight(hdrBODYEND, maxFtr))
|
||||
sb.WriteString(sep)
|
||||
sb.WriteString("// HDR:BODYEND\n")
|
||||
|
||||
// Footer: MSGEND
|
||||
sb.WriteString(strings.Repeat(indent, int(level)))
|
||||
sb.WriteString(padBytesRight(hdrMSGEND, maxFtr))
|
||||
sb.WriteString(sep)
|
||||
sb.WriteString("// HDR:MSGEND\n")
|
||||
|
||||
out = sb.String()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Resolve associates children with parents.
|
||||
func (r *Request) Resolve() {
|
||||
for idx, i := range r.RecordGroups {
|
||||
i.parent = r
|
||||
i.rgIdx = idx
|
||||
i.Resolve()
|
||||
}
|
||||
}
|
||||
|
||||
// SetConnID allows for setting a connection ID. This is largely just used for debugging purposes.
|
||||
func (r *Request) SetConnID(connId uuid.UUID) {
|
||||
|
||||
r.connId = connId
|
||||
|
||||
for _, rg := range r.RecordGroups {
|
||||
rg.connId = connId
|
||||
for _, rec := range rg.Records {
|
||||
rec.connId = connId
|
||||
for _, kvp := range rec.Pairs {
|
||||
kvp.connId = &connId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Size returns the Request's calculated size (in bytes) and updates the size field if 0.
|
||||
func (r *Request) Size() (size int) {
|
||||
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Checksum
|
||||
if r.Checksum != nil {
|
||||
size += len(hdrCKSUM)
|
||||
size += CksumPackedSize
|
||||
}
|
||||
|
||||
// Message header
|
||||
size += len(hdrMSGSTART)
|
||||
|
||||
// Protocol version
|
||||
size += PackedNumSize
|
||||
|
||||
// Count and Size uint32s
|
||||
size += PackedNumSize * 2
|
||||
|
||||
// Message begin
|
||||
size += len(hdrBODYSTART)
|
||||
|
||||
for _, p := range r.RecordGroups {
|
||||
size += p.Size()
|
||||
}
|
||||
|
||||
// Message end
|
||||
size += len(hdrBODYEND)
|
||||
|
||||
// And closing sequence.
|
||||
size += len(hdrMSGEND)
|
||||
|
||||
if r.common == nil {
|
||||
r.common = new(common)
|
||||
}
|
||||
|
||||
r.size = uint32(size)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ToMap returns a slice of slice of slice of FVP maps for this Message.
|
||||
func (r *Request) ToMap() (m [][][]map[string]interface{}) {
|
||||
|
||||
m = make([][][]map[string]interface{}, len(r.RecordGroups))
|
||||
for idx, rg := range r.RecordGroups {
|
||||
m[idx] = rg.ToMap()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalBinary populates a Request from packed bytes.
|
||||
func (r *Request) UnmarshalBinary(data []byte) (err error) {
|
||||
|
||||
if data == nil || len(data) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var b []byte
|
||||
var rgCnt, bodySize int
|
||||
var rgSize int
|
||||
var rgBuf *bytes.Buffer
|
||||
var buf *bytes.Reader = bytes.NewReader(data)
|
||||
var msgBuf *bytes.Buffer = new(bytes.Buffer)
|
||||
|
||||
if r == nil {
|
||||
*r = Request{}
|
||||
}
|
||||
r.common = &common{}
|
||||
r.size = 0
|
||||
|
||||
// Check for a checksum.
|
||||
b = make([]byte, len(hdrMSGSTART))
|
||||
if _, err = buf.Read(b); err != nil {
|
||||
return
|
||||
}
|
||||
if bytes.Equal(b, hdrCKSUM) {
|
||||
// A checksum header was found.
|
||||
b = make([]byte, CksumPackedSize)
|
||||
if _, err = buf.Read(b); err != nil {
|
||||
return
|
||||
}
|
||||
r.Checksum = new(uint32)
|
||||
*r.Checksum = byteOrder.Uint32(b)
|
||||
// Since we've only read the checksum, we now also have to read in the MSGSTART...
|
||||
b = make([]byte, len(hdrMSGSTART))
|
||||
if _, err = buf.Read(b); err != nil {
|
||||
return
|
||||
}
|
||||
// But we don't need to do anything with it.
|
||||
} else {
|
||||
// We've already read MSGSTART as part of the checksum check.
|
||||
r.Checksum = nil
|
||||
}
|
||||
|
||||
// Get the protocol version.
|
||||
b = make([]byte, PackedNumSize)
|
||||
if _, err = buf.Read(b); err != nil {
|
||||
return
|
||||
}
|
||||
r.ProtocolVersion = UnpackUint32(b)
|
||||
|
||||
// Skip over the BODYSTART (but write it to msgBuf).
|
||||
if _, err = io.CopyN(msgBuf, buf, int64(len(hdrBODYSTART))); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Get the count of record groups
|
||||
b = make([]byte, PackedNumSize)
|
||||
if _, err = buf.Read(b); err != nil {
|
||||
return
|
||||
}
|
||||
rgCnt = UnpackInt(b)
|
||||
if _, err = msgBuf.Write(b); err != nil {
|
||||
return
|
||||
}
|
||||
// And their size
|
||||
b = make([]byte, PackedNumSize)
|
||||
if _, err = buf.Read(b); err != nil {
|
||||
return
|
||||
}
|
||||
bodySize = UnpackInt(b)
|
||||
if _, err = msgBuf.Write(b); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// And the record groups themselves.
|
||||
if _, err = io.CopyN(msgBuf, buf, int64(bodySize+len(hdrBODYEND))); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Validate the checksum.
|
||||
if r.Checksum != nil {
|
||||
if crc32.ChecksumIEEE(msgBuf.Bytes()) != *r.Checksum {
|
||||
err = ErrBadCksum
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we've validated the checksum (if provided), we trim the msgBuf to only RGs.
|
||||
// Skip over the BODYSTART, record group count, and record group size.
|
||||
if _, err = msgBuf.Read(make([]byte, len(hdrBODYSTART)+(PackedNumSize*2))); err != nil {
|
||||
return
|
||||
}
|
||||
// Then truncate.
|
||||
msgBuf.Truncate(bodySize)
|
||||
|
||||
r.RecordGroups = make([]*RequestRecordGroup, rgCnt)
|
||||
|
||||
for idx := 0; idx < rgCnt; idx++ {
|
||||
rgBuf = new(bytes.Buffer)
|
||||
|
||||
// The RG unmarshaler handles the record count, but we need to read it to discard it in msgBuf.
|
||||
if _, err = io.CopyN(rgBuf, msgBuf, int64(PackedNumSize)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
b = make([]byte, PackedNumSize)
|
||||
if _, err = msgBuf.Read(b); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = rgBuf.Write(b); err != nil {
|
||||
return
|
||||
}
|
||||
rgSize = UnpackInt(b)
|
||||
|
||||
if _, err = io.CopyN(rgBuf, msgBuf, int64(rgSize)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r.RecordGroups[idx] = new(RequestRecordGroup)
|
||||
if err = r.RecordGroups[idx].UnmarshalBinary(rgBuf.Bytes()); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_ = r.Size()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// getIdx is a NOOP for Messages, but is used for Model conformance.
|
||||
func (r *Request) getIdx() (idx int) {
|
||||
return
|
||||
}
|
||||
|
||||
// getRecordGroups returns the RecordGroups in this Message.
|
||||
func (r *Request) getRecordGroups() (recordGroups []RecordGroup) {
|
||||
|
||||
recordGroups = make([]RecordGroup, len(r.RecordGroups))
|
||||
for idx, rg := range r.RecordGroups {
|
||||
recordGroups[idx] = rg
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user