You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
207 lines
5.7 KiB
207 lines
5.7 KiB
package tlv |
|
|
|
import ( |
|
"encoding/binary" |
|
"errors" |
|
"io" |
|
) |
|
|
|
// ErrTUintNotMinimal signals that decoding a truncated uint failed because the |
|
// value was not minimally encoded. |
|
var ErrTUintNotMinimal = errors.New("truncated uint not minimally encoded") |
|
|
|
// numLeadingZeroBytes16 computes the number of leading zeros for a uint16. |
|
func numLeadingZeroBytes16(v uint16) uint64 { |
|
switch { |
|
case v == 0: |
|
return 2 |
|
case v&0xff00 == 0: |
|
return 1 |
|
default: |
|
return 0 |
|
} |
|
} |
|
|
|
// SizeTUint16 returns the number of bytes remaining in a uint16 after |
|
// truncating the leading zeros. |
|
func SizeTUint16(v uint16) uint64 { |
|
return 2 - numLeadingZeroBytes16(v) |
|
} |
|
|
|
// ETUint16 is an Encoder for truncated uint16 values, where leading zeros will |
|
// be omitted. An error is returned if val is not a *uint16. |
|
func ETUint16(w io.Writer, val interface{}, buf *[8]byte) error { |
|
if t, ok := val.(*uint16); ok { |
|
binary.BigEndian.PutUint16(buf[:2], *t) |
|
numZeros := numLeadingZeroBytes16(*t) |
|
_, err := w.Write(buf[numZeros:2]) |
|
return err |
|
} |
|
return NewTypeForEncodingErr(val, "uint16") |
|
} |
|
|
|
// ETUint16T is an Encoder for truncated uint16 values, where leading zeros will |
|
// be omitted. An error is returned if val is not a *uint16. |
|
func ETUint16T(w io.Writer, val uint16, buf *[8]byte) error { |
|
binary.BigEndian.PutUint16(buf[:2], val) |
|
numZeros := numLeadingZeroBytes16(val) |
|
_, err := w.Write(buf[numZeros:2]) |
|
return err |
|
} |
|
|
|
// DTUint16 is an Decoder for truncated uint16 values, where leading zeros will |
|
// be resurrected. An error is returned if val is not a *uint16. |
|
func DTUint16(r io.Reader, val interface{}, buf *[8]byte, l uint64) error { |
|
if t, ok := val.(*uint16); ok && l <= 2 { |
|
_, err := io.ReadFull(r, buf[2-l:2]) |
|
if err != nil { |
|
return err |
|
} |
|
zero(buf[:2-l]) |
|
*t = binary.BigEndian.Uint16(buf[:2]) |
|
if 2-numLeadingZeroBytes16(*t) != l { |
|
return ErrTUintNotMinimal |
|
} |
|
return nil |
|
} |
|
return NewTypeForDecodingErr(val, "uint16", l, 2) |
|
} |
|
|
|
// numLeadingZeroBytes32 computes the number of leading zeros for a uint32. |
|
func numLeadingZeroBytes32(v uint32) uint64 { |
|
switch { |
|
case v == 0: |
|
return 4 |
|
case v&0xffffff00 == 0: |
|
return 3 |
|
case v&0xffff0000 == 0: |
|
return 2 |
|
case v&0xff000000 == 0: |
|
return 1 |
|
default: |
|
return 0 |
|
} |
|
} |
|
|
|
// SizeTUint32 returns the number of bytes remaining in a uint32 after |
|
// truncating the leading zeros. |
|
func SizeTUint32(v uint32) uint64 { |
|
return 4 - numLeadingZeroBytes32(v) |
|
} |
|
|
|
// ETUint32 is an Encoder for truncated uint32 values, where leading zeros will |
|
// be omitted. An error is returned if val is not a *uint32. |
|
func ETUint32(w io.Writer, val interface{}, buf *[8]byte) error { |
|
if t, ok := val.(*uint32); ok { |
|
binary.BigEndian.PutUint32(buf[:4], *t) |
|
numZeros := numLeadingZeroBytes32(*t) |
|
_, err := w.Write(buf[numZeros:4]) |
|
return err |
|
} |
|
return NewTypeForEncodingErr(val, "uint32") |
|
} |
|
|
|
// ETUint32T is an Encoder for truncated uint32 values, where leading zeros will |
|
// be omitted. An error is returned if val is not a *uint32. |
|
func ETUint32T(w io.Writer, val uint32, buf *[8]byte) error { |
|
binary.BigEndian.PutUint32(buf[:4], val) |
|
numZeros := numLeadingZeroBytes32(val) |
|
_, err := w.Write(buf[numZeros:4]) |
|
return err |
|
} |
|
|
|
// DTUint32 is an Decoder for truncated uint32 values, where leading zeros will |
|
// be resurrected. An error is returned if val is not a *uint32. |
|
func DTUint32(r io.Reader, val interface{}, buf *[8]byte, l uint64) error { |
|
if t, ok := val.(*uint32); ok && l <= 4 { |
|
_, err := io.ReadFull(r, buf[4-l:4]) |
|
if err != nil { |
|
return err |
|
} |
|
zero(buf[:4-l]) |
|
*t = binary.BigEndian.Uint32(buf[:4]) |
|
if 4-numLeadingZeroBytes32(*t) != l { |
|
return ErrTUintNotMinimal |
|
} |
|
return nil |
|
} |
|
return NewTypeForDecodingErr(val, "uint32", l, 4) |
|
} |
|
|
|
// numLeadingZeroBytes64 computes the number of leading zeros for a uint64. |
|
// |
|
// TODO(conner): optimize using unrolled binary search |
|
func numLeadingZeroBytes64(v uint64) uint64 { |
|
switch { |
|
case v == 0: |
|
return 8 |
|
case v&0xffffffffffffff00 == 0: |
|
return 7 |
|
case v&0xffffffffffff0000 == 0: |
|
return 6 |
|
case v&0xffffffffff000000 == 0: |
|
return 5 |
|
case v&0xffffffff00000000 == 0: |
|
return 4 |
|
case v&0xffffff0000000000 == 0: |
|
return 3 |
|
case v&0xffff000000000000 == 0: |
|
return 2 |
|
case v&0xff00000000000000 == 0: |
|
return 1 |
|
default: |
|
return 0 |
|
} |
|
} |
|
|
|
// SizeTUint64 returns the number of bytes remaining in a uint64 after |
|
// truncating the leading zeros. |
|
func SizeTUint64(v uint64) uint64 { |
|
return 8 - numLeadingZeroBytes64(v) |
|
} |
|
|
|
// ETUint64 is an Encoder for truncated uint64 values, where leading zeros will |
|
// be omitted. An error is returned if val is not a *uint64. |
|
func ETUint64(w io.Writer, val interface{}, buf *[8]byte) error { |
|
if t, ok := val.(*uint64); ok { |
|
binary.BigEndian.PutUint64(buf[:], *t) |
|
numZeros := numLeadingZeroBytes64(*t) |
|
_, err := w.Write(buf[numZeros:]) |
|
return err |
|
} |
|
return NewTypeForEncodingErr(val, "uint64") |
|
} |
|
|
|
// ETUint64T is an Encoder for truncated uint64 values, where leading zeros will |
|
// be omitted. An error is returned if val is not a *uint64. |
|
func ETUint64T(w io.Writer, val uint64, buf *[8]byte) error { |
|
binary.BigEndian.PutUint64(buf[:], val) |
|
numZeros := numLeadingZeroBytes64(val) |
|
_, err := w.Write(buf[numZeros:]) |
|
return err |
|
} |
|
|
|
// DTUint64 is an Decoder for truncated uint64 values, where leading zeros will |
|
// be resurrected. An error is returned if val is not a *uint64. |
|
func DTUint64(r io.Reader, val interface{}, buf *[8]byte, l uint64) error { |
|
if t, ok := val.(*uint64); ok && l <= 8 { |
|
_, err := io.ReadFull(r, buf[8-l:]) |
|
if err != nil { |
|
return err |
|
} |
|
zero(buf[:8-l]) |
|
*t = binary.BigEndian.Uint64(buf[:]) |
|
if 8-numLeadingZeroBytes64(*t) != l { |
|
return ErrTUintNotMinimal |
|
} |
|
return nil |
|
} |
|
return NewTypeForDecodingErr(val, "uint64", l, 8) |
|
} |
|
|
|
// zero clears the passed byte slice. |
|
func zero(b []byte) { |
|
for i := range b { |
|
b[i] = 0x00 |
|
} |
|
}
|
|
|