github.com/deso-protocol/core@v1.2.9/lib/varint.go (about) 1 package lib 2 3 // This file implements "varint" encoding of 64-bit integers. 4 // The encoding is: 5 // - unsigned integers are serialized 7 bits at a time, starting with the 6 // least significant bits 7 // - the most significant bit (msb) in each output byte indicates if there 8 // is a continuation byte (msb = 1) 9 // - signed integers are mapped to unsigned integers using "zig-zag" 10 // encoding: Positive values x are written as 2*x + 0, negative values 11 // are written as 2*(^x) + 1; that is, negative numbers are complemented 12 // and whether to complement is encoded in bit 0. 13 // 14 // Design note: 15 // At most 10 bytes are needed for 64-bit values. The encoding could 16 // be more dense: a full 64-bit value needs an extra byte just to hold bit 63. 17 // Instead, the msb of the previous byte could be used to hold bit 63 since we 18 // know there can't be more than 64 bits. This is a trivial improvement and 19 // would reduce the maximum encoding length to 9 bytes. However, it breaks the 20 // invariant that the msb is always the "continuation bit" and thus makes the 21 // format incompatible with a varint encoding for larger numbers (say 128-bit). 22 23 import ( 24 "errors" 25 "io" 26 ) 27 28 // MaxVarintLenN is the maximum length of a varint-encoded N-bit integer. 29 const ( 30 MaxVarintLen16 = 3 31 MaxVarintLen32 = 5 32 MaxVarintLen64 = 10 33 ) 34 35 func UintToBuf(xx uint64) []byte { 36 scratchBytes := make([]byte, MaxVarintLen64) 37 nn := PutUvarint(scratchBytes, xx) 38 return scratchBytes[:nn] 39 } 40 41 // PutUvarint encodes a uint64 into buf and returns the number of bytes written. 42 // If the buffer is too small, PutUvarint will panic. 43 func PutUvarint(buf []byte, x uint64) int { 44 i := 0 45 for x >= 0x80 { 46 buf[i] = byte(x) | 0x80 47 x >>= 7 48 i++ 49 } 50 buf[i] = byte(x) 51 return i + 1 52 } 53 54 // Uvarint decodes a uint64 from buf and returns that value and the 55 // number of bytes read (> 0). If an error occurred, the value is 0 56 // and the number of bytes n is <= 0 meaning: 57 // 58 // n == 0: buf too small 59 // n < 0: value larger than 64 bits (overflow) 60 // and -n is the number of bytes read 61 // 62 func Uvarint(buf []byte) (uint64, int) { 63 var x uint64 64 var s uint 65 for i, b := range buf { 66 if b < 0x80 { 67 if i > 9 || i == 9 && b > 1 { 68 return 0, -(i + 1) // overflow 69 } 70 return x | uint64(b)<<s, i + 1 71 } 72 x |= uint64(b&0x7f) << s 73 s += 7 74 } 75 return 0, 0 76 } 77 78 func IntToBuf(xx int64) []byte { 79 scratchBytes := make([]byte, MaxVarintLen64) 80 nn := PutVarint(scratchBytes, xx) 81 return scratchBytes[:nn] 82 } 83 84 // PutVarint encodes an int64 into buf and returns the number of bytes written. 85 // If the buffer is too small, PutVarint will panic. 86 func PutVarint(buf []byte, x int64) int { 87 ux := uint64(x) << 1 88 if x < 0 { 89 ux = ^ux 90 } 91 return PutUvarint(buf, ux) 92 } 93 94 // Varint decodes an int64 from buf and returns that value and the 95 // number of bytes read (> 0). If an error occurred, the value is 0 96 // and the number of bytes n is <= 0 with the following meaning: 97 // 98 // n == 0: buf too small 99 // n < 0: value larger than 64 bits (overflow) 100 // and -n is the number of bytes read 101 // 102 func Varint(buf []byte) (int64, int) { 103 ux, n := Uvarint(buf) // ok to continue in presence of error 104 x := int64(ux >> 1) 105 if ux&1 != 0 { 106 x = ^x 107 } 108 return x, n 109 } 110 111 var overflow = errors.New("binary: varint overflows a 64-bit integer") 112 113 // ReadUvarint reads an encoded unsigned integer from r and returns it as a uint64. 114 func ReadUvarint(r io.Reader) (uint64, error) { 115 var x uint64 116 var s uint 117 buf := []byte{0x00} 118 for i := 0; ; i++ { 119 nn, err := io.ReadFull(r, buf) 120 if err != nil || nn != 1 { 121 return x, err 122 } 123 b := buf[0] 124 if b < 0x80 { 125 if i > 9 || i == 9 && b > 1 { 126 return x, overflow 127 } 128 return x | uint64(b)<<s, nil 129 } 130 x |= uint64(b&0x7f) << s 131 s += 7 132 } 133 } 134 135 // ReadVarint reads an encoded signed integer from r and returns it as an int64. 136 func ReadVarint(r io.Reader) (int64, error) { 137 ux, err := ReadUvarint(r) // ok to continue in presence of error 138 x := int64(ux >> 1) 139 if ux&1 != 0 { 140 x = ^x 141 } 142 return x, err 143 }