Search Apps Documentation Source Content File Folder Download Copy Actions Download

proto.gno

4.90 Kb · 161 lines
  1package proto
  2
  3import (
  4	"encoding/binary"
  5	"time"
  6
  7	"gno.land/p/nt/ufmt/v0"
  8)
  9
 10type WireType int
 11
 12const (
 13	VARINT  WireType = 0 //	int32, int64, uint32, uint64, sint32, sint64, bool, enum
 14	FIXED64 WireType = 1 // fixed64, sfixed64, double
 15	LEN     WireType = 2 // string, bytes, embedded messages, packed repeated fields
 16	FIXED32 WireType = 5 //	fixed32, sfixed32, float
 17)
 18
 19// AppendVarint appends a varint field.
 20func AppendVarint(buf []byte, fieldNum int, v uint64) []byte {
 21	if v == 0 {
 22		return buf
 23	}
 24	buf = AppendTag(buf, fieldNum, VARINT)
 25	return binary.AppendUvarint(buf, v)
 26}
 27
 28// MarshalAny returns the wire encoding of a google.protobuf.Any message
 29// holding (typeURL, value). Used to match the cdc.MarshalInterface format
 30// produced by the Cosmos SDK proto codec when storing or verifying
 31// interface-typed messages (e.g. ClientState in the upgrade store).
 32func MarshalAny(typeURL string, value []byte) []byte {
 33	var bz []byte
 34	bz = AppendLengthDelimited(bz, 1, []byte(typeURL))
 35	bz = AppendLengthDelimited(bz, 2, value)
 36	return bz
 37}
 38
 39// AppendPackedVarintInt32 appends a packed repeated int32 varint field.
 40// Negative values are sign-extended to 64 bits to match proto3 wire format.
 41// Returns buf unchanged when values is empty (proto3 default).
 42func AppendPackedVarintInt32(buf []byte, fieldNum int, values []int32) []byte {
 43	if len(values) == 0 {
 44		return buf
 45	}
 46	var packed []byte
 47	for _, v := range values {
 48		packed = binary.AppendUvarint(packed, uint64(int64(v)))
 49	}
 50	buf = AppendTag(buf, fieldNum, LEN)
 51	buf = binary.AppendUvarint(buf, uint64(len(packed)))
 52	return append(buf, packed...)
 53}
 54
 55// AppendFixed64 appends a fixed 64-bit field.
 56func AppendFixed64(buf []byte, fieldNum int, v uint64) []byte {
 57	if v == 0 {
 58		return buf
 59	}
 60	buf = AppendTag(buf, fieldNum, FIXED64)
 61	var b [8]byte
 62	binary.LittleEndian.PutUint64(b[:], v)
 63	return append(buf, b[:]...)
 64}
 65
 66// AppendLengthDelimited appends a length-delimited field. Returns buf
 67// unchanged when bz is empty (proto3 default for optional fields).
 68func AppendLengthDelimited(buf []byte, fieldNum int, bz []byte) []byte {
 69	if len(bz) == 0 {
 70		return buf
 71	}
 72	buf = AppendTag(buf, fieldNum, LEN)
 73	buf = binary.AppendUvarint(buf, uint64(len(bz)))
 74	return append(buf, bz...)
 75}
 76
 77// AppendAlwaysLengthDelimited appends a length-delimited field, including
 78// when bz is empty (emits a length-0 value). Required to match the wire
 79// format produced by gogoproto for fields marked
 80// `[(gogoproto.nullable) = false]`, which always serialize even when their
 81// underlying message has no non-zero fields.
 82func AppendAlwaysLengthDelimited(buf []byte, fieldNum int, bz []byte) []byte {
 83	buf = AppendTag(buf, fieldNum, LEN)
 84	buf = binary.AppendUvarint(buf, uint64(len(bz)))
 85	return append(buf, bz...)
 86}
 87
 88// TimeMarshal returns a proto marshalled time.
 89func TimeMarshal(t time.Time) []byte {
 90	var (
 91		buf     []byte
 92		seconds = t.Unix()
 93		nanos   = int32(t.Nanosecond())
 94	)
 95	// Field 1: seconds (int64 - varint)
 96	buf = AppendVarint(buf, 1, uint64(seconds))
 97	// Field 2: nanos (int32 - varint)
 98	buf = AppendVarint(buf, 2, uint64(nanos))
 99	return buf
100}
101
102// AppendTime appends a google.protobuf.Timestamp field.
103func AppendTime(buf []byte, fieldNum int, t time.Time) []byte {
104	return AppendLengthDelimited(buf, fieldNum, TimeMarshal(t))
105}
106
107// DurationMarshal returns the inner proto encoding of a
108// google.protobuf.Duration (without the length-delimited field wrapper).
109func DurationMarshal(d time.Duration) []byte {
110	var buf []byte
111	seconds := d / time.Second
112	nanos := int32(d % time.Second)
113	if nanos < 0 {
114		nanos += int32(time.Second)
115	}
116	buf = AppendVarint(buf, 1, uint64(seconds))
117	buf = AppendVarint(buf, 2, uint64(nanos))
118	return buf
119}
120
121// AppendDuration appends a google.protobuf.Duration field.
122func AppendDuration(buf []byte, fieldNum int, d time.Duration) []byte {
123	return AppendLengthDelimited(buf, fieldNum, DurationMarshal(d))
124}
125
126// AppendTag appends a protobuf tag (field number and wire type)
127func AppendTag(buf []byte, fieldNum int, wireType WireType) []byte {
128	tag := (fieldNum << 3) | int(wireType)
129	return binary.AppendUvarint(buf, uint64(tag))
130}
131
132// DecodeVarint reads a varint from the byte slice and returns the value and new position
133func DecodeVarint(buf []byte, pos int) (uint64, int, error) {
134	if pos > len(buf) {
135		return 0, pos, ufmt.Errorf("buffer underflow while reading varint")
136	}
137	v, n := binary.Uvarint(buf[pos:])
138	if n == 0 {
139		return 0, pos, ufmt.Errorf("buffer underflow while reading varint")
140	}
141	if n < 0 {
142		return 0, pos + (-n), ufmt.Errorf("varint overflows uint64")
143	}
144	return v, pos + n, nil
145}
146
147// DecodeString reads a string (length-prefixed) from the byte slice
148func DecodeString(buf []byte, pos int) (string, int, error) {
149	length, newPos, err := DecodeVarint(buf, pos)
150	if err != nil {
151		return "", newPos, err
152	}
153	pos = newPos
154
155	if length > uint64(len(buf)-pos) {
156		return "", pos, ufmt.Errorf("buffer underflow while reading string")
157	}
158
159	end := pos + int(length)
160	return string(buf[pos:end]), end, nil
161}