208 lines
5.7 KiB
Go
208 lines
5.7 KiB
Go
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
|
|
}
|
|
}
|