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 }