typed/buffer.go (275 lines of code) (raw):

// Copyright (c) 2015 Uber Technologies, Inc. // 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. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package typed import ( "encoding/binary" "errors" "io" ) var ( // ErrEOF is returned when trying to read past end of buffer ErrEOF = errors.New("buffer is too small") // ErrBufferFull is returned when trying to write past end of buffer ErrBufferFull = errors.New("no more room in buffer") // errStringTooLong is returned when writing a string with length larger // than the allows length limit. Intentionally not exported, in case we // want to add more context in future. errStringTooLong = errors.New("string is too long") ) // A ReadBuffer is a wrapper around an underlying []byte with methods to read from // that buffer in big-endian format. type ReadBuffer struct { initialLength int remaining []byte err error } // NewReadBuffer returns a ReadBuffer wrapping a byte slice func NewReadBuffer(buffer []byte) *ReadBuffer { return &ReadBuffer{initialLength: len(buffer), remaining: buffer} } // ReadSingleByte reads the next byte from the buffer func (r *ReadBuffer) ReadSingleByte() byte { b, _ := r.ReadByte() return b } // ReadByte returns the next byte from the buffer. // // This method implements the ByteReader interface. func (r *ReadBuffer) ReadByte() (byte, error) { if r.err != nil { return 0, r.err } if len(r.remaining) < 1 { r.err = ErrEOF return 0, r.err } b := r.remaining[0] r.remaining = r.remaining[1:] return b, nil } // ReadBytes returns the next n bytes from the buffer func (r *ReadBuffer) ReadBytes(n int) []byte { if r.err != nil { return nil } if len(r.remaining) < n { r.err = ErrEOF return nil } b := r.remaining[0:n] r.remaining = r.remaining[n:] return b } // SkipBytes skips the next n bytes from the buffer func (r *ReadBuffer) SkipBytes(n int) { if r.err != nil { return } if len(r.remaining) < n { r.err = ErrEOF return } r.remaining = r.remaining[n:] } // ReadString returns a string of size n from the buffer func (r *ReadBuffer) ReadString(n int) string { if b := r.ReadBytes(n); b != nil { // TODO(mmihic): This creates a copy, which sucks return string(b) } return "" } // ReadUint16 returns the next value in the buffer as a uint16 func (r *ReadBuffer) ReadUint16() uint16 { if b := r.ReadBytes(2); b != nil { return binary.BigEndian.Uint16(b) } return 0 } // ReadUint32 returns the next value in the buffer as a uint32 func (r *ReadBuffer) ReadUint32() uint32 { if b := r.ReadBytes(4); b != nil { return binary.BigEndian.Uint32(b) } return 0 } // ReadUint64 returns the next value in the buffer as a uint64 func (r *ReadBuffer) ReadUint64() uint64 { if b := r.ReadBytes(8); b != nil { return binary.BigEndian.Uint64(b) } return 0 } // ReadUvarint reads an unsigned varint from the buffer. func (r *ReadBuffer) ReadUvarint() uint64 { v, _ := binary.ReadUvarint(r) return v } // ReadLen8String reads an 8-bit length preceded string value func (r *ReadBuffer) ReadLen8String() string { n := r.ReadSingleByte() return r.ReadString(int(n)) } // ReadLen16String reads a 16-bit length preceded string value func (r *ReadBuffer) ReadLen16String() string { n := r.ReadUint16() return r.ReadString(int(n)) } // Remaining returns the unconsumed bytes. func (r *ReadBuffer) Remaining() []byte { return r.remaining } // BytesRemaining returns the length of Remaining. func (r *ReadBuffer) BytesRemaining() int { return len(r.Remaining()) } // BytesRead returns the number of bytes consumed func (r *ReadBuffer) BytesRead() int { return r.initialLength - len(r.remaining) } // Wrap initializes the buffer to read from the given byte slice func (r *ReadBuffer) Wrap(b []byte) { r.initialLength = len(b) r.remaining = b r.err = nil } // Err returns the error in the ReadBuffer func (r *ReadBuffer) Err() error { return r.err } // A WriteBuffer is a wrapper around an underlying []byte with methods to write to // that buffer in big-endian format. The buffer is of fixed size, and does not grow. type WriteBuffer struct { buffer []byte remaining []byte err error } // NewWriteBuffer creates a WriteBuffer wrapping the given slice func NewWriteBuffer(buffer []byte) *WriteBuffer { return &WriteBuffer{buffer: buffer, remaining: buffer} } // NewWriteBufferWithSize create a new WriteBuffer using an internal buffer of the given size func NewWriteBufferWithSize(size int) *WriteBuffer { return NewWriteBuffer(make([]byte, size)) } // WriteSingleByte writes a single byte to the buffer func (w *WriteBuffer) WriteSingleByte(n byte) { if w.err != nil { return } if len(w.remaining) == 0 { w.setErr(ErrBufferFull) return } w.remaining[0] = n w.remaining = w.remaining[1:] } // WriteBytes writes a slice of bytes to the buffer func (w *WriteBuffer) WriteBytes(in []byte) { if b := w.reserve(len(in)); b != nil { copy(b, in) } } // WriteUint16 writes a big endian encoded uint16 value to the buffer func (w *WriteBuffer) WriteUint16(n uint16) { if b := w.reserve(2); b != nil { binary.BigEndian.PutUint16(b, n) } } // WriteUint32 writes a big endian uint32 value to the buffer func (w *WriteBuffer) WriteUint32(n uint32) { if b := w.reserve(4); b != nil { binary.BigEndian.PutUint32(b, n) } } // WriteUint64 writes a big endian uint64 to the buffer func (w *WriteBuffer) WriteUint64(n uint64) { if b := w.reserve(8); b != nil { binary.BigEndian.PutUint64(b, n) } } // WriteUvarint writes an unsigned varint to the buffer func (w *WriteBuffer) WriteUvarint(n uint64) { // A uvarint could be up to 10 bytes long. buf := make([]byte, 10) varBytes := binary.PutUvarint(buf, n) if b := w.reserve(varBytes); b != nil { copy(b, buf[0:varBytes]) } } // WriteString writes a string to the buffer func (w *WriteBuffer) WriteString(s string) { // NB(mmihic): Don't just call WriteBytes; that will make a double copy // of the string due to the cast if b := w.reserve(len(s)); b != nil { copy(b, s) } } // WriteLen8String writes an 8-bit length preceded string func (w *WriteBuffer) WriteLen8String(s string) { if int(byte(len(s))) != len(s) { w.setErr(errStringTooLong) } w.WriteSingleByte(byte(len(s))) w.WriteString(s) } // WriteLen16String writes a 16-bit length preceded string func (w *WriteBuffer) WriteLen16String(s string) { if int(uint16(len(s))) != len(s) { w.setErr(errStringTooLong) } w.WriteUint16(uint16(len(s))) w.WriteString(s) } // DeferByte reserves space in the buffer for a single byte, and returns a // reference that can be used to update that byte later func (w *WriteBuffer) DeferByte() ByteRef { if len(w.remaining) == 0 { w.setErr(ErrBufferFull) return ByteRef(nil) } // Always zero out references, since the caller expects the default to be 0. w.remaining[0] = 0 bufRef := ByteRef(w.remaining[0:]) w.remaining = w.remaining[1:] return bufRef } // DeferUint16 reserves space in the buffer for a uint16, and returns a // reference that can be used to update that uint16 func (w *WriteBuffer) DeferUint16() Uint16Ref { return Uint16Ref(w.deferred(2)) } // DeferUint32 reserves space in the buffer for a uint32, and returns a // reference that can be used to update that uint32 func (w *WriteBuffer) DeferUint32() Uint32Ref { return Uint32Ref(w.deferred(4)) } // DeferUint64 reserves space in the buffer for a uint64, and returns a // reference that can be used to update that uint64 func (w *WriteBuffer) DeferUint64() Uint64Ref { return Uint64Ref(w.deferred(8)) } // DeferBytes reserves space in the buffer for a fixed sequence of bytes, and // returns a reference that can be used to update those bytes func (w *WriteBuffer) DeferBytes(n int) BytesRef { return BytesRef(w.deferred(n)) } func (w *WriteBuffer) deferred(n int) []byte { bs := w.reserve(n) for i := range bs { bs[i] = 0 } return bs } func (w *WriteBuffer) reserve(n int) []byte { if w.err != nil { return nil } if len(w.remaining) < n { w.setErr(ErrBufferFull) return nil } b := w.remaining[0:n] w.remaining = w.remaining[n:] return b } // BytesRemaining returns the number of available bytes remaining in the bufffer func (w *WriteBuffer) BytesRemaining() int { return len(w.remaining) } // FlushTo flushes the written buffer to the given writer. func (w *WriteBuffer) FlushTo(iow io.Writer) (int, error) { dirty := w.buffer[0:w.BytesWritten()] return iow.Write(dirty) } // BytesWritten returns the number of bytes that have been written to the buffer func (w *WriteBuffer) BytesWritten() int { return len(w.buffer) - len(w.remaining) } // Reset resets the buffer to an empty state, ready for writing func (w *WriteBuffer) Reset() { w.remaining = w.buffer w.err = nil } func (w *WriteBuffer) setErr(err error) { // Only store the first error if w.err != nil { return } w.err = err } // Err returns the current error in the buffer func (w *WriteBuffer) Err() error { return w.err } // Wrap initializes the buffer to wrap the given byte slice func (w *WriteBuffer) Wrap(b []byte) { w.buffer = b w.remaining = b } // A ByteRef is a reference to a byte in a bufffer type ByteRef []byte // Update updates the byte in the buffer func (ref ByteRef) Update(b byte) { if ref != nil { ref[0] = b } } // A Uint16Ref is a reference to a uint16 placeholder in a buffer type Uint16Ref []byte // Update updates the uint16 in the buffer func (ref Uint16Ref) Update(n uint16) { if ref != nil { binary.BigEndian.PutUint16(ref, n) } } // A Uint32Ref is a reference to a uint32 placeholder in a buffer type Uint32Ref []byte // Update updates the uint32 in the buffer func (ref Uint32Ref) Update(n uint32) { if ref != nil { binary.BigEndian.PutUint32(ref, n) } } // A Uint64Ref is a reference to a uin64 placeholder in a buffer type Uint64Ref []byte // Update updates the uint64 in the buffer func (ref Uint64Ref) Update(n uint64) { if ref != nil { binary.BigEndian.PutUint64(ref, n) } } // A BytesRef is a reference to a multi-byte placeholder in a buffer type BytesRef []byte // Update updates the bytes in the buffer func (ref BytesRef) Update(b []byte) { if ref != nil { copy(ref, b) } } // UpdateString updates the bytes in the buffer from a string func (ref BytesRef) UpdateString(s string) { if ref != nil { copy(ref, s) } }