Documentation
¶
Overview ¶
Package record is the bounded, ordered reading of one application record: a canonical CBOR map with unsigned-integer keys, whose key 0 is the record's magic and whose keys above the last one a version defines are preserved and ignored.
A record is refused for the first rule it breaks, in one order, so that two implementations refuse the same bytes for the same reason:
- ErrTooLarge: the bytes exceed the record's bound, checked before anything is decoded;
- ErrCBOR: not one canonical CBOR item (package cbor), or not a map;
- ErrTooLarge: more than MaxKeys entries;
- ErrKeyType: a key that is not an unsigned integer;
- ErrMagic: key 0 is not the byte string the caller names;
- then each defined key as the caller reads it, in the caller's order: ErrMissing, ErrType, and ErrRange or ErrList for the key's own rule.
The application supplies the bound, the last defined key and the magic, and reads its own fields. Nothing here names a record.
Index ¶
- Constants
- Variables
- func Ascending32(a [][32]byte) error
- func CheckExtra(extra cbor.Map, last uint64, defined int) error
- func CheckExtraKeys(extra cbor.Map, last uint64) error
- func Claims(payload, magic []byte) bool
- func DecodeMap(b []byte, max int) (cbor.Map, error)
- func Encode(m cbor.Map, extra cbor.Map, max int) ([]byte, error)
- func InRange(n, lo, hi uint64, what string) error
- func Reason(err error) (string, bool)
- func Values32(a [][32]byte) []cbor.Value
- type Fields
- func (f *Fields) Array(k uint64, max int) ([]cbor.Value, error)
- func (f *Fields) Bool(k uint64) (bool, error)
- func (f *Fields) Bytes(k uint64) ([]byte, error)
- func (f *Fields) BytesN(k uint64, n int) ([]byte, error)
- func (f *Fields) BytesRange(k uint64, lo, hi int) ([]byte, error)
- func (f *Fields) CheckMagic(magic []byte) error
- func (f *Fields) Get(k uint64) (cbor.Value, bool)
- func (f *Fields) Has(k uint64) bool
- func (f *Fields) List32(k uint64, max int) ([][32]byte, error)
- func (f *Fields) Need(k uint64) (cbor.Value, error)
- func (f *Fields) Text(k uint64) (string, error)
- func (f *Fields) Uint(k uint64, lo, hi uint64) (uint64, error)
Examples ¶
Constants ¶
const MaxKeys = 64
MaxKeys bounds the entries of a record map, unknown keys included.
Variables ¶
var ( ErrTooLarge = errors.New("record: record exceeds its bound") ErrCBOR = errors.New("record: not a canonical CBOR map") ErrKeyType = errors.New("record: record key is not an unsigned integer") ErrMagic = errors.New("record: wrong magic") ErrMissing = errors.New("record: required key missing") ErrType = errors.New("record: field has the wrong type") ErrRange = errors.New("record: field out of range") ErrList = errors.New("record: list is over its bound, or not distinct and ascending") )
The refusals of the record steps. An application counts them by Reason, and wraps nothing around them: errors.Is finds each through the detail a step adds.
Functions ¶
func Ascending32 ¶
Ascending32 holds a list of 32-byte values to strictly ascending byte order, or ErrList.
func CheckExtra ¶
CheckExtra is CheckExtraKeys and the bound on the whole map: the preserved pairs and the defined keys the record writes are at most MaxKeys together.
func CheckExtraKeys ¶
CheckExtraKeys holds preserved pairs to the rule that their keys are unsigned integers above last, so re-encoding cannot shadow a defined field.
func Claims ¶
Claims reports whether payload claims to be a record under magic, reading only its head: a definite-length map head (0xa0 to 0xbb), key 0, then a byte-string head of four bytes and magic itself. Key 0 always sorts first in a canonical map with unsigned-integer keys. It allocates nothing and decides nothing else: a payload that claims a record is then held to Decode.
func DecodeMap ¶
DecodeMap is steps 1 to 3: the bound, one canonical CBOR item that is a map, and at most MaxKeys entries. The bound is checked before the decoder runs, and the decoder bounds every declared length against the bytes present before it allocates.
func Encode ¶
Encode writes the defined pairs then the preserved ones as one canonical CBOR map, and holds the result to max. The encoder sorts the keys, so the order of m does not matter.
func Reason ¶
Reason is the fixed label of a record refusal, and false for any other error.
Example ¶
A record is refused for the first rule it breaks, and each refusal has the fixed label a host counts it by.
package main
import (
"fmt"
"github.com/lightwebinc/bcommon/cbor"
"github.com/lightwebinc/bcommon/record"
)
// The sample record's magic and bounds: the test prefix, a letter and a
// version byte. An application registers its own.
var (
sampleMagic = []byte("vxr\x01")
sampleLast = uint64(2)
sampleMax = 256
)
func main() {
enc := func(m cbor.Map) []byte {
b, err := cbor.Encode(m)
if err != nil {
panic(err)
}
return b
}
array, err := cbor.Encode([]cbor.Value{sampleMagic, "a name"})
if err != nil {
panic(err)
}
good := cbor.Map{{Key: uint64(0), Val: sampleMagic}, {Key: uint64(1), Val: "a name"}, {Key: uint64(2), Val: uint64(7)}}
read := func(b []byte) error {
f, err := record.Decode(b, sampleMax, sampleLast, sampleMagic)
if err != nil {
return err
}
if _, err := f.Text(1); err != nil {
return err
}
_, err = f.Uint(2, 1, 100)
return err
}
for _, c := range []struct {
name string
b []byte
}{
{"over the bound, whatever it is", make([]byte, sampleMax+1)},
{"not CBOR", []byte{0xff}},
{"an array", array},
{"a text key", enc(append(cbor.Map{{Key: "name", Val: uint64(1)}}, good...))},
{"another magic", enc(cbor.Map{{Key: uint64(0), Val: []byte("vxq\x01")}})},
{"key 1 missing", enc(cbor.Map{good[0], good[2]})},
{"key 1 a number", enc(cbor.Map{good[0], {Key: uint64(1), Val: uint64(1)}, good[2]})},
{"key 2 out of range", enc(cbor.Map{good[0], good[1], {Key: uint64(2), Val: uint64(101)}})},
} {
reason, _ := record.Reason(read(c.b))
fmt.Printf("%s: %s\n", c.name, reason)
}
}
Output: over the bound, whatever it is: too-large not CBOR: cbor an array: cbor a text key: key-type another magic: magic key 1 missing: missing key 1 a number: type key 2 out of range: range
Types ¶
type Fields ¶
type Fields struct {
// Extra holds the pairs whose keys are above the last defined key, in
// the record's order, exactly as decoded.
Extra cbor.Map
// contains filtered or unexported fields
}
Fields is a decoded record: its known integer keys, and the unknown ones above the last defined key, which are preserved.
func Decode ¶
Decode is steps 1 to 5 in order: DecodeMap, Split and CheckMagic.
Example ¶
An application's decoder reads its own keys, in its own order, from a record the package has held to the steps every record shares. A key above the last defined one is preserved, so a reader of this version writes back what a later version added.
package main
import (
"fmt"
"github.com/lightwebinc/bcommon/cbor"
"github.com/lightwebinc/bcommon/record"
)
// The sample record's magic and bounds: the test prefix, a letter and a
// version byte. An application registers its own.
var (
sampleMagic = []byte("vxr\x01")
sampleLast = uint64(2)
sampleMax = 256
)
func main() {
b, err := record.Encode(cbor.Map{
{Key: uint64(0), Val: sampleMagic},
{Key: uint64(1), Val: "a name"},
{Key: uint64(2), Val: uint64(7)},
}, cbor.Map{{Key: uint64(9), Val: "from a later version"}}, sampleMax)
if err != nil {
fmt.Println(err)
return
}
f, err := record.Decode(b, sampleMax, sampleLast, sampleMagic)
if err != nil {
fmt.Println(err)
return
}
name, err := f.Text(1)
fmt.Println("name:", name, err)
count, err := f.Uint(2, 1, 100)
fmt.Println("count:", count, err)
fmt.Println("preserved:", len(f.Extra), "pair, key", f.Extra[0].Key)
again, err := record.Encode(cbor.Map{
{Key: uint64(0), Val: sampleMagic},
{Key: uint64(1), Val: name},
{Key: uint64(2), Val: count},
}, f.Extra, sampleMax)
fmt.Println("re-encodes to the same bytes:", string(again) == string(b), err)
fmt.Println("claims the record:", record.Claims(b, sampleMagic), record.Claims(b, []byte("vxq\x01")))
}
Output: name: a name <nil> count: 7 <nil> preserved: 1 pair, key 9 re-encodes to the same bytes: true <nil> claims the record: true false
func Split ¶
Split is step 4: it splits a record map into its known integer keys and the unknown ones above last, which are preserved. Any key that is not an unsigned integer refuses the record.
func (*Fields) Array ¶
Array returns key k as an array of at most max elements. The length is checked before the caller allocates anything for the elements.
func (*Fields) BytesN ¶
BytesN returns key k as a byte string of exactly n bytes; a negative n takes any length.
func (*Fields) BytesRange ¶
BytesRange returns key k as a byte string of lo to hi bytes.
func (*Fields) CheckMagic ¶
CheckMagic is step 5: key 0 present, a byte string, and equal to magic.
func (*Fields) List32 ¶
List32 returns key k as an array of at most max 32-byte strings in strictly ascending byte order, so one set has one encoding.