youtubebeat/vendor/github.com/elastic/beats/libbeat/common/streambuf/streambuf.go

499 lines
13 KiB
Go
Raw Normal View History

2018-11-18 11:08:38 +01:00
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
// Package streambuf provides helpers for buffering multiple packet payloads
// and some general parsing functions. All parsing functions are re-entrant,
// that is if a parse function fails due do not having buffered enough bytes yet
// (error value ErrNoMoreBytes) the parser can be called again after appending more
// bytes to the buffer. Parsers potentially reading large amount of bytes might
// remember the last position.
// Additionally a Buffer can be marked as fixed. Fixed buffers to not support
// adding new data, plus ErrNoMoreBytes will never be returned. Instead if a parser
// decides it need more bytes ErrUnexpectedEOB will be returned.
//
// Error handling:
// All functions that might fail, will return an error. The last error reported
// will be stored with the buffer itself. Instead of checking every single error
// one can use the Failed() and Err() methods to check if the buffer is still in a
// valid state and all parsing was successful.
package streambuf
import (
"github.com/elastic/beats/libbeat/logp"
"bytes"
"errors"
)
// Error returned if Append or Write operation is not allowed due to the buffer
// being fixed
var ErrOperationNotAllowed = errors.New("Operation not allowed")
var ErrOutOfRange = errors.New("Data access out of range")
// Parse operation can not be continued. More bytes required. Only returned if
// buffer is not fixed
var ErrNoMoreBytes = errors.New("No more bytes")
// Parse operation failed cause of buffer snapped short + buffer is fixed.
var ErrUnexpectedEOB = errors.New("unexpected end of buffer")
var ErrExpectedByteSequenceMismatch = errors.New("expected byte sequence did not match")
// A Buffer is a variable sized buffer of bytes with Read, Write and simple
// parsing methods. The zero value is an empty buffer ready for use.
//
// A Buffer can be marked as fixed. In this case no data can be appended to the
// buffer anymore and parser/reader methods will fail with ErrUnexpectedEOB if they
// would expect more bytes to come. Mark buffers fixed if some slice was separated
// for further parsing first.
type Buffer struct {
data []byte
err error
fixed bool
// Internal parser state offsets.
// Offset is the position a parse might continue to work at when called
// again (e.g. useful for parsing tcp streams.). The mark is used to remember
// the position last parse operation ended at. The variable available is used
// for faster lookup
// Invariants:
// (1) 0 <= mark <= offset
// (2) 0 <= available <= len(data)
// (3) available = len(data) - mark
mark, offset, available int
}
// Init initializes a zero buffer with some byte slice being retained by the
// buffer. Usage of Init is optional as zero value Buffer is already in valid state.
func (b *Buffer) Init(d []byte, fixed bool) {
b.data = d
b.err = nil
b.fixed = fixed
b.mark = 0
b.offset = 0
b.available = len(d)
}
// New creates new extensible buffer from data slice being retained by the buffer.
func New(data []byte) *Buffer {
return &Buffer{
data: data,
fixed: false,
available: len(data),
}
}
// NewFixed create new fixed buffer from data slice being retained by the buffer.
func NewFixed(data []byte) *Buffer {
return &Buffer{
data: data,
fixed: true,
available: len(data),
}
}
// Snapshot creates a snapshot of buffers current state. Use in conjunction with
// Restore to get simple backtracking support. Between Snapshot and Restore the
// Reset method MUST not be called, as restored buffer will be in invalid state
// after.
func (b *Buffer) Snapshot() *Buffer {
tmp := *b
return &tmp
}
// Restore restores a buffers state. Use in conjunction with
// Snapshot to get simple backtracking support. Between Snapshot and Restore the
// Reset method MUST not be called, as restored buffer will be in invalid state
// after.
func (b *Buffer) Restore(snapshot *Buffer) {
b.err = snapshot.err
b.fixed = snapshot.fixed
b.mark = snapshot.mark
b.offset = snapshot.offset
b.available = snapshot.available
}
func (b *Buffer) doAppend(data []byte, retainable bool, newCap int) error {
if b.fixed {
return b.SetError(ErrOperationNotAllowed)
}
if b.err != nil && b.err != ErrNoMoreBytes {
return b.err
}
if len(b.data) == 0 {
retain := retainable && cap(data) > newCap
if retain {
b.data = data
} else {
if newCap < len(data) {
b.data = make([]byte, len(data))
} else {
b.data = make([]byte, len(data), newCap)
}
copy(b.data, data)
}
} else {
if newCap > 0 && cap(b.data[b.offset:]) < len(data) {
required := cap(b.data) + len(data)
if required < newCap {
tmp := make([]byte, len(b.data), newCap)
copy(tmp, b.data)
b.data = tmp
}
}
b.data = append(b.data, data...)
}
b.available += len(data)
// reset error status (continue parsing)
if b.err == ErrNoMoreBytes {
b.err = nil
}
return nil
}
// Append will append the given data to the buffer. If Buffer is fixed
// ErrOperationNotAllowed will be returned.
func (b *Buffer) Append(data []byte) error {
return b.doAppend(data, true, -1)
}
func (b *Buffer) AppendWithCapLimits(data []byte, newCap int) error {
return b.doAppend(data, true, newCap)
}
// Fix marks a buffer as fixed. No more data can be added to the buffer and
// parse operation might fail if they expect more bytes.
func (b *Buffer) Fix() {
b.fixed = true
}
// Total returns the total number of bytes stored in the buffer
func (b *Buffer) Total() int {
return len(b.data)
}
// Avail checks if count bytes are available for reading from the buffer.
func (b *Buffer) Avail(count int) bool {
return count <= b.available
}
// Len returns the number of bytes of the unread portion.
func (b *Buffer) Len() int {
return b.available
}
// Cap returns the buffer capacity until new memory must be allocated
func (b *Buffer) Cap() int {
return cap(b.data)
}
// LeftBehind returns the number of bytes a re-entrant but not yet finished
// parser did already read.
func (b *Buffer) LeftBehind() int {
return b.offset - b.mark
}
// BufferConsumed returns the number of bytes already consumed since last call to Reset.
func (b *Buffer) BufferConsumed() int {
return b.mark
}
// Advance will advance the buffers read pointer by count bytes. Returns
// ErrNoMoreBytes or ErrUnexpectedEOB if count bytes are not available.
func (b *Buffer) Advance(count int) error {
if !b.Avail(count) {
return b.bufferEndError()
}
b.mark += count
b.offset = b.mark
b.available -= count
return nil
}
// Failed returns true if buffer is in failed state. If buffer is in failed
// state, almost all buffer operations will fail
func (b *Buffer) Failed() bool {
failed := b.err != nil
if failed {
logp.Debug("streambuf", "buf parser already failed with: %s", b.err)
}
return failed
}
// Err returns the error value of the last failed operation.
func (b *Buffer) Err() error {
return b.err
}
// Check if n bytes are addressable in the buffer offset at b.mark and
// increases either the length or allocates bigger slice if necessary
func (b *Buffer) ensureLen(n int) {
delta := n - b.available
if delta <= 0 {
// no additional space required:
return
}
// newly available bytes
b.available += delta
total := len(b.data) + delta
if total <= cap(b.data) {
// enough space in slice -> grow it
b.data = b.data[0:total]
return
}
tmp := make([]byte, total)
copy(tmp, b.data)
b.data = tmp
}
// return slice to write to starting at off + b.mark with given length.
func (b *Buffer) sliceAt(off, len int) []byte {
off += b.mark
end := off + len
b.ensureLen(end - b.mark)
return b.data[off:end]
}
// Consume removes the first n bytes (special variant of Reset) from the
// beginning of the buffer, if at least n bytes have already been processed.
// Returns the byte slice of all bytes being removed from the buffer.
// If total buffer is < n, ErrOutOfRange will be reported or ErrOutOfRange if
// not enough bytes have been processed yet.
func (b *Buffer) Consume(n int) ([]byte, error) {
if n > len(b.data) {
return nil, ErrOutOfRange
}
newMark := b.mark - n
if newMark < 0 {
return nil, ErrOutOfRange
}
old := b.data[:n]
b.data = b.data[n:]
b.mark = newMark
b.offset -= n
b.available = len(b.data) - b.mark
return old, nil
}
// Reset remove all bytes already processed from the buffer. Use Reset after
// processing message to limit amount of buffered data.
func (b *Buffer) Reset() {
b.data = b.data[b.mark:]
b.offset -= b.mark
b.mark = 0
b.available = len(b.data)
b.err = nil
}
// BufferedBytes returns all buffered bytes since last reset.
func (b *Buffer) BufferedBytes() []byte {
return b.data
}
// Bytes returns all bytes not yet processed. The read counters are not advanced
// yet. For example use with fixed Buffer for simple lookahead.
//
// Note:
// The read markers are not advanced. If rest of buffer should be
// processed, call Advance immediately.
func (b *Buffer) Bytes() []byte {
return b.data[b.mark:]
}
func (b *Buffer) bufferEndError() error {
if b.fixed {
return b.SetError(ErrUnexpectedEOB)
}
return b.SetError(ErrNoMoreBytes)
}
// SetError marks a buffer as failed. Append and parse operations will fail with
// err. SetError returns err directly.
func (b *Buffer) SetError(err error) error {
b.err = err
return err
}
// Collect tries to collect count bytes from the buffer and updates the read
// pointers. If the buffer is in failed state or count bytes are not available
// an error will be returned.
func (b *Buffer) Collect(count int) ([]byte, error) {
if b.err != nil {
return nil, b.err
}
if !b.Avail(count) {
return nil, b.bufferEndError()
}
data := b.data[b.mark : b.mark+count]
b.Advance(count)
return data, nil
}
// CollectWithSuffix collects count bytes and checks delim will immediately
// follow the byte sequence. Returns count bytes without delim.
// If delim is not matched ErrExpectedByteSequenceMismatch will be raised.
func (b *Buffer) CollectWithSuffix(count int, delim []byte) ([]byte, error) {
total := count + len(delim)
if b.err != nil {
return nil, b.err
}
if !b.Avail(total) {
return nil, b.bufferEndError()
}
end := b.mark + count
if !bytes.HasPrefix(b.data[end:], delim) {
return nil, b.SetError(ErrExpectedByteSequenceMismatch)
}
data := b.data[b.mark : b.mark+count]
b.Advance(total)
return data, nil
}
// Index returns offset of seq in unprocessed buffer.
// Returns -1 if seq can not be found.
func (b *Buffer) Index(seq []byte) int {
return b.IndexFrom(0, seq)
}
// IndexFrom returns offset of seq in unprocessed buffer start at from.
// Returns -1 if seq can not be found.
func (b *Buffer) IndexFrom(from int, seq []byte) int {
if b.err != nil {
return -1
}
idx := bytes.Index(b.data[b.mark+from:], seq)
if idx < 0 {
return -1
}
return idx + from + b.mark
}
// IndexByte returns offset of byte in unprocessed buffer.
// Returns -1 if byte not in buffer.
func (b *Buffer) IndexByte(byte byte) int {
if b.err != nil {
return -1
}
idx := bytes.IndexByte(b.data[b.offset:], byte)
if idx < 0 {
return -1
}
return idx + (b.offset - b.mark)
}
// IndexByteFrom returns offset of byte in unpressed buffer starting at off.
// Returns -1 if byte not in buffer
func (b *Buffer) IndexByteFrom(off int, byte byte) int {
if b.err != nil {
return -1
}
idx := bytes.IndexByte(b.data[b.offset+off:], byte)
if idx < 0 {
return -1
}
return idx + (b.offset - b.mark) + off
}
// CollectUntil collects all bytes until delim was found (including delim).
func (b *Buffer) CollectUntil(delim []byte) ([]byte, error) {
if b.err != nil {
return nil, b.err
}
idx := bytes.Index(b.data[b.mark:], delim)
if idx < 0 {
return nil, b.bufferEndError()
}
end := b.mark + idx + len(delim)
data := b.data[b.mark:end]
b.Advance(len(data))
return data, nil
}
// CollectUntilByte collects all bytes until delim was found (including delim).
func (b *Buffer) CollectUntilByte(delim byte) ([]byte, error) {
if b.err != nil {
return nil, b.err
}
idx := bytes.IndexByte(b.data[b.offset:], delim)
if idx < 0 {
b.offset = b.mark + b.available
return nil, b.bufferEndError()
}
end := b.offset + idx + 1
data := b.data[b.mark:end]
b.Advance(len(data))
return data, nil
}
// CollectWhile collects all bytes until predicate returns false
func (b *Buffer) CollectWhile(pred func(byte) bool) ([]byte, error) {
if b.err != nil {
return nil, b.err
}
data := b.data[b.offset:]
for i, byte := range data {
if !pred(byte) {
end := b.offset + i + 1
data := b.data[b.mark:end]
b.Advance(len(data))
return data, nil
}
}
b.offset = b.mark + b.available
return nil, b.bufferEndError()
}
func (b *Buffer) PeekByte() (byte, error) {
return b.PeekByteFrom(0)
}
func (b *Buffer) PeekByteFrom(off int) (byte, error) {
if b.err != nil {
return 0, b.err
}
if !b.Avail(off + 1) {
return 0, b.bufferEndError()
}
return b.data[b.mark+off], nil
}