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.

varint_test.gno

5.11 Kb · 201 lines
  1package varint
  2
  3import (
  4	"strings"
  5	"testing"
  6
  7	"gno.land/p/nt/uassert/v0"
  8)
  9
 10const (
 11	minInt64  = -1 << 63
 12	maxInt64  = 1<<63 - 1
 13	maxUint64 = 1<<64 - 1
 14)
 15
 16// hex renders a byte string as lowercase hex, so a failing vector prints the
 17// bytes rather than mojibake.
 18func hex(s string) string {
 19	const digits = "0123456789abcdef"
 20	var b strings.Builder
 21	for i := 0; i < len(s); i++ {
 22		b.WriteByte(digits[s[i]>>4])
 23		b.WriteByte(digits[s[i]&0x0f])
 24	}
 25	return b.String()
 26}
 27
 28// The protobuf base-128 vectors, one per group boundary plus the documented
 29// 300 example and the full 64-bit maximum.
 30func TestUvarintVectors(t *testing.T) {
 31	cases := []struct {
 32		in   uint64
 33		want string
 34	}{
 35		{0, "00"},
 36		{1, "01"},
 37		{127, "7f"},
 38		{128, "8001"},
 39		{300, "ac02"},
 40		{16383, "ff7f"},
 41		{16384, "808001"},
 42		{maxUint64, "ffffffffffffffffff01"},
 43	}
 44	for _, c := range cases {
 45		got := PutUvarint(c.in)
 46		uassert.Equal(t, c.want, hex(got))
 47
 48		back, n, err := Uvarint(got)
 49		uassert.NoError(t, err)
 50		uassert.Equal(t, c.in, back)
 51		uassert.Equal(t, len(got), n)
 52	}
 53}
 54
 55func TestLenAgreesWithEncoding(t *testing.T) {
 56	for _, x := range []uint64{0, 1, 127, 128, 16383, 16384, 2097151, 2097152, maxUint64} {
 57		uassert.Equal(t, len(PutUvarint(x)), Len(x))
 58	}
 59	// The boundary is what the groups are for: one byte up to 127.
 60	uassert.Equal(t, 1, Len(127))
 61	uassert.Equal(t, 2, Len(128))
 62	uassert.Equal(t, MaxLen, Len(maxUint64))
 63}
 64
 65func TestZigZagVectors(t *testing.T) {
 66	cases := []struct {
 67		in   int64
 68		want uint64
 69	}{
 70		{0, 0},
 71		{-1, 1},
 72		{1, 2},
 73		{-2, 3},
 74		{2, 4},
 75		{maxInt64, maxUint64 - 1},
 76		{minInt64, maxUint64},
 77	}
 78	for _, c := range cases {
 79		uassert.Equal(t, c.want, ZigZag(c.in))
 80		uassert.Equal(t, c.in, UnZigZag(c.want))
 81	}
 82}
 83
 84func TestVarintRoundTrip(t *testing.T) {
 85	for _, x := range []int64{0, -1, 1, -64, 63, -1000000, 1000000, minInt64, maxInt64} {
 86		enc := PutVarint(x)
 87		uassert.Equal(t, len(enc), LenVarint(x))
 88
 89		back, n, err := Varint(enc)
 90		uassert.NoError(t, err)
 91		uassert.Equal(t, x, back)
 92		uassert.Equal(t, len(enc), n)
 93	}
 94}
 95
 96// Zigzag is the whole reason signed values are not encoded directly: -1 as a
 97// two's complement uint64 is ten bytes, as a zigzag it is one.
 98func TestZigZagKeepsSmallNegativesShort(t *testing.T) {
 99	uassert.Equal(t, 1, LenVarint(-1))
100	uassert.Equal(t, 1, LenVarint(-64))
101	uassert.Equal(t, 2, LenVarint(-65))
102	uassert.Equal(t, MaxLen, Len(uint64(0xffffffffffffffff))) // the same -1, unzigzagged
103}
104
105func TestRejectsNonCanonical(t *testing.T) {
106	cases := []struct {
107		name, in string
108	}{
109		{"zero padded once", "\x80\x00"},
110		{"one padded once", "\x81\x00"},
111		{"zero padded thrice", "\x80\x80\x80\x00"},
112		{"full width padding", "\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00"},
113	}
114	for _, c := range cases {
115		_, _, err := Uvarint(c.in)
116		uassert.ErrorIs(t, err, ErrNotCanonical)
117	}
118	// A single zero byte is canonical: it is the only encoding of 0.
119	x, n, err := Uvarint("\x00")
120	uassert.NoError(t, err)
121	uassert.Equal(t, uint64(0), x)
122	uassert.Equal(t, 1, n)
123}
124
125func TestRejectsTruncated(t *testing.T) {
126	for _, s := range []string{"", "\x80", "\xff\xff", strings.Repeat("\xff", 9)} {
127		_, _, err := Uvarint(s)
128		uassert.ErrorIs(t, err, ErrTruncated)
129	}
130}
131
132func TestRejectsOverflow(t *testing.T) {
133	// Ten groups carry 70 bits; the tenth may only hold the one bit left of 64.
134	_, _, err := Uvarint(strings.Repeat("\xff", 9) + "\x02")
135	uassert.ErrorIs(t, err, ErrOverflow)
136
137	// An eleventh group is unreachable, so it reads as overflow too.
138	_, _, err = Uvarint(strings.Repeat("\xff", 10) + "\x01")
139	uassert.ErrorIs(t, err, ErrOverflow)
140
141	// Exactly at the limit is fine.
142	x, n, err := Uvarint(strings.Repeat("\xff", 9) + "\x01")
143	uassert.NoError(t, err)
144	uassert.Equal(t, uint64(maxUint64), x)
145	uassert.Equal(t, MaxLen, n)
146}
147
148func TestUvarintReadsOnlyItsOwnPrefix(t *testing.T) {
149	s := PutUvarint(300) + PutUvarint(1) + "leftover"
150	x, n, err := Uvarint(s)
151	uassert.NoError(t, err)
152	uassert.Equal(t, uint64(300), x)
153	uassert.Equal(t, 2, n)
154
155	x, n, err = Uvarint(s[n:])
156	uassert.NoError(t, err)
157	uassert.Equal(t, uint64(1), x)
158	uassert.Equal(t, 1, n)
159}
160
161func TestSequences(t *testing.T) {
162	in := []uint64{0, 1, 127, 128, 300, maxUint64}
163	enc := PutUvarints(in)
164	uassert.Equal(t, 1+1+1+2+2+10, len(enc))
165
166	out, err := Uvarints(enc)
167	uassert.NoError(t, err)
168	uassert.Equal(t, len(in), len(out))
169	for i := range in {
170		uassert.Equal(t, in[i], out[i])
171	}
172
173	signed := []int64{0, -1, 1, minInt64, maxInt64}
174	back, err := Varints(PutVarints(signed))
175	uassert.NoError(t, err)
176	uassert.Equal(t, len(signed), len(back))
177	for i := range signed {
178		uassert.Equal(t, signed[i], back[i])
179	}
180}
181
182// A partial value at the end is corruption, and returning the values read so
183// far would hand a caller a silently short list.
184func TestSequenceRefusesTrailingFragment(t *testing.T) {
185	_, err := Uvarints(PutUvarint(300) + "\x80")
186	uassert.ErrorIs(t, err, ErrTrailing)
187
188	_, err = Varints(PutVarint(-5) + "\xff")
189	uassert.ErrorIs(t, err, ErrTrailing)
190
191	out, err := Uvarints("")
192	uassert.NoError(t, err)
193	uassert.Equal(t, 0, len(out))
194}
195
196func TestAppendBuildsInPlace(t *testing.T) {
197	var buf []byte
198	buf = AppendUvarint(buf, 300)
199	buf = AppendVarint(buf, -1)
200	uassert.Equal(t, "ac0201", hex(string(buf)))
201}