| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- package binary
- import (
- "encoding/binary"
- "io"
- "math"
- "reflect"
- "unsafe"
- )
- func NewEncoder(w io.Writer) *Encoder {
- return &Encoder{
- output: w,
- }
- }
- func NewEncoderWithCompress(w io.Writer) *Encoder {
- return &Encoder{
- output: w,
- compressOutput: NewCompressWriter(w),
- }
- }
- type Encoder struct {
- compress bool
- output io.Writer
- compressOutput io.Writer
- scratch [binary.MaxVarintLen64]byte
- }
- func (enc *Encoder) SelectCompress(compress bool) {
- if enc.compressOutput == nil {
- return
- }
- if enc.compress && !compress {
- enc.Flush()
- }
- enc.compress = compress
- }
- func (enc *Encoder) Get() io.Writer {
- if enc.compress && enc.compressOutput != nil {
- return enc.compressOutput
- }
- return enc.output
- }
- func (enc *Encoder) Uvarint(v uint64) error {
- ln := binary.PutUvarint(enc.scratch[:binary.MaxVarintLen64], v)
- if _, err := enc.Get().Write(enc.scratch[0:ln]); err != nil {
- return err
- }
- return nil
- }
- func (enc *Encoder) Bool(v bool) error {
- if v {
- return enc.UInt8(1)
- }
- return enc.UInt8(0)
- }
- func (enc *Encoder) Int8(v int8) error {
- return enc.UInt8(uint8(v))
- }
- func (enc *Encoder) Int16(v int16) error {
- return enc.UInt16(uint16(v))
- }
- func (enc *Encoder) Int32(v int32) error {
- return enc.UInt32(uint32(v))
- }
- func (enc *Encoder) Int64(v int64) error {
- return enc.UInt64(uint64(v))
- }
- func (enc *Encoder) UInt8(v uint8) error {
- enc.scratch[0] = v
- if _, err := enc.Get().Write(enc.scratch[:1]); err != nil {
- return err
- }
- return nil
- }
- func (enc *Encoder) UInt16(v uint16) error {
- enc.scratch[0] = byte(v)
- enc.scratch[1] = byte(v >> 8)
- if _, err := enc.Get().Write(enc.scratch[:2]); err != nil {
- return err
- }
- return nil
- }
- func (enc *Encoder) UInt32(v uint32) error {
- enc.scratch[0] = byte(v)
- enc.scratch[1] = byte(v >> 8)
- enc.scratch[2] = byte(v >> 16)
- enc.scratch[3] = byte(v >> 24)
- if _, err := enc.Get().Write(enc.scratch[:4]); err != nil {
- return err
- }
- return nil
- }
- func (enc *Encoder) UInt64(v uint64) error {
- enc.scratch[0] = byte(v)
- enc.scratch[1] = byte(v >> 8)
- enc.scratch[2] = byte(v >> 16)
- enc.scratch[3] = byte(v >> 24)
- enc.scratch[4] = byte(v >> 32)
- enc.scratch[5] = byte(v >> 40)
- enc.scratch[6] = byte(v >> 48)
- enc.scratch[7] = byte(v >> 56)
- if _, err := enc.Get().Write(enc.scratch[:8]); err != nil {
- return err
- }
- return nil
- }
- func (enc *Encoder) Float32(v float32) error {
- return enc.UInt32(math.Float32bits(v))
- }
- func (enc *Encoder) Float64(v float64) error {
- return enc.UInt64(math.Float64bits(v))
- }
- func (enc *Encoder) String(v string) error {
- str := Str2Bytes(v)
- if err := enc.Uvarint(uint64(len(str))); err != nil {
- return err
- }
- if _, err := enc.Get().Write(str); err != nil {
- return err
- }
- return nil
- }
- func (enc *Encoder) RawString(str []byte) error {
- if err := enc.Uvarint(uint64(len(str))); err != nil {
- return err
- }
- if _, err := enc.Get().Write(str); err != nil {
- return err
- }
- return nil
- }
- func (enc *Encoder) Decimal128(bytes []byte) error {
- _, err := enc.Get().Write(bytes)
- return err
- }
- func (enc *Encoder) Write(b []byte) (int, error) {
- return enc.Get().Write(b)
- }
- func (enc *Encoder) Flush() error {
- if w, ok := enc.Get().(WriteFlusher); ok {
- return w.Flush()
- }
- return nil
- }
- type WriteFlusher interface {
- Flush() error
- }
- func Str2Bytes(str string) []byte {
- // Copied from https://github.com/m3db/m3/blob/master/src/x/unsafe/string.go#L62
- if len(str) == 0 {
- return nil
- }
- // We need to declare a real byte slice so internally the compiler
- // knows to use an unsafe.Pointer to keep track of the underlying memory so that
- // once the slice's array pointer is updated with the pointer to the string's
- // underlying bytes, the compiler won't prematurely GC the memory when the string
- // goes out of scope.
- var b []byte
- byteHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b))
- // This makes sure that even if GC relocates the string's underlying
- // memory after this assignment, the corresponding unsafe.Pointer in the internal
- // slice struct will be updated accordingly to reflect the memory relocation.
- byteHeader.Data = (*reflect.StringHeader)(unsafe.Pointer(&str)).Data
- // It is important that we access str after we assign the Data
- // pointer of the string header to the Data pointer of the slice header to
- // make sure the string (and the underlying bytes backing the string) don't get
- // GC'ed before the assignment happens.
- l := len(str)
- byteHeader.Len = l
- byteHeader.Cap = l
- return b
- }
|