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  }