go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/data/cmpbin/number.go (about) 1 // Copyright 2015 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package cmpbin 16 17 import ( 18 "errors" 19 "io" 20 "math" 21 ) 22 23 // MaxIntLenN is the maximum length of a cmpbin-encoded N-bit integer 24 // (signed or unsigned). 25 const ( 26 MaxIntLen16 = 3 27 MaxIntLen32 = 5 28 MaxIntLen64 = 9 29 ) 30 31 // ErrOverflow is returned when reading an number which is too large for the 32 // destination type (either uint64 or int64) 33 var ErrOverflow = errors.New("cmpbin: varint overflows") 34 35 // ErrUnderflow is returned when reading an number which is too small for the 36 // destination type (either uint64 or int64) 37 var ErrUnderflow = errors.New("cmpbin: uvarint underflows") 38 39 var paddingMasks = [...]uint64{ 40 0xFFFFFFFF00000000, 41 0xFFFF0000, 42 0xFF00, 43 0xF0, 44 0xC, 45 0x2, 46 0x1, 47 } 48 49 // Calculate the log2 of the unsigned value v. 50 // 51 // This is used to find the position of the highest-set bit in v. 52 // 53 // from https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog 54 // 32 bit implementation extended to 64 bits 55 func uint64Log2(v uint64) uint { 56 log := uint(0) 57 for i, m := range paddingMasks { 58 if v&m != 0 { 59 shift := uint(1<<uint(len(paddingMasks)-2)) >> uint(i) 60 v >>= shift 61 log |= shift 62 } 63 } 64 return log + 1 65 } 66 67 // WriteInt val as a cmpbin Int to the ByteWriter. Returns the number of bytes 68 // written. Only returns an error if the underlying ByteWriter returns an error. 69 func WriteInt(w io.ByteWriter, val int64) (int, error) { 70 var inv byte 71 if val < 0 { 72 inv = 0xff 73 } 74 mag := uint64(val) 75 if inv != 0 { 76 mag = -mag 77 } 78 return writeSignMag(w, mag, inv) 79 } 80 81 // WriteUint writes mag to the ByteWriter. Returns the number of bytes written. 82 // Only returns an error if the underlying ByteWriter returns an error. 83 func WriteUint(w io.ByteWriter, mag uint64) (int, error) { 84 return writeSignMag(w, mag, 0) 85 } 86 87 // ReadInt decodes a cmpbin-encoded number from a ByteReader. It returns the 88 // decoded value and the number of bytes read. The error may be 89 // Err{Over,Under}flow if the number is out of bounds. It may also return an 90 // error if the ByteReader returns an error. 91 func ReadInt(r io.ByteReader) (ret int64, n int, err error) { 92 pos, sigs, mag, n, err := readSignMag(r) 93 if err != nil { 94 return 95 } 96 if pos { 97 if sigs > 63 { 98 err = ErrOverflow 99 } else { 100 ret = int64(mag) 101 } 102 } else { 103 if mag > uint64(-math.MinInt64) { 104 err = ErrUnderflow 105 } else { 106 ret = int64(-mag) 107 } 108 } 109 return 110 } 111 112 // ReadUint decodes a cmpbin-encoded positive number from a ByteReader. It 113 // returns the decoded value and the number of bytes read. The error may be 114 // Err{Over,Under}flow if the number is out of bounds. It may also return an 115 // error if the ByteReader returns an error. 116 func ReadUint(r io.ByteReader) (mag uint64, n int, err error) { 117 pos, _, mag, n, err := readSignMag(r) 118 if err != nil { 119 return 120 } 121 if !pos { 122 err = ErrUnderflow 123 } 124 return 125 } 126 127 func writeSignMag(w io.ByteWriter, mag uint64, inv byte) (n int, err error) { 128 sigs := uint64Log2(mag) 129 130 wb := func(b byte) error { 131 n++ 132 return w.WriteByte(b) 133 } 134 135 if err = wb(byte(0x80|(sigs-1)) ^ inv); err != nil { 136 return 137 } 138 139 for sigs > 8 { 140 sigs -= 8 141 142 if err = wb(byte(mag>>sigs) ^ inv); err != nil { 143 return 144 } 145 } 146 if sigs != 0 { 147 if err = wb(byte(mag<<(8-sigs)) ^ inv); err != nil { 148 return 149 } 150 } 151 152 return 153 } 154 155 func readSignMag(r io.ByteReader) (positive bool, sigs uint, mag uint64, n int, err error) { 156 var inv byte 157 158 rb := func() (byte, error) { 159 n++ 160 return r.ReadByte() 161 } 162 163 b0, err := rb() 164 if err != nil { 165 return 166 } 167 positive = true 168 if b0&0x80 == 0 { 169 positive = false 170 inv = 0xff 171 } 172 173 sigs = uint((b0^inv)&0x7f) + 1 174 if sigs > 64 { 175 err = ErrOverflow 176 return 177 } 178 179 numBytes := int((sigs+7)>>3) + 1 180 181 var b byte 182 shift := uint(64 - 8) 183 for i := 1; i < numBytes; i++ { 184 b, err = rb() 185 if err != nil { 186 return 187 } 188 mag |= uint64(b^inv) << shift 189 shift -= 8 190 } 191 mag >>= 64 - sigs 192 193 return 194 }