package wireproto import ( `bytes` `fmt` `io` `strings` ) // GetParent returns the parent RecordGroup. func (r *ResponseRecord) GetParent() (rg RecordGroup) { rg = r.parent return } // MarshalBinary renders a ResponseRecord into a byte-packed format. func (r *ResponseRecord) MarshalBinary() (data []byte, err error) { var b []byte var recSize int var buf *bytes.Buffer = new(bytes.Buffer) _ = r.Size() // KVP Count if _, err = buf.Write(PackInt(len(r.Pairs))); err != nil { return } for _, i := range r.Pairs { recSize += i.Size() } // KVP Size if _, err = buf.Write(PackInt(recSize)); err != nil { return } // Original/Request Size if _, err = buf.Write(PackInt(r.OriginalRecord.Size())); err != nil { return } for _, i := range r.Pairs { if b, err = i.MarshalBinary(); err != nil { return } if _, err = buf.Write(b); err != nil { return } } if r.OriginalRecord != nil { if b, err = r.OriginalRecord.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 *ResponseRecord) Model() (out string) { out = r.ModelCustom(IndentChars, SeparatorChars, indentRec) return } // ModelCustom is like Model with user-defined formatting. func (r *ResponseRecord) 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)) // Request Record Size sb.WriteString(strings.Repeat(indent, int(level))) sb.WriteString(fmt.Sprintf("%x", PackUint32(uint32(r.OriginalRecord.Size())))) sb.WriteString(sep) sb.WriteString(fmt.Sprintf("// Request Record Size (%d)\n", r.OriginalRecord.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)) } // REQUEST sb.WriteString(strings.Repeat(indent, int(level))) if r.OriginalRecord == nil { sb.WriteString("// (No Original Request Record Attached)\n") } else { sb.WriteString( fmt.Sprintf("// Record Group %d, Record %d (REQUEST RECORD) (%d bytes)\n", r.rgIdx+1, r.rIdx+1, r.OriginalRecord.Size()), ) sb.WriteString(r.OriginalRecord.ModelCustom(indent, sep, level+1)) } out = sb.String() return } // Resolve associates children with parents. func (r *ResponseRecord) 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 ResponseRecord's calculated size (in bytes) and updates the size field if 0. Note that it *includes* the size of ResponseRecord.OriginalRecord and its allocator. */ func (r *ResponseRecord) Size() (size int) { if r == nil { return } // Count and Size uint32s, plus Size for response record. size += PackedNumSize * 3 for _, p := range r.Pairs { size += p.Size() } if r.OriginalRecord != nil { size += r.OriginalRecord.Size() } if r.common == nil { r.common = new(common) } r.size = uint32(size) return } /* SizeNoResp is like Size but does not include the size of the ResponseRecord.OriginalRecord nor its allocator. It does *not* update the internal size field. */ func (r *ResponseRecord) SizeNoResp() (size int) { if r == nil { return } // Count and Size uint32s size += PackedNumSize * 2 for _, p := range r.Pairs { size += p.Size() } return } // ToMap returns a slice of FVP maps for this Record. func (r *ResponseRecord) 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 ResponseRecord from packed bytes. func (r *ResponseRecord) UnmarshalBinary(data []byte) (err error) { if data == nil || len(data) == 0 { return } var b []byte var cnt, size, reqSize int var fvpNmSize, fvpValSize int var fvpBuf *bytes.Buffer var recBuf *bytes.Buffer = new(bytes.Buffer) var origBuf *bytes.Buffer = new(bytes.Buffer) var buf *bytes.Reader = bytes.NewReader(data) if r == nil { *r = ResponseRecord{} } 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) // Size of original record. b = make([]byte, PackedNumSize) if _, err = buf.Read(b); err != nil { return } reqSize = UnpackInt(b) // And split out the FVPs and original record. if _, err = io.CopyN(recBuf, buf, int64(size)); err != nil { return } if _, err = io.CopyN(origBuf, buf, int64(reqSize)); err != nil { return } r.Pairs = make([]*FieldValuePair, cnt) for idx := 0; idx < cnt; idx++ { fvpBuf = new(bytes.Buffer) // Field name size b = make([]byte, PackedNumSize) if _, err = recBuf.Read(b); err != nil { return } if _, err = fvpBuf.Write(b); err != nil { return } fvpNmSize = UnpackInt(b) // Field value size b = make([]byte, PackedNumSize) if _, err = recBuf.Read(b); err != nil { return } if _, err = fvpBuf.Write(b); err != nil { return } fvpValSize = UnpackInt(b) if _, err = io.CopyN(fvpBuf, recBuf, int64(fvpNmSize+fvpValSize)); err != nil { return } r.Pairs[idx] = new(FieldValuePair) if err = r.Pairs[idx].UnmarshalBinary(fvpBuf.Bytes()); err != nil { return } } if reqSize != 0 { r.OriginalRecord = new(RequestRecord) if err = r.OriginalRecord.UnmarshalBinary(origBuf.Bytes()); err != nil { return } } _ = r.Size() return } // getFvps returns this Record's FVP. func (r *ResponseRecord) 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 *ResponseRecord) getIdx() (idx int) { idx = r.rIdx return } /* recToFVPs returns a slice of FieldValuePair from a Record. Mostly used for ResponseFieldValuePair.OriginalRecord. data should be a Record data structure. */ func (r *ResponseRecord) recToFVPs(data []byte) (fvps []*FieldValuePair, err error) { var b []byte var fvpCnt, recSize int var fvpNmSize, fvpValSize int var fvpBuf *bytes.Buffer var buf *bytes.Reader = bytes.NewReader(data) // Number of FVPs b = make([]byte, PackedNumSize) if _, err = buf.Read(b); err != nil { return } fvpCnt = UnpackInt(b) fvps = make([]*FieldValuePair, fvpCnt) // Size of record b = make([]byte, PackedNumSize) if _, err = buf.Read(b); err != nil { return } recSize = UnpackInt(b) // And create a new buffer from the remainder. b = make([]byte, recSize) if _, err = buf.Read(b); err != nil { return } buf = bytes.NewReader(b) for idx := 0; idx < fvpCnt; idx++ { fvpBuf = new(bytes.Buffer) // Get the fvp name size b = make([]byte, PackedNumSize) if _, err = buf.Read(b); err != nil { return } if _, err = fvpBuf.Write(b); err != nil { return } fvpNmSize = UnpackInt(b) // Get the fvp value size b = make([]byte, PackedNumSize) if _, err = buf.Read(b); err != nil { return } if _, err = fvpBuf.Write(b); err != nil { return } fvpValSize = UnpackInt(b) b = make([]byte, fvpNmSize+fvpValSize) if _, err = buf.Read(b); err != nil { return } if _, err = fvpBuf.Write(b); err != nil { return } fvps[idx] = new(FieldValuePair) if err = fvps[idx].UnmarshalBinary(fvpBuf.Bytes()); err != nil { return } } return }