github.com/decred/dcrlnd@v0.7.6/tlv/truncated.go (about) 1 package tlv 2 3 import ( 4 "encoding/binary" 5 "errors" 6 "io" 7 ) 8 9 // ErrTUintNotMinimal signals that decoding a truncated uint failed because the 10 // value was not minimally encoded. 11 var ErrTUintNotMinimal = errors.New("truncated uint not minimally encoded") 12 13 // numLeadingZeroBytes16 computes the number of leading zeros for a uint16. 14 func numLeadingZeroBytes16(v uint16) uint64 { 15 switch { 16 case v == 0: 17 return 2 18 case v&0xff00 == 0: 19 return 1 20 default: 21 return 0 22 } 23 } 24 25 // SizeTUint16 returns the number of bytes remaining in a uint16 after 26 // truncating the leading zeros. 27 func SizeTUint16(v uint16) uint64 { 28 return 2 - numLeadingZeroBytes16(v) 29 } 30 31 // ETUint16 is an Encoder for truncated uint16 values, where leading zeros will 32 // be omitted. An error is returned if val is not a *uint16. 33 func ETUint16(w io.Writer, val interface{}, buf *[8]byte) error { 34 if t, ok := val.(*uint16); ok { 35 binary.BigEndian.PutUint16(buf[:2], *t) 36 numZeros := numLeadingZeroBytes16(*t) 37 _, err := w.Write(buf[numZeros:2]) 38 return err 39 } 40 return NewTypeForEncodingErr(val, "uint16") 41 } 42 43 // ETUint16T is an Encoder for truncated uint16 values, where leading zeros will 44 // be omitted. An error is returned if val is not a *uint16. 45 func ETUint16T(w io.Writer, val uint16, buf *[8]byte) error { 46 binary.BigEndian.PutUint16(buf[:2], val) 47 numZeros := numLeadingZeroBytes16(val) 48 _, err := w.Write(buf[numZeros:2]) 49 return err 50 } 51 52 // DTUint16 is an Decoder for truncated uint16 values, where leading zeros will 53 // be resurrected. An error is returned if val is not a *uint16. 54 func DTUint16(r io.Reader, val interface{}, buf *[8]byte, l uint64) error { 55 if t, ok := val.(*uint16); ok && l <= 2 { 56 _, err := io.ReadFull(r, buf[2-l:2]) 57 if err != nil { 58 return err 59 } 60 zero(buf[:2-l]) 61 *t = binary.BigEndian.Uint16(buf[:2]) 62 if 2-numLeadingZeroBytes16(*t) != l { 63 return ErrTUintNotMinimal 64 } 65 return nil 66 } 67 return NewTypeForDecodingErr(val, "uint16", l, 2) 68 } 69 70 // numLeadingZeroBytes32 computes the number of leading zeros for a uint32. 71 func numLeadingZeroBytes32(v uint32) uint64 { 72 switch { 73 case v == 0: 74 return 4 75 case v&0xffffff00 == 0: 76 return 3 77 case v&0xffff0000 == 0: 78 return 2 79 case v&0xff000000 == 0: 80 return 1 81 default: 82 return 0 83 } 84 } 85 86 // SizeTUint32 returns the number of bytes remaining in a uint32 after 87 // truncating the leading zeros. 88 func SizeTUint32(v uint32) uint64 { 89 return 4 - numLeadingZeroBytes32(v) 90 } 91 92 // ETUint32 is an Encoder for truncated uint32 values, where leading zeros will 93 // be omitted. An error is returned if val is not a *uint32. 94 func ETUint32(w io.Writer, val interface{}, buf *[8]byte) error { 95 if t, ok := val.(*uint32); ok { 96 binary.BigEndian.PutUint32(buf[:4], *t) 97 numZeros := numLeadingZeroBytes32(*t) 98 _, err := w.Write(buf[numZeros:4]) 99 return err 100 } 101 return NewTypeForEncodingErr(val, "uint32") 102 } 103 104 // ETUint32T is an Encoder for truncated uint32 values, where leading zeros will 105 // be omitted. An error is returned if val is not a *uint32. 106 func ETUint32T(w io.Writer, val uint32, buf *[8]byte) error { 107 binary.BigEndian.PutUint32(buf[:4], val) 108 numZeros := numLeadingZeroBytes32(val) 109 _, err := w.Write(buf[numZeros:4]) 110 return err 111 } 112 113 // DTUint32 is an Decoder for truncated uint32 values, where leading zeros will 114 // be resurrected. An error is returned if val is not a *uint32. 115 func DTUint32(r io.Reader, val interface{}, buf *[8]byte, l uint64) error { 116 if t, ok := val.(*uint32); ok && l <= 4 { 117 _, err := io.ReadFull(r, buf[4-l:4]) 118 if err != nil { 119 return err 120 } 121 zero(buf[:4-l]) 122 *t = binary.BigEndian.Uint32(buf[:4]) 123 if 4-numLeadingZeroBytes32(*t) != l { 124 return ErrTUintNotMinimal 125 } 126 return nil 127 } 128 return NewTypeForDecodingErr(val, "uint32", l, 4) 129 } 130 131 // numLeadingZeroBytes64 computes the number of leading zeros for a uint64. 132 // 133 // TODO(conner): optimize using unrolled binary search 134 func numLeadingZeroBytes64(v uint64) uint64 { 135 switch { 136 case v == 0: 137 return 8 138 case v&0xffffffffffffff00 == 0: 139 return 7 140 case v&0xffffffffffff0000 == 0: 141 return 6 142 case v&0xffffffffff000000 == 0: 143 return 5 144 case v&0xffffffff00000000 == 0: 145 return 4 146 case v&0xffffff0000000000 == 0: 147 return 3 148 case v&0xffff000000000000 == 0: 149 return 2 150 case v&0xff00000000000000 == 0: 151 return 1 152 default: 153 return 0 154 } 155 } 156 157 // SizeTUint64 returns the number of bytes remaining in a uint64 after 158 // truncating the leading zeros. 159 func SizeTUint64(v uint64) uint64 { 160 return 8 - numLeadingZeroBytes64(v) 161 } 162 163 // ETUint64 is an Encoder for truncated uint64 values, where leading zeros will 164 // be omitted. An error is returned if val is not a *uint64. 165 func ETUint64(w io.Writer, val interface{}, buf *[8]byte) error { 166 if t, ok := val.(*uint64); ok { 167 binary.BigEndian.PutUint64(buf[:], *t) 168 numZeros := numLeadingZeroBytes64(*t) 169 _, err := w.Write(buf[numZeros:]) 170 return err 171 } 172 return NewTypeForEncodingErr(val, "uint64") 173 } 174 175 // ETUint64T is an Encoder for truncated uint64 values, where leading zeros will 176 // be omitted. An error is returned if val is not a *uint64. 177 func ETUint64T(w io.Writer, val uint64, buf *[8]byte) error { 178 binary.BigEndian.PutUint64(buf[:], val) 179 numZeros := numLeadingZeroBytes64(val) 180 _, err := w.Write(buf[numZeros:]) 181 return err 182 } 183 184 // DTUint64 is an Decoder for truncated uint64 values, where leading zeros will 185 // be resurrected. An error is returned if val is not a *uint64. 186 func DTUint64(r io.Reader, val interface{}, buf *[8]byte, l uint64) error { 187 if t, ok := val.(*uint64); ok && l <= 8 { 188 _, err := io.ReadFull(r, buf[8-l:]) 189 if err != nil { 190 return err 191 } 192 zero(buf[:8-l]) 193 *t = binary.BigEndian.Uint64(buf[:]) 194 if 8-numLeadingZeroBytes64(*t) != l { 195 return ErrTUintNotMinimal 196 } 197 return nil 198 } 199 return NewTypeForDecodingErr(val, "uint64", l, 8) 200 } 201 202 // zero clears the passed byte slice. 203 func zero(b []byte) { 204 for i := range b { 205 b[i] = 0x00 206 } 207 }