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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

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}