Documentation ¶
Overview ¶
Package vamp implements the Vampire binary data serialization format for Go.
While Vampire does not provide zero-copy characteristics it gives you "direct access" to values i.e., you can navigate any path down into nested structures without parsing too many other parts that come before the value you are looking for. This feature makes any value directly accessible – “perviam” in Latin. Just because “Vampire” sounds much cooler the format is called Vampire in the end. But vamp is the package name to Go for because its shorter and still associated with vampires.
Type Promotion ¶
When reading and writing data, Vamp supports type promotion between certain types to facilitate the preservation of backwards compatibility.
Type promotion between integers is supported if a source type can be stored in a target type without loss. E.g. uint16 can be promoted to int32 but no signed integer type can be promoted to any unsigned integer type.
float32 will be promoted to `float64`
For the machine-dependent integers int and uint, it depends on the current value whether it can be read from or written to a Vampire integer. If the current value is within the range of the target, the read or write is successful. Otherwise, it results in a runtime error.
Date and Duration ¶
By default as int64 <-> Unix time with millis.
Example ¶
type Animal struct { Age int `vamp:"age,size=8"` Name string `vamp:",size=8"` Owners []string `vamp:",size=8:8"` Male bool } // Use reflection to get the Vampire type; can be created with API, too t, _ := TypeOf(Animal{}) // We use a 16-bit offset for the buffers const BufferOffset = Size16 // Create write buffer with room for a Vampire header wbuf := NewWriteBuffer(t, BufferOffset, HeaderSize, nil) // Write the header (incl. buffer's offset size) in front of data copy(wbuf.AllBytes(), HeaderFor(BufferOffset).Bytes()) // Write a record of type t to the buffer. Value has to match type t. t.Write(wbuf, Animal{ Age: 4, Name: "Lassie", Owners: []string{"Timmy", "Martin"}, Male: false, }) // How many bytes did we write fmt.Printf("With header: %d; Value data only: %d\n", len(wbuf.AllBytes()), len(wbuf.Bytes())) // Now extract the complete buffer, incl. header data := wbuf.AllBytes() // Wrap data into a read buffer that expects the header at byte 0 // Reads buffer's offset size from header (We could also directly read from wbuf) rbuf, _ := NewReadBuffer(t, data, false) // We need the specific type – record – to select a field rt := t.(*Record) // Slice the read buffer to the 3rd field "Owners" revert := rt.Field(rbuf, 2) // Read the Owners into a string array var owners []string rt.FieldType(2).Read(rbuf, &owners) // We want to unslice the buffer to return to the complete record rbuf.Unslice(revert) // Print what we got from the binary data fmt.Println(owners)
Output: With header: 35; Value data only: 31 [Timmy Martin]
Example (Lexer) ¶
rd := bufio.NewReader(strings.NewReader(`<Size8 Foo:[Size8 "Size8"] | float32 | {ID:uint32 Name:"Size16"} >`)) fmt.Println(Parse(rd))
Output: <Size8 Foo:[Size8 "Size8"]|float32|{ID:uint32 Name:"Size16"}> <nil>
Index ¶
- Constants
- Variables
- func ResetTypeBuffer(buf *Buffer, off sizeRange, prefix int)
- func TypeName(t Type) string
- func ValidName(s string) int
- func WriteType(buf *Buffer, t Type) error
- type Alt
- type AnyMarshaler
- type Array
- func (t *Array) DynNo() uint
- func (t *Array) Elem() Type
- func (a *Array) Equals(t Type, names bool) bool
- func (t *Array) FixSize() uint
- func (a *Array) Hash(h hash.Hash, names bool)
- func (t *Array) Index(buf *ReadBuffer, i uint) (revert uint)
- func (t *Array) Len(buf *ReadBuffer) uint
- func (t *Array) LenRange() sizeRange
- func (t *Array) Read(buf *ReadBuffer, into any) error
- func (t *Array) ReadArray(buf *ReadBuffer, ls ArrayUnmarshaler) error
- func (t *Array) ReadOpt(buf *ReadBuffer, into any) (ok bool, err error)
- func (t *Array) String() string
- func (t *Array) VampRecordRead(rt *Record, buf *ReadBuffer) error
- func (t *Array) VampRecordWrite(rt *Record, buf *Buffer) error
- func (t *Array) VampUnionAlt() (uint, any)
- func (t *Array) Write(buf *Buffer, ls any) (err error)
- func (t *Array) WriteArray(buf *Buffer, ls ArrayMarshaler) error
- func (t *Array) WriteOptNone(buf *Buffer) error
- func (t *Array) WriteOptOne(buf *Buffer, value any) error
- type ArrayMarshaler
- type ArrayUnmarshaler
- type Buffer
- type Bytes
- func (t Bytes) DynNo() uint
- func (s Bytes) Equals(t Type, _ bool) bool
- func (t Bytes) FixSize() uint
- func (s Bytes) Hash(h hash.Hash, _ bool)
- func (t Bytes) LenRange() sizeRange
- func (t Bytes) Read(buf *ReadBuffer, into any) (err error)
- func (t Bytes) ReadBytes(buf *ReadBuffer) []byte
- func (t Bytes) ReadString(buf *ReadBuffer) string
- func (t Bytes) String() string
- func (t *Bytes) VampFromUint8(v uint8) error
- func (t Bytes) VampToUint8() (uint8, error)
- func (t Bytes) VampUnionAlt() (uint, any)
- func (t Bytes) Write(buf *Buffer, value any) error
- func (t Bytes) WriteBytes(buf *Buffer, data []byte) error
- func (t Bytes) WriteString(buf *Buffer, s string) error
- type Header
- type List
- func (t *List) DynNo() uint
- func (t *List) Elem() Type
- func (l *List) Equals(t Type, names bool) bool
- func (t *List) FixSize() uint
- func (a *List) Hash(h hash.Hash, names bool)
- func (t *List) LenRange() sizeRange
- func (t *List) ListLen(buf *ReadBuffer) (len uint, ok bool)
- func (t *List) Next(buf *ReadBuffer) (revert uint, ok bool)
- func (t *List) Read(buf *ReadBuffer, into any) error
- func (t *List) ReadList(buf *ReadBuffer, into ListUnmarshaler) error
- func (l *List) ReadStream(r io.Reader, offSize sizeRange, buffer []byte) *ReadStream
- func (t *List) String() string
- func (t *List) VampRecordRead(rt *Record, buf *ReadBuffer) error
- func (t *List) VampRecordWrite(rt *Record, buf *Buffer) error
- func (t *List) VampUnionAlt() (uint, any)
- func (t *List) Write(buf *Buffer, val any) error
- func (t *List) WriteList(buf *Buffer, ls ListMarshaler) error
- func (l *List) WriteStream(w io.Writer, offSize sizeRange, buffer []byte) *WriteStream
- type ListIterator
- type ListMarshaler
- type ListUnmarshaler
- type Path
- type PathElement
- type PathError
- type ReadBuffer
- type ReadStream
- type Record
- func (t *Record) DynNo() uint
- func (r *Record) Equals(t Type, names bool) bool
- func (t *Record) Field(buf *ReadBuffer, i uint) (revert uint)
- func (t *Record) FieldName(i uint) string
- func (t *Record) FieldType(i uint) Type
- func (t *Record) FixSize() uint
- func (r *Record) Hash(h hash.Hash, names bool)
- func (t *Record) NumField() uint
- func (t *Record) Read(buf *ReadBuffer, out any) (err error)
- func (t *Record) ReadFields(buf *ReadBuffer, outs ...any) error
- func (t *Record) String() string
- func (t *Record) VampArrayFinish() error
- func (t *Record) VampArrayLen() uint
- func (t *Record) VampArrayReader(i uint) (any, error)
- func (t *Record) VampArrayResize(l uint) error
- func (t *Record) VampArrayWriter(i uint) (any, error)
- func (t *Record) VampUnionAlt() (uint, any)
- func (t *Record) Write(buf *Buffer, values any) error
- func (t *Record) WriteFields(buf *Buffer, values ...any) error
- type RecordMarshaler
- type RecordUnmarshaler
- type Reflect
- type Type
- type Union
- func (t *Union) Alt(i uint) Type
- func (t *Union) AltName(i uint) string
- func (t *Union) AltRange() sizeRange
- func (t *Union) DynNo() uint
- func (u *Union) Equals(t Type, names bool) bool
- func (t *Union) FixSize() uint
- func (u *Union) Hash(h hash.Hash, names bool)
- func (t *Union) NumAlts() uint
- func (t *Union) Read(buf *ReadBuffer, into any) error
- func (t *Union) ReadAlt(buf *ReadBuffer, selectVal func(uint) any) (any, error)
- func (t *Union) String() string
- func (t *Union) VampRecordRead(rt *Record, buf *ReadBuffer) error
- func (t *Union) VampRecordWrite(rt *Record, buf *Buffer) error
- func (t *Union) VampUnionAlt() (uint, any)
- func (t *Union) WithAltReader(f func(uint, any) (any, error)) *Union
- func (t *Union) Write(buf *Buffer, value any) error
- func (t *Union) WriteAlt(buf *Buffer, alt uint, value any) error
- type UnionMarshaler
- type UnionUnmarshaler
- type WriteStream
Examples ¶
Constants ¶
const ( // Version of the implemented Vampire encoding schema Version = 0 // Size of Vampire data header in byte HeaderSize = len(Header{}) )
const ( // Bytes will be read as string if the bytes are valid UTF-8 CheckUTF8 bytesReadMode = iota // Bytes will be read as []byte ReadRaw // Bytes will be read as string. It is not checked if bytes are valid UTF-8. ReadString )
Bytes Reading Mode ¶
The bytes mode controls how a Vampire array is unmarshaled when the target type is not determined e.g., read into an *any.
const ( // Size8 is a 8-bit size range with values 0…255 Size8 sizeRange = iota // Size16 is a 16-bit size range with values 0…65535 Size16 // Size32 is a 32-bit size range with values 0…4294967295 Size32 )
Size Ranges ¶
Size ranges are used in vamp to define the valid range for buffer offsets, string length and any sort of ranges of size in byte that is used in Vampire encoding.
Variables ¶
var ( Bool boolType Int8 int8Type Int16 int16Type Int32 int32Type Int64 int64Type Uint8 uint8Type Uint16 uint16Type Uint32 uint32Type Uint64 uint64Type Float32 float32Type Float64 float64Type )
Vampire Basic Types ¶
Each <N>Type variable implements Type and represents a Vampire type that corresponds directly to a Go type <n> where <n> is <N> folded to lower case e.g., Bool corresponds to Go's bool type.
Marshaler and Unmarshaler interfaces are not explicitly defined for basic types. To implement user defined (un-)marshaling to or from <N>Type a value has to implement methods that match the following convention:
The marshaler for writing <n> to <N>Type:
interface{ VampTo<N>() (<n>, error) }
The unmarshaler for reading <n> from <N>Type:
interface{ VampFrom<N>(<n>) error }
var Any anyType
var IOBytesMode = CheckUTF8
Functions ¶
func ResetTypeBuffer ¶
Types ¶
type Alt ¶
func (Alt) VampUnionAlt ¶
type AnyMarshaler ¶
type Array ¶ added in v0.5.0
type Array struct {
// contains filtered or unexported fields
}
Example ¶
a := []string{"boo", "bar", "baz"} t, _ := TypeOf(a) fmt.Println(t) buf := NewWriteBuffer(t, Size16, 0, nil) t.Write(buf, a) var out []string t.Read(&buf.ReadBuffer, &out) fmt.Printf("%[1]T=%[1]v\n", out)
Output: [Size32 "Size16"] []string=[boo bar baz]
func NewOptional ¶ added in v0.5.0
NewOptional returns an ArrayType with length of Size8 – the smallest sufficient size range. This is the recommended way to represent optional values.
Example ¶
et := NewBytes(Size8, ReadRaw) ot := NewOptional(et) buf := NewWriteBuffer(ot, Size8, 0, nil) ot.WriteOptOne(buf, "Hello, optional!") var out string ok, _ := ot.ReadOpt(&buf.ReadBuffer, &out) fmt.Println(ok, out) buf.Reset(ot, Size16, 0) ot.WriteOptNone(buf) out = "-" ok, _ = ot.ReadOpt(&buf.ReadBuffer, &out) fmt.Println(ok, out)
Output: true Hello, optional! false -
func (*Array) Len ¶ added in v0.5.0
func (t *Array) Len(buf *ReadBuffer) uint
func (*Array) ReadArray ¶ added in v0.5.0
func (t *Array) ReadArray(buf *ReadBuffer, ls ArrayUnmarshaler) error
func (*Array) ReadOpt ¶ added in v0.5.0
func (t *Array) ReadOpt(buf *ReadBuffer, into any) (ok bool, err error)
func (*Array) VampRecordRead ¶ added in v0.5.0
func (t *Array) VampRecordRead(rt *Record, buf *ReadBuffer) error
func (*Array) VampRecordWrite ¶ added in v0.5.0
func (*Array) VampUnionAlt ¶ added in v0.5.0
func (*Array) WriteArray ¶ added in v0.5.0
func (t *Array) WriteArray(buf *Buffer, ls ArrayMarshaler) error
func (*Array) WriteOptNone ¶ added in v0.5.0
type ArrayMarshaler ¶
type ArrayUnmarshaler ¶
type Bytes ¶ added in v0.5.0
type Bytes struct {
// contains filtered or unexported fields
}
mode is a vamp but not a Vampire thing (Hash; Equals)
func (Bytes) Read ¶ added in v0.5.0
func (t Bytes) Read(buf *ReadBuffer, into any) (err error)
User Defined Unmarshaling
Unmarshaling from byte array:
interface{ VampFromBytes([]byte) error } // must copy its argument
Unmarshaling from string:
interface{ VampFromString(string) error }
func (Bytes) ReadBytes ¶ added in v0.5.0
func (t Bytes) ReadBytes(buf *ReadBuffer) []byte
func (Bytes) ReadString ¶ added in v0.5.0
func (t Bytes) ReadString(buf *ReadBuffer) string
func (*Bytes) VampFromUint8 ¶ added in v0.5.0
func (Bytes) VampToUint8 ¶ added in v0.5.0
func (Bytes) VampUnionAlt ¶ added in v0.5.0
type Header ¶
type Header [4]byte
Header is the VMP header that can be written to buffers to mark the data to be Vampire encoded and to provide the offsetSize used for the data.
The VMP header is "VMPx" where x encodes the vampire version and the offset size.s
Example ¶
fmt.Println(HeaderFor(Size16))
Output: Vampire-v0+16bit
func ParseHeader ¶
type List ¶ added in v0.5.0
type List struct {
// contains filtered or unexported fields
}
Example ¶
a := []string{"boo", "bar", "baz"} t := NewList(Size32, NewString(Size16)) fmt.Println(t) buf := NewWriteBuffer(t, Size16, 0, nil) t.Write(buf, a) var out any t.Read(&buf.ReadBuffer, &out) fmt.Printf("%[1]T=%[1]v\n", out)
Output: (Size32 "Size16") []interface {}=[boo bar baz]
func (*List) Next ¶ added in v0.5.0
func (t *List) Next(buf *ReadBuffer) (revert uint, ok bool)
TODO Test ListType.Next()
func (*List) ReadList ¶ added in v0.5.0
func (t *List) ReadList(buf *ReadBuffer, into ListUnmarshaler) error
func (*List) ReadStream ¶ added in v0.5.0
func (l *List) ReadStream(r io.Reader, offSize sizeRange, buffer []byte) *ReadStream
func (*List) VampRecordRead ¶ added in v0.5.0
func (t *List) VampRecordRead(rt *Record, buf *ReadBuffer) error
func (*List) VampRecordWrite ¶ added in v0.5.0
func (*List) VampUnionAlt ¶ added in v0.5.0
func (*List) WriteList ¶ added in v0.5.0
func (t *List) WriteList(buf *Buffer, ls ListMarshaler) error
func (*List) WriteStream ¶ added in v0.5.0
func (l *List) WriteStream(w io.Writer, offSize sizeRange, buffer []byte) *WriteStream
type ListIterator ¶
type ListMarshaler ¶
type ListMarshaler interface {
VampListIter() ListIterator
}
type ListUnmarshaler ¶
type PathElement ¶ added in v0.2.0
type PathError ¶ added in v0.2.0
type PathError struct {
// contains filtered or unexported fields
}
Example ¶
rt := NewRecord( NewString(Size16), Float64, Bool, ) buf := NewWriteBuffer(rt, Size16, 0, nil) err := rt.WriteFields(buf, "next fails", "", true) fmt.Println(err)
Output: .1:vampire cannot write string as float64
type ReadBuffer ¶
type ReadBuffer struct {
// contains filtered or unexported fields
}
ReadBuffer adapts a Vampire encoded byte array to be used for read-only access. One has to use the specific Type that matches the encoded data to read the data from the buffer.
func NewReadBuffer ¶
func NewReadBuffer(t Type, buf []byte, search bool) (*ReadBuffer, error)
NewReadBufferOffSize wraps buf for read access when the offsetSize is provided by a VMP Header in buf. When search is true NewReadBuffer scans for the first valid VMP header and uses it's offsetSize. Otherwise the VMP header must start at buf[0].
func NewReadBufferOffSize ¶
func NewReadBufferOffSize(t Type, off sizeRange, buf []byte) *ReadBuffer
NewReadBufferOffSize wraps buf for read access when the offsetSize used for the encoded data is known. The first byte of the data of Type t must be buf[0].
func NewTypeReadBuffer ¶
func NewTypeReadBuffer(buf []byte, search bool) (*ReadBuffer, error)
func NewTypeReadBufferOffSize ¶
func NewTypeReadBufferOffSize(off sizeRange, buf []byte) *ReadBuffer
func (*ReadBuffer) AllBytes ¶
func (b *ReadBuffer) AllBytes() []byte
func (*ReadBuffer) Bytes ¶
func (b *ReadBuffer) Bytes() []byte
func (*ReadBuffer) Offset ¶
func (b *ReadBuffer) Offset() sizeRange
func (*ReadBuffer) Unslice ¶
func (b *ReadBuffer) Unslice(revert uint)
type ReadStream ¶ added in v0.5.0
type ReadStream struct {
// contains filtered or unexported fields
}
func (*ReadStream) Buffer ¶ added in v0.5.0
func (rs *ReadStream) Buffer() *ReadBuffer
func (*ReadStream) Err ¶ added in v0.5.0
func (rs *ReadStream) Err() error
func (*ReadStream) Head ¶ added in v0.5.0
func (rs *ReadStream) Head() (length uint, data []byte, error error)
func (*ReadStream) Next ¶ added in v0.5.0
func (rs *ReadStream) Next() bool
func (*ReadStream) Read ¶ added in v0.5.0
func (rs *ReadStream) Read(into any) error
type Record ¶ added in v0.5.0
type Record struct {
// contains filtered or unexported fields
}
TODO Slice to field
Example ¶
r := struct { Name string Age uint8 }{ Name: "John Doe", Age: 44, } t, _ := TypeOf(r) fmt.Println(t) buf := NewWriteBuffer(t, Size16, 0, nil) t.Write(buf, r) var out any t.Read(&buf.ReadBuffer, &out) fmt.Printf("%[1]T=%[1]v\n", out)
Output: {Name:"Size16" Age:uint8} []interface {}=[John Doe 44]
func (*Record) Field ¶ added in v0.5.0
func (t *Record) Field(buf *ReadBuffer, i uint) (revert uint)
func (*Record) ReadFields ¶ added in v0.5.0
func (t *Record) ReadFields(buf *ReadBuffer, outs ...any) error
func (*Record) VampArrayFinish ¶ added in v0.5.0
func (*Record) VampArrayLen ¶ added in v0.5.0
func (*Record) VampArrayReader ¶ added in v0.5.0
func (*Record) VampArrayResize ¶ added in v0.5.0
func (*Record) VampArrayWriter ¶ added in v0.5.0
func (*Record) VampUnionAlt ¶ added in v0.5.0
type RecordMarshaler ¶
type RecordUnmarshaler ¶
type RecordUnmarshaler interface {
VampRecordRead(*Record, *ReadBuffer) error
}
type Reflect ¶
type Reflect struct {
// contains filtered or unexported fields
}
func DefaultReflect ¶
func DefaultReflect() Reflect
func (Reflect) MustTypeOf ¶ added in v0.4.0
func (Reflect) StringSize ¶
type Type ¶
type Type interface { FixSize() uint // fix size in byte of the type DynNo() uint // number of dynamic elements of the type // Write the value to buffer if its structure matches Type Write(b *Buffer, value any) error // Read buffers content into argument if its structure matches Type Read(b *ReadBuffer, into any) error Equals(t Type, names bool) bool Hash(h hash.Hash, names bool) fmt.Stringer }
Type is the interface of all Vampire types that are used to define the structure of marshaled data. Types can be created as Array, List, Record or Union form other types. Basic types are booleans, signed and unsigned integers with 8, 16, 32 or 64 bit and float with 32 or 64 bit. The special Bytes types are used to hold any binary data, especially UTF-8 strings. Finally there is the Any type that can hold any Vampire value as a tuple of a value and its type.
func Anonymize ¶ added in v0.4.0
Anonymize strips all attached names from a type and returns the pure type definition.
func MustTypeOf ¶ added in v0.4.0
func Named ¶ added in v0.4.0
Named attaches a name to a type. Type names are irrelevant for the encoded data end exist for documentation only. Passing a named type to the constructors of record or union types will assign the name to the respective record field or union variant.
func ReadType ¶
func ReadType(buf *ReadBuffer) (t Type, err error)
func TypeOf ¶
Example ¶
type animal struct { Age int `vamp:"age,size=8"` Name string `vamp:",size=8"` Owners []string `vamp:",size=8:8"` Male bool `vamp:"-"` } t, err := TypeOf(animal{}) if err != nil { fmt.Println(err) } else { fmt.Println(t.String()) }
Output: {age:int8 Name:"Size8" Owners:[Size8 "Size8"]}
type Union ¶ added in v0.5.0
type Union struct { AltReader func(alt uint, into any) (reader any, err error) // contains filtered or unexported fields }
Example ¶
type address struct { Street string No string } type geocoos struct { Lat, Lon float64 } addrType, _ := TypeOf(address{}) geoType, _ := TypeOf(geocoos{}) placeType := NewUnion(Size8, addrType, MustNamed("geo", geoType), ) fmt.Println(placeType) buf := NewWriteBuffer(placeType, Size16, 0, nil) placeType.Write(buf, Alt{S: 0, V: address{Street: "Justroad", No: "33a"}}) var out any placeType.Read(&buf.ReadBuffer, &out) fmt.Printf("%[1]T=%[1]v\n", out) buf.Reset(placeType, Size16, 0) placeType.Write(buf, Alt{S: 1, V: geocoos{Lat: 33.321, Lon: 44.123}}) placeType.Read(&buf.ReadBuffer, &out) fmt.Printf("%[1]T=%[1]v\n", out)
Output: <Size8 {Street:"Size16" No:"Size16"}|geo:{Lat:float64 Lon:float64}> []interface {}=[Justroad 33a] []interface {}=[33.321 44.123]
func (*Union) VampRecordRead ¶ added in v0.5.0
func (t *Union) VampRecordRead(rt *Record, buf *ReadBuffer) error
func (*Union) VampRecordWrite ¶ added in v0.5.0
func (*Union) VampUnionAlt ¶ added in v0.5.0
func (*Union) WithAltReader ¶ added in v0.5.0
type UnionMarshaler ¶
type UnionUnmarshaler ¶
type WriteStream ¶ added in v0.5.0
type WriteStream struct {
// contains filtered or unexported fields
}
func (*WriteStream) Close ¶ added in v0.5.0
func (ws *WriteStream) Close() error
func (*WriteStream) Flush ¶ added in v0.5.0
func (ws *WriteStream) Flush() error
func (*WriteStream) Write ¶ added in v0.5.0
func (ws *WriteStream) Write(elem any) error
func (*WriteStream) WriteLast ¶ added in v0.5.0
func (ws *WriteStream) WriteLast(elem any) error
func (*WriteStream) WriteWithNext ¶ added in v0.5.0
func (ws *WriteStream) WriteWithNext(elem any) error