251 lines
5.3 KiB
Go
251 lines
5.3 KiB
Go
// 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
|
|
|
|
// ASCII parsing support
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
)
|
|
|
|
var ErrExpectedDigit = errors.New("Expected digit")
|
|
|
|
// UntilCRLF collects all bytes until a CRLF ("\r\n") sequence is found. The
|
|
// returned byte slice will not contain the CRLF sequence.
|
|
// If CRLF was not found yet one of ErrNoMoreBytes or ErrUnexpectedEOB will be
|
|
// reported.
|
|
func (b *Buffer) UntilCRLF() ([]byte, error) {
|
|
if b.err != nil {
|
|
return nil, b.err
|
|
}
|
|
|
|
data := b.data[b.offset:]
|
|
for i, byte := range data {
|
|
if byte != '\r' {
|
|
continue
|
|
}
|
|
|
|
if len(data) < i+2 {
|
|
b.offset += i
|
|
return nil, b.bufferEndError()
|
|
}
|
|
|
|
if data[i+1] != '\n' {
|
|
// false alarm, continue
|
|
continue
|
|
}
|
|
|
|
// yay, found
|
|
end := i
|
|
data = b.data[b.mark : b.offset+end]
|
|
b.Advance(len(data) + 2)
|
|
return data, nil
|
|
}
|
|
|
|
b.offset += len(data)
|
|
return nil, b.bufferEndError()
|
|
}
|
|
|
|
// IgnoreSymbol will advance the read pointer until the first symbol not
|
|
// matching s is found.
|
|
func (b *Buffer) IgnoreSymbol(s uint8) error {
|
|
if b.err != nil {
|
|
return b.err
|
|
}
|
|
|
|
data := b.data[b.offset:]
|
|
for i, byte := range data {
|
|
if byte != s {
|
|
b.Advance(b.offset + i - b.mark)
|
|
return nil
|
|
}
|
|
}
|
|
b.offset += len(data)
|
|
return b.bufferEndError()
|
|
}
|
|
|
|
// IgnoreSymbols will advance the read pointer until the first symbol not matching
|
|
// set of symbols is found
|
|
func (b *Buffer) IgnoreSymbols(syms []byte) error {
|
|
if b.err != nil {
|
|
return b.err
|
|
}
|
|
|
|
data := b.data[b.offset:]
|
|
for i, byte := range data {
|
|
for _, other := range syms {
|
|
if byte == other {
|
|
goto next
|
|
}
|
|
}
|
|
// no match
|
|
b.Advance(b.offset + i - b.mark)
|
|
return nil
|
|
|
|
next:
|
|
}
|
|
b.offset += len(data)
|
|
return b.bufferEndError()
|
|
}
|
|
|
|
// UntilSymbol collects all bytes until symbol s is found. If errOnEnd is set to
|
|
// true, the collected byte slice will be returned if no more bytes are available
|
|
// for parsing, but s has not matched yet.
|
|
func (b *Buffer) UntilSymbol(s uint8, errOnEnd bool) ([]byte, error) {
|
|
if b.err != nil {
|
|
return nil, b.err
|
|
}
|
|
|
|
data := b.data[b.offset:]
|
|
for i, byte := range data {
|
|
if byte == s {
|
|
data := b.data[b.mark : b.offset+i]
|
|
b.Advance(len(data))
|
|
return data, nil
|
|
}
|
|
}
|
|
|
|
if errOnEnd {
|
|
b.offset += len(data)
|
|
return nil, b.bufferEndError()
|
|
}
|
|
|
|
data = b.data[b.mark:]
|
|
b.Advance(len(data))
|
|
return data, nil
|
|
}
|
|
|
|
// UintASCII will parse unsigned number from Buffer.
|
|
func (b *Buffer) UintASCII(errOnEnd bool) (uint64, error) {
|
|
if b.err != nil {
|
|
return 0, b.err
|
|
}
|
|
if len(b.data) <= b.mark { // end of buffer
|
|
return 0, b.bufferEndError()
|
|
}
|
|
|
|
end, err := b.asciiFindNumberEnd(b.offset, errOnEnd)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// parse value
|
|
value, err := doParseNumber(b.data[b.mark:end])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
b.Advance(end - b.mark)
|
|
return value, nil
|
|
}
|
|
|
|
// IntASCII will parse (optionally) signed number from Buffer.
|
|
func (b *Buffer) IntASCII(errOnEnd bool) (int64, error) {
|
|
if b.err != nil {
|
|
return 0, b.err
|
|
}
|
|
if len(b.data) <= b.mark { // end of buffer
|
|
return 0, b.bufferEndError()
|
|
}
|
|
|
|
// check signedness of number
|
|
signed := b.data[b.mark] == '-'
|
|
start := b.mark
|
|
if signed {
|
|
start++
|
|
if len(b.data) <= start {
|
|
return 0, b.bufferEndError()
|
|
}
|
|
} else if b.data[b.mark] == '+' {
|
|
start++
|
|
if len(b.data) <= start {
|
|
return 0, b.bufferEndError()
|
|
}
|
|
}
|
|
|
|
// adapt offset to point to start of number
|
|
offset := b.offset
|
|
if b.offset == b.mark {
|
|
offset = start
|
|
}
|
|
|
|
end, err := b.asciiFindNumberEnd(offset, errOnEnd)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
value, err := doParseNumber(b.data[start:end])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
b.Advance(end - b.mark)
|
|
if signed {
|
|
return -int64(value), nil
|
|
}
|
|
return int64(value), nil
|
|
}
|
|
|
|
// MatchASCII checks the Buffer it's next byte sequence matched prefix. The
|
|
// read pointer is not advanced by AsciiPrefix.
|
|
func (b *Buffer) MatchASCII(prefix []byte) (bool, error) {
|
|
if b.err != nil {
|
|
return false, b.err
|
|
}
|
|
if !b.Avail(len(prefix)) {
|
|
return false, b.bufferEndError()
|
|
}
|
|
|
|
has := bytes.HasPrefix(b.data[b.mark:], prefix)
|
|
return has, nil
|
|
}
|
|
|
|
func (b *Buffer) asciiFindNumberEnd(start int, errOnEnd bool) (int, error) {
|
|
// find end of number
|
|
end := -1
|
|
for i, byte := range b.data[start:] {
|
|
if byte < '0' || '9' < byte {
|
|
end = i + start
|
|
break
|
|
}
|
|
}
|
|
|
|
// check end
|
|
if end < 0 {
|
|
if errOnEnd {
|
|
return -1, b.bufferEndError()
|
|
}
|
|
end = len(b.data)
|
|
}
|
|
|
|
return end, nil
|
|
}
|
|
|
|
func doParseNumber(buf []byte) (uint64, error) {
|
|
if len(buf) == 0 {
|
|
return 0, ErrExpectedDigit
|
|
}
|
|
|
|
var value uint64
|
|
for _, byte := range buf {
|
|
value = uint64(byte-'0') + 10*value
|
|
}
|
|
return value, nil
|
|
}
|
|
|
|
// binary parsing support
|