Some types skip the varint entirely and store exactly four little-endian bytes: fixed32, sfixed32, and float. Today you decode and encode those four bytes, the I32 wire type, and confirm the byte order both ways.
Decode and encode a fixed32 value as four little-endian bytes.
The I32 wire type is dead simple: exactly four bytes, no length prefix,
stored little-endian (least-significant byte first). It backs fixed32,
sfixed32, and float. Decoding is just reassembling the four bytes with the
first byte in the low position; encoding writes them back low byte first. Pin the
example 0xD2 0x02 0x96 0x49, which is 1234567890 - reversing the bytes gives the
hex 0x499602D2, so getting the order wrong is immediately visible.
A float uses the same four bytes, reinterpreted as an IEEE 754 single-precision
number rather than an integer - most languages expose this as a bit-cast (Go’s
math.Float32frombits, for instance). The value 1.0 has the bit pattern
0x3F800000, which little-endian is 0x00 0x00 0x80 0x3F, and a negative like -2.5
is 0x00 0x00 0x20 0xC0. The integer and the float share the wire; only the schema
says which one you meant.
// low byte firstfunc ReadFixed32(b []byte) uint32 {return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24}func AppendFixed32(buf []byte, v uint32) []byte {return append(buf, byte(v), byte(v>>8), byte(v>>16), byte(v>>24))}// a float32 is these same 4 bytes reinterpreted (math.Float32frombits)