Lindenii Project Forge
bare: Zero-copy []byte and string conversions
// SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright (c) 2025 Drew Devault <https://drewdevault.com> package bare import ( "encoding/binary" "fmt" "io" "math" "unicode/utf8"
"go.lindenii.runxiyu.org/forge/forged/internal/misc"
) type byteReader interface { io.Reader io.ByteReader } // A Reader for BARE primitive types. type Reader struct { base byteReader scratch [8]byte } type simpleByteReader struct { io.Reader scratch [1]byte } func (r simpleByteReader) ReadByte() (byte, error) { // using reference type here saves us allocations _, err := r.Read(r.scratch[:]) return r.scratch[0], err } // Returns a new BARE primitive reader wrapping the given io.Reader. func NewReader(base io.Reader) *Reader { br, ok := base.(byteReader) if !ok { br = simpleByteReader{Reader: base} } return &Reader{base: br} } func (r *Reader) ReadUint() (uint64, error) { x, err := binary.ReadUvarint(r.base) if err != nil { return x, err } return x, nil } func (r *Reader) ReadU8() (uint8, error) { return r.base.ReadByte() } func (r *Reader) ReadU16() (uint16, error) { var i uint16 if _, err := io.ReadAtLeast(r.base, r.scratch[:2], 2); err != nil { return i, err } return binary.LittleEndian.Uint16(r.scratch[:]), nil } func (r *Reader) ReadU32() (uint32, error) { var i uint32 if _, err := io.ReadAtLeast(r.base, r.scratch[:4], 4); err != nil { return i, err } return binary.LittleEndian.Uint32(r.scratch[:]), nil } func (r *Reader) ReadU64() (uint64, error) { var i uint64 if _, err := io.ReadAtLeast(r.base, r.scratch[:8], 8); err != nil { return i, err } return binary.LittleEndian.Uint64(r.scratch[:]), nil } func (r *Reader) ReadInt() (int64, error) { return binary.ReadVarint(r.base) } func (r *Reader) ReadI8() (int8, error) { b, err := r.base.ReadByte() return int8(b), err } func (r *Reader) ReadI16() (int16, error) { var i int16 if _, err := io.ReadAtLeast(r.base, r.scratch[:2], 2); err != nil { return i, err } return int16(binary.LittleEndian.Uint16(r.scratch[:])), nil } func (r *Reader) ReadI32() (int32, error) { var i int32 if _, err := io.ReadAtLeast(r.base, r.scratch[:4], 4); err != nil { return i, err } return int32(binary.LittleEndian.Uint32(r.scratch[:])), nil } func (r *Reader) ReadI64() (int64, error) { var i int64 if _, err := io.ReadAtLeast(r.base, r.scratch[:], 8); err != nil { return i, err } return int64(binary.LittleEndian.Uint64(r.scratch[:])), nil } func (r *Reader) ReadF32() (float32, error) { u, err := r.ReadU32() f := math.Float32frombits(u) if math.IsNaN(float64(f)) { return 0.0, fmt.Errorf("NaN is not permitted in BARE floats") } return f, err } func (r *Reader) ReadF64() (float64, error) { u, err := r.ReadU64() f := math.Float64frombits(u) if math.IsNaN(f) { return 0.0, fmt.Errorf("NaN is not permitted in BARE floats") } return f, err } func (r *Reader) ReadBool() (bool, error) { b, err := r.ReadU8() if err != nil { return false, err } if b > 1 { return false, fmt.Errorf("Invalid bool value: %#x", b) } return b == 1, nil } func (r *Reader) ReadString() (string, error) { buf, err := r.ReadData() if err != nil { return "", err } if !utf8.Valid(buf) { return "", ErrInvalidStr }
return string(buf), nil
return misc.BytesToString(buf), nil
} // Reads a fixed amount of arbitrary data, defined by the length of the slice. func (r *Reader) ReadDataFixed(dest []byte) error { var amt int = 0 for amt < len(dest) { n, err := r.base.Read(dest[amt:]) if err != nil { return err } amt += n } return nil } // Reads arbitrary data whose length is read from the message. func (r *Reader) ReadData() ([]byte, error) { l, err := r.ReadUint() if err != nil { return nil, err } if l >= maxUnmarshalBytes { return nil, ErrLimitExceeded } buf := make([]byte, l) var amt uint64 = 0 for amt < l { n, err := r.base.Read(buf[amt:]) if err != nil { return nil, err } amt += uint64(n) } return buf, nil }
// SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright (c) 2025 Drew Devault <https://drewdevault.com> package bare import ( "encoding/binary" "fmt" "io" "math"
"go.lindenii.runxiyu.org/forge/forged/internal/misc"
) // A Writer for BARE primitive types. type Writer struct { base io.Writer scratch [binary.MaxVarintLen64]byte } // Returns a new BARE primitive writer wrapping the given io.Writer. func NewWriter(base io.Writer) *Writer { return &Writer{base: base} } func (w *Writer) WriteUint(i uint64) error { n := binary.PutUvarint(w.scratch[:], i) _, err := w.base.Write(w.scratch[:n]) return err } func (w *Writer) WriteU8(i uint8) error { return binary.Write(w.base, binary.LittleEndian, i) } func (w *Writer) WriteU16(i uint16) error { return binary.Write(w.base, binary.LittleEndian, i) } func (w *Writer) WriteU32(i uint32) error { return binary.Write(w.base, binary.LittleEndian, i) } func (w *Writer) WriteU64(i uint64) error { return binary.Write(w.base, binary.LittleEndian, i) } func (w *Writer) WriteInt(i int64) error { var buf [binary.MaxVarintLen64]byte n := binary.PutVarint(buf[:], i) _, err := w.base.Write(buf[:n]) return err } func (w *Writer) WriteI8(i int8) error { return binary.Write(w.base, binary.LittleEndian, i) } func (w *Writer) WriteI16(i int16) error { return binary.Write(w.base, binary.LittleEndian, i) } func (w *Writer) WriteI32(i int32) error { return binary.Write(w.base, binary.LittleEndian, i) } func (w *Writer) WriteI64(i int64) error { return binary.Write(w.base, binary.LittleEndian, i) } func (w *Writer) WriteF32(f float32) error { if math.IsNaN(float64(f)) { return fmt.Errorf("NaN is not permitted in BARE floats") } return binary.Write(w.base, binary.LittleEndian, f) } func (w *Writer) WriteF64(f float64) error { if math.IsNaN(f) { return fmt.Errorf("NaN is not permitted in BARE floats") } return binary.Write(w.base, binary.LittleEndian, f) } func (w *Writer) WriteBool(b bool) error { return binary.Write(w.base, binary.LittleEndian, b) } func (w *Writer) WriteString(str string) error {
return w.WriteData([]byte(str))
return w.WriteData(misc.StringToBytes(str))
} // Writes a fixed amount of arbitrary data, defined by the length of the slice. func (w *Writer) WriteDataFixed(data []byte) error { var amt int = 0 for amt < len(data) { n, err := w.base.Write(data[amt:]) if err != nil { return err } amt += n } return nil } // Writes arbitrary data whose length is encoded into the message. func (w *Writer) WriteData(data []byte) error { err := w.WriteUint(uint64(len(data))) if err != nil { return err } var amt int = 0 for amt < len(data) { n, err := w.base.Write(data[amt:]) if err != nil { return err } amt += n } return nil }