Something went wrong. Try again.
forked from tailscale/tailscale
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178// Copyright (c) Tailscale Inc & contributors// SPDX-License-Identifier: BSD-3-Clause
// Package cstruct provides a helper for decoding binary data that is in the// form of a padded C structure.package cstruct
import ( "encoding/binary" "errors" "io")
// Size of a pointer-typed value, in bitsconst pointerSize = 32 << (^uintptr(0) >> 63)
// We assume that non-64-bit platforms are 32-bit; we don't expect Go to run on// a 16- or 8-bit architecture any time soon.const is64Bit = pointerSize == 64
// Decoder reads and decodes padded fields from a slice of bytes. All fields// are decoded with native endianness.//// Methods of a Decoder do not return errors, but rather store any error within// the Decoder. The first error can be obtained via the Err method; after the// first error, methods will return the zero value for their type.type Decoder struct { b []byte off int err error dbuf [8]byte // for decoding}
// NewDecoder creates a Decoder from a byte slice.func NewDecoder(b []byte) *Decoder { return &Decoder{b: b}}
var errUnsupportedSize = errors.New("unsupported size")
func padBytes(offset, size int) int { if offset == 0 || size == 1 { return 0 } remainder := offset % size return size - remainder}
func (d *Decoder) getField(b []byte) error { size := len(b)
// We only support fields that are multiples of 2 (or 1-sized) if size != 1 && size&1 == 1 { return errUnsupportedSize }
// Fields are aligned to their size padBytes := padBytes(d.off, size) if d.off+size+padBytes > len(d.b) { return io.EOF } d.off += padBytes
copy(b, d.b[d.off:d.off+size]) d.off += size return nil}
// Err returns the first error that was encountered by this Decoder.func (d *Decoder) Err() error { return d.err}
// Offset returns the current read offset for data in the buffer.func (d *Decoder) Offset() int { return d.off}
// Byte returns a single byte from the buffer.func (d *Decoder) Byte() byte { if d.err != nil { return 0 }
if err := d.getField(d.dbuf[0:1]); err != nil { d.err = err return 0 } return d.dbuf[0]}
// Byte returns a number of bytes from the buffer based on the size of the// input slice. No padding is applied.//// If an error is encountered or this Decoder has previously encountered an// error, no changes are made to the provided buffer.func (d *Decoder) Bytes(b []byte) { if d.err != nil { return }
// No padding for byte slices size := len(b) if d.off+size >= len(d.b) { d.err = io.EOF return } copy(b, d.b[d.off:d.off+size]) d.off += size}
// Uint16 returns a uint16 decoded from the buffer.func (d *Decoder) Uint16() uint16 { if d.err != nil { return 0 }
if err := d.getField(d.dbuf[0:2]); err != nil { d.err = err return 0 } return binary.NativeEndian.Uint16(d.dbuf[0:2])}
// Uint32 returns a uint32 decoded from the buffer.func (d *Decoder) Uint32() uint32 { if d.err != nil { return 0 }
if err := d.getField(d.dbuf[0:4]); err != nil { d.err = err return 0 } return binary.NativeEndian.Uint32(d.dbuf[0:4])}
// Uint64 returns a uint64 decoded from the buffer.func (d *Decoder) Uint64() uint64 { if d.err != nil { return 0 }
if err := d.getField(d.dbuf[0:8]); err != nil { d.err = err return 0 } return binary.NativeEndian.Uint64(d.dbuf[0:8])}
// Uintptr returns a uintptr decoded from the buffer.func (d *Decoder) Uintptr() uintptr { if d.err != nil { return 0 }
if is64Bit { return uintptr(d.Uint64()) } else { return uintptr(d.Uint32()) }}
// Int16 returns a int16 decoded from the buffer.func (d *Decoder) Int16() int16 { return int16(d.Uint16())}
// Int32 returns a int32 decoded from the buffer.func (d *Decoder) Int32() int32 { return int32(d.Uint32())}
// Int64 returns a int64 decoded from the buffer.func (d *Decoder) Int64() int64 { return int64(d.Uint64())}