167 lines
4.2 KiB
Go
167 lines
4.2 KiB
Go
package socket
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
type lengthFieldSpec struct {
|
|
Offset int64 `toml:"offset"`
|
|
Bytes int64 `toml:"bytes"`
|
|
Endianness string `toml:"endianness"`
|
|
HeaderLength int64 `toml:"header_length"`
|
|
converter func([]byte) int
|
|
}
|
|
|
|
type SplitConfig struct {
|
|
SplittingStrategy string `toml:"splitting_strategy"`
|
|
SplittingDelimiter string `toml:"splitting_delimiter"`
|
|
SplittingLength int `toml:"splitting_length"`
|
|
SplittingLengthField lengthFieldSpec `toml:"splitting_length_field"`
|
|
}
|
|
|
|
func (cfg *SplitConfig) NewSplitter() (bufio.SplitFunc, error) {
|
|
switch cfg.SplittingStrategy {
|
|
case "", "newline":
|
|
return bufio.ScanLines, nil
|
|
case "null":
|
|
return scanNull, nil
|
|
case "delimiter":
|
|
re := regexp.MustCompile(`(\s*0?x)`)
|
|
d := re.ReplaceAllString(strings.ToLower(cfg.SplittingDelimiter), "")
|
|
delimiter, err := hex.DecodeString(d)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("decoding delimiter failed: %w", err)
|
|
}
|
|
return createScanDelimiter(delimiter), nil
|
|
case "fixed length":
|
|
return createScanFixedLength(cfg.SplittingLength), nil
|
|
case "variable length":
|
|
// Create the converter function
|
|
var order binary.ByteOrder
|
|
switch strings.ToLower(cfg.SplittingLengthField.Endianness) {
|
|
case "", "be":
|
|
order = binary.BigEndian
|
|
case "le":
|
|
order = binary.LittleEndian
|
|
default:
|
|
return nil, fmt.Errorf("invalid 'endianness' %q", cfg.SplittingLengthField.Endianness)
|
|
}
|
|
|
|
switch cfg.SplittingLengthField.Bytes {
|
|
case 1:
|
|
cfg.SplittingLengthField.converter = func(b []byte) int {
|
|
return int(b[0])
|
|
}
|
|
case 2:
|
|
cfg.SplittingLengthField.converter = func(b []byte) int {
|
|
return int(order.Uint16(b))
|
|
}
|
|
case 4:
|
|
cfg.SplittingLengthField.converter = func(b []byte) int {
|
|
return int(order.Uint32(b))
|
|
}
|
|
case 8:
|
|
cfg.SplittingLengthField.converter = func(b []byte) int {
|
|
return int(order.Uint64(b))
|
|
}
|
|
default:
|
|
cfg.SplittingLengthField.converter = func(b []byte) int {
|
|
buf := make([]byte, 8)
|
|
start := 0
|
|
if order == binary.BigEndian {
|
|
start = 8 - len(b)
|
|
}
|
|
for i := 0; i < len(b); i++ {
|
|
buf[start+i] = b[i]
|
|
}
|
|
return int(order.Uint64(buf))
|
|
}
|
|
}
|
|
|
|
// Check if we have enough bytes in the header
|
|
return createScanVariableLength(cfg.SplittingLengthField), nil
|
|
}
|
|
|
|
return nil, fmt.Errorf("unknown 'splitting_strategy' %q", cfg.SplittingStrategy)
|
|
}
|
|
|
|
func scanNull(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
|
if atEOF && len(data) == 0 {
|
|
return 0, nil, nil
|
|
}
|
|
if i := bytes.IndexByte(data, 0); i >= 0 {
|
|
return i + 1, data[:i], nil
|
|
}
|
|
if atEOF {
|
|
return len(data), data, nil
|
|
}
|
|
// Request more data.
|
|
return 0, nil, nil
|
|
}
|
|
|
|
func createScanDelimiter(delimiter []byte) bufio.SplitFunc {
|
|
return func(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
|
if atEOF && len(data) == 0 {
|
|
return 0, nil, nil
|
|
}
|
|
if i := bytes.Index(data, delimiter); i >= 0 {
|
|
return i + len(delimiter), data[:i], nil
|
|
}
|
|
if atEOF {
|
|
return len(data), data, nil
|
|
}
|
|
// Request more data.
|
|
return 0, nil, nil
|
|
}
|
|
}
|
|
|
|
func createScanFixedLength(length int) bufio.SplitFunc {
|
|
return func(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
|
if atEOF && len(data) == 0 {
|
|
return 0, nil, nil
|
|
}
|
|
if len(data) >= length {
|
|
return length, data[:length], nil
|
|
}
|
|
if atEOF {
|
|
return len(data), data, nil
|
|
}
|
|
// Request more data.
|
|
return 0, nil, nil
|
|
}
|
|
}
|
|
|
|
func createScanVariableLength(spec lengthFieldSpec) bufio.SplitFunc {
|
|
minlen := int(spec.Offset)
|
|
minlen += int(spec.Bytes)
|
|
headerLen := int(spec.HeaderLength)
|
|
|
|
return func(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
|
if atEOF && len(data) == 0 {
|
|
return 0, nil, nil
|
|
}
|
|
dataLen := len(data)
|
|
if dataLen >= minlen {
|
|
// Extract the length field and convert it to a number
|
|
lf := data[spec.Offset : spec.Offset+spec.Bytes]
|
|
length := spec.converter(lf)
|
|
start := headerLen
|
|
end := length + headerLen
|
|
// If we have enough data return it without the header
|
|
if end <= dataLen {
|
|
return end, data[start:end], nil
|
|
}
|
|
}
|
|
if atEOF {
|
|
return len(data), data, nil
|
|
}
|
|
// Request more data.
|
|
return 0, nil, nil
|
|
}
|
|
}
|