Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

codec.gno

4.36 Kb · 174 lines
  1package vmkit
  2
  3import "errors"
  4
  5// ErrTruncated is returned by every Reader method that runs past the end of
  6// the buffer. A snapshot is consensus state, so a short read is always a hard
  7// error and never a zero value.
  8var ErrTruncated = errors.New("vmkit: truncated snapshot")
  9
 10// ErrBadSnapshot is returned when a snapshot is well-formed but not for this
 11// machine: wrong magic, wrong version, or a field outside its legal range.
 12var ErrBadSnapshot = errors.New("vmkit: snapshot is not for this machine")
 13
 14// Writer builds a snapshot. Every integer is fixed-width big-endian and every
 15// byte slice is length-prefixed, so the encoding is canonical: the same
 16// machine state always produces the same bytes, on every node.
 17//
 18// Machines are not required to use it, but a machine that invents its own
 19// layout owes the zoo an explanation, because the cross-VM snapshot cost
 20// comparison only means something when the encodings match.
 21type Writer struct {
 22	buf []byte
 23}
 24
 25// NewWriter returns a Writer with room for n bytes reserved up front.
 26func NewWriter(n int) *Writer {
 27	if n < 0 {
 28		n = 0
 29	}
 30	return &Writer{buf: make([]byte, 0, n)}
 31}
 32
 33// Byte appends one byte.
 34func (w *Writer) Byte(b byte) { w.buf = append(w.buf, b) }
 35
 36// Uint32 appends a 4-byte big-endian value.
 37func (w *Writer) Uint32(v uint32) {
 38	w.buf = append(w.buf, byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
 39}
 40
 41// Uint64 appends an 8-byte big-endian value.
 42func (w *Writer) Uint64(v uint64) {
 43	w.buf = append(w.buf,
 44		byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32),
 45		byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
 46}
 47
 48// Int appends a signed value, zig-zag encoded into a Uint64 so that small
 49// negative numbers do not cost eight 0xff bytes.
 50func (w *Writer) Int(v int64) {
 51	u := uint64(v) << 1
 52	if v < 0 {
 53		u = ^u
 54	}
 55	w.Uint64(u)
 56}
 57
 58// Bytes appends a length-prefixed byte slice.
 59func (w *Writer) Bytes(p []byte) {
 60	w.Uint32(uint32(len(p)))
 61	w.buf = append(w.buf, p...)
 62}
 63
 64// String appends a length-prefixed string.
 65func (w *Writer) String(s string) { w.Bytes([]byte(s)) }
 66
 67// Out returns the encoded bytes.
 68func (w *Writer) Out() []byte { return w.buf }
 69
 70// Reader consumes a snapshot written by [Writer].
 71//
 72// It latches the first error it hits: a caller may decode a whole struct and
 73// check [Reader.Err] once at the end, instead of after every field. Every
 74// method returns a zero value once the Reader is in error.
 75type Reader struct {
 76	buf []byte
 77	pos int
 78	err error
 79}
 80
 81// NewReader returns a Reader over b.
 82func NewReader(b []byte) *Reader { return &Reader{buf: b} }
 83
 84// Err returns the first error hit, or nil.
 85func (r *Reader) Err() error { return r.err }
 86
 87// Remaining reports how many bytes are left unread.
 88func (r *Reader) Remaining() int {
 89	if r.pos > len(r.buf) {
 90		return 0
 91	}
 92	return len(r.buf) - r.pos
 93}
 94
 95// Fail latches err, so a machine can reject a field its own rules forbid and
 96// have it surface through [Reader.Err] like any decoding failure.
 97func (r *Reader) Fail(err error) {
 98	if r.err == nil {
 99		r.err = err
100	}
101}
102
103func (r *Reader) take(n int) []byte {
104	if r.err != nil {
105		return nil
106	}
107	if n < 0 || r.pos+n > len(r.buf) {
108		r.err = ErrTruncated
109		return nil
110	}
111	out := r.buf[r.pos : r.pos+n]
112	r.pos += n
113	return out
114}
115
116// Byte reads one byte.
117func (r *Reader) Byte() byte {
118	p := r.take(1)
119	if p == nil {
120		return 0
121	}
122	return p[0]
123}
124
125// Uint32 reads a 4-byte big-endian value.
126func (r *Reader) Uint32() uint32 {
127	p := r.take(4)
128	if p == nil {
129		return 0
130	}
131	return uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3])
132}
133
134// Uint64 reads an 8-byte big-endian value.
135func (r *Reader) Uint64() uint64 {
136	p := r.take(8)
137	if p == nil {
138		return 0
139	}
140	return uint64(p[0])<<56 | uint64(p[1])<<48 | uint64(p[2])<<40 | uint64(p[3])<<32 |
141		uint64(p[4])<<24 | uint64(p[5])<<16 | uint64(p[6])<<8 | uint64(p[7])
142}
143
144// Int reads a zig-zag encoded signed value written by [Writer.Int].
145func (r *Reader) Int() int64 {
146	u := r.Uint64()
147	v := int64(u >> 1)
148	if u&1 != 0 {
149		v = ^v
150	}
151	return v
152}
153
154// Bytes reads a length-prefixed byte slice. The result is a copy, so a
155// machine can keep it without aliasing the snapshot it was handed.
156func (r *Reader) Bytes() []byte {
157	n := int(r.Uint32())
158	p := r.take(n)
159	if p == nil {
160		return nil
161	}
162	out := make([]byte, n)
163	copy(out, p)
164	return out
165}
166
167// String reads a length-prefixed string.
168func (r *Reader) String() string {
169	p := r.take(int(r.Uint32()))
170	if p == nil {
171		return ""
172	}
173	return string(p)
174}