package tape import "io" import "math" import "bufio" // Encodable is any type that can write itself to an encoder. type Encodable interface { // Encode sends data to encoder. It returns the amount of bytes written, // and an error if the write stopped early. Encode(encoder *Encoder) (n int, err error) } // Encoder encodes data to an io.Writer. type Encoder struct { bufio.Writer } // NewEncoder creates a new encoder that writes to writer. func NewEncoder(writer io.Writer) *Encoder { encoder := &Encoder { } encoder.Reset(writer) return encoder } // WriteInt8 encodes an 8-bit signed integer to the output writer. func (this *Encoder) WriteInt8(value int8) (n int, err error) { return this.WriteUint8(uint8(value)) } // WriteUint8 encodes an 8-bit unsigned integer to the output writer. func (this *Encoder) WriteUint8(value uint8) (n int, err error) { return this.Write([]byte { byte(value) }) } // WriteInt16 encodes an 16-bit signed integer to the output writer. func (this *Encoder) WriteInt16(value int16) (n int, err error) { return this.WriteUint16(uint16(value)) } // WriteUint16 encodes an 16-bit unsigned integer to the output writer. func (this *Encoder) WriteUint16(value uint16) (n int, err error) { return this.Write([]byte { byte(value >> 8), byte(value), }) } // WriteInt32 encodes an 32-bit signed integer to the output writer. func (this *Encoder) WriteInt32(value int32) (n int, err error) { return this.WriteUint32(uint32(value)) } // WriteUint32 encodes an 32-bit unsigned integer to the output writer. func (this *Encoder) WriteUint32(value uint32) (n int, err error) { return this.Write([]byte { byte(value >> 24), byte(value >> 16), byte(value >> 8), byte(value), }) } // WriteInt64 encodes an 64-bit signed integer to the output writer. func (this *Encoder) WriteInt64(value int64) (n int, err error) { return this.WriteUint64(uint64(value)) } // WriteUint64 encodes an 64-bit unsigned integer to the output writer. func (this *Encoder) WriteUint64(value uint64) (n int, err error) { return this.Write([]byte { byte(value >> 56), byte(value >> 48), byte(value >> 40), byte(value >> 32), byte(value >> 24), byte(value >> 16), byte(value >> 8), byte(value), }) } // WriteIntN encodes an N-byte signed integer to the output writer. func (this *Encoder) WriteIntN(value int64, bytes int) (n int, err error) { return this.WriteUintN(uint64(value), bytes) } // for Write/ReadUintN, increase buffers if go somehow gets support for over 64 // bit integers. we could also make an expanding int type in goutil to use here, // or maybe there is one in the stdlib. keep the int64 versions as well though // because its ergonomic. // WriteUintN encodes an N-byte unsigned integer to the output writer. func (this *Encoder) WriteUintN(value uint64, bytes int) (n int, err error) { // TODO: don't make multiple write calls (without allocating) buffer := [1]byte { } for bytesLeft := bytes; bytesLeft > 0; bytesLeft -- { buffer[0] = byte(value) >> ((bytesLeft - 1) * 8) nn, err := this.Write(buffer[:]) n += nn; if err != nil { return n, err } } return n, nil } // WriteFloat16 encodes a 16-bit floating point value to the output writer. func (this *Encoder) WriteFloat16(value float32) (n int, err error) { return this.WriteUint16(f32bitsToF16bits(math.Float32bits(value))) } // WriteFloat32 encodes a 32-bit floating point value to the output writer. func (this *Encoder) WriteFloat32(value float32) (n int, err error) { return this.WriteUint32(math.Float32bits(value)) } // WriteFloat64 encodes a 64-bit floating point value to the output writer. func (this *Encoder) WriteFloat64(value float64) (n int, err error) { return this.WriteUint64(math.Float64bits(value)) } // WriteTag encodes a [Tag] to the output writer. func (this *Encoder) WriteTag(value Tag) (n int, err error) { return this.WriteUint8(uint8(value)) } // f32bitsToF16bits returns uint16 (Float16 bits) converted from the specified float32. // Conversion rounds to nearest integer with ties to even. // Taken from https://github.com/x448/float16/blob/v0.8.4/float16 // // MIT License // // Copyright (c) 2019 Montgomery Edwards⁴⁴⁸ and Faye Amacker // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. func f32bitsToF16bits(u32 uint32) uint16 { // Translated from Rust to Go by Montgomery Edwards⁴⁴⁸ (github.com/x448). // All 4294967296 conversions with this were confirmed to be correct by x448. // Original Rust implementation is by Kathryn Long (github.com/starkat99) with MIT license. sign := u32 & 0x80000000 exp := u32 & 0x7f800000 coef := u32 & 0x007fffff if exp == 0x7f800000 { // NaN or Infinity nanBit := uint32(0) if coef != 0 { nanBit = uint32(0x0200) } return uint16((sign >> 16) | uint32(0x7c00) | nanBit | (coef >> 13)) } halfSign := sign >> 16 unbiasedExp := int32(exp>>23) - 127 halfExp := unbiasedExp + 15 if halfExp >= 0x1f { return uint16(halfSign | uint32(0x7c00)) } if halfExp <= 0 { if 14-halfExp > 24 { return uint16(halfSign) } coef := coef | uint32(0x00800000) halfCoef := coef >> uint32(14-halfExp) roundBit := uint32(1) << uint32(13-halfExp) if (coef&roundBit) != 0 && (coef&(3*roundBit-1)) != 0 { halfCoef++ } return uint16(halfSign | halfCoef) } uHalfExp := uint32(halfExp) << 10 halfCoef := coef >> 13 roundBit := uint32(0x00001000) if (coef&roundBit) != 0 && (coef&(3*roundBit-1)) != 0 { return uint16((halfSign | uHalfExp | halfCoef) + 1) } return uint16(halfSign | uHalfExp | halfCoef) }