| package sarama |
| |
| import ( |
| "encoding/binary" |
| "math" |
| ) |
| |
| var errInvalidArrayLength = PacketDecodingError{"invalid array length"} |
| var errInvalidByteSliceLength = PacketDecodingError{"invalid byteslice length"} |
| var errInvalidByteSliceLengthType = PacketDecodingError{"invalid byteslice length type"} |
| var errInvalidStringLength = PacketDecodingError{"invalid string length"} |
| var errInvalidSubsetSize = PacketDecodingError{"invalid subset size"} |
| var errVarintOverflow = PacketDecodingError{"varint overflow"} |
| var errInvalidBool = PacketDecodingError{"invalid bool"} |
| |
| type realDecoder struct { |
| raw []byte |
| off int |
| stack []pushDecoder |
| } |
| |
| // primitives |
| |
| func (rd *realDecoder) getInt8() (int8, error) { |
| if rd.remaining() < 1 { |
| rd.off = len(rd.raw) |
| return -1, ErrInsufficientData |
| } |
| tmp := int8(rd.raw[rd.off]) |
| rd.off++ |
| return tmp, nil |
| } |
| |
| func (rd *realDecoder) getInt16() (int16, error) { |
| if rd.remaining() < 2 { |
| rd.off = len(rd.raw) |
| return -1, ErrInsufficientData |
| } |
| tmp := int16(binary.BigEndian.Uint16(rd.raw[rd.off:])) |
| rd.off += 2 |
| return tmp, nil |
| } |
| |
| func (rd *realDecoder) getInt32() (int32, error) { |
| if rd.remaining() < 4 { |
| rd.off = len(rd.raw) |
| return -1, ErrInsufficientData |
| } |
| tmp := int32(binary.BigEndian.Uint32(rd.raw[rd.off:])) |
| rd.off += 4 |
| return tmp, nil |
| } |
| |
| func (rd *realDecoder) getInt64() (int64, error) { |
| if rd.remaining() < 8 { |
| rd.off = len(rd.raw) |
| return -1, ErrInsufficientData |
| } |
| tmp := int64(binary.BigEndian.Uint64(rd.raw[rd.off:])) |
| rd.off += 8 |
| return tmp, nil |
| } |
| |
| func (rd *realDecoder) getVarint() (int64, error) { |
| tmp, n := binary.Varint(rd.raw[rd.off:]) |
| if n == 0 { |
| rd.off = len(rd.raw) |
| return -1, ErrInsufficientData |
| } |
| if n < 0 { |
| rd.off -= n |
| return -1, errVarintOverflow |
| } |
| rd.off += n |
| return tmp, nil |
| } |
| |
| func (rd *realDecoder) getArrayLength() (int, error) { |
| if rd.remaining() < 4 { |
| rd.off = len(rd.raw) |
| return -1, ErrInsufficientData |
| } |
| tmp := int(int32(binary.BigEndian.Uint32(rd.raw[rd.off:]))) |
| rd.off += 4 |
| if tmp > rd.remaining() { |
| rd.off = len(rd.raw) |
| return -1, ErrInsufficientData |
| } else if tmp > 2*math.MaxUint16 { |
| return -1, errInvalidArrayLength |
| } |
| return tmp, nil |
| } |
| |
| func (rd *realDecoder) getBool() (bool, error) { |
| b, err := rd.getInt8() |
| if err != nil || b == 0 { |
| return false, err |
| } |
| if b != 1 { |
| return false, errInvalidBool |
| } |
| return true, nil |
| } |
| |
| // collections |
| |
| func (rd *realDecoder) getBytes() ([]byte, error) { |
| tmp, err := rd.getInt32() |
| if err != nil { |
| return nil, err |
| } |
| if tmp == -1 { |
| return nil, nil |
| } |
| |
| return rd.getRawBytes(int(tmp)) |
| } |
| |
| func (rd *realDecoder) getVarintBytes() ([]byte, error) { |
| tmp, err := rd.getVarint() |
| if err != nil { |
| return nil, err |
| } |
| if tmp == -1 { |
| return nil, nil |
| } |
| |
| return rd.getRawBytes(int(tmp)) |
| } |
| |
| func (rd *realDecoder) getStringLength() (int, error) { |
| length, err := rd.getInt16() |
| if err != nil { |
| return 0, err |
| } |
| |
| n := int(length) |
| |
| switch { |
| case n < -1: |
| return 0, errInvalidStringLength |
| case n > rd.remaining(): |
| rd.off = len(rd.raw) |
| return 0, ErrInsufficientData |
| } |
| |
| return n, nil |
| } |
| |
| func (rd *realDecoder) getString() (string, error) { |
| n, err := rd.getStringLength() |
| if err != nil || n == -1 { |
| return "", err |
| } |
| |
| tmpStr := string(rd.raw[rd.off : rd.off+n]) |
| rd.off += n |
| return tmpStr, nil |
| } |
| |
| func (rd *realDecoder) getNullableString() (*string, error) { |
| n, err := rd.getStringLength() |
| if err != nil || n == -1 { |
| return nil, err |
| } |
| |
| tmpStr := string(rd.raw[rd.off : rd.off+n]) |
| rd.off += n |
| return &tmpStr, err |
| } |
| |
| func (rd *realDecoder) getInt32Array() ([]int32, error) { |
| if rd.remaining() < 4 { |
| rd.off = len(rd.raw) |
| return nil, ErrInsufficientData |
| } |
| n := int(binary.BigEndian.Uint32(rd.raw[rd.off:])) |
| rd.off += 4 |
| |
| if rd.remaining() < 4*n { |
| rd.off = len(rd.raw) |
| return nil, ErrInsufficientData |
| } |
| |
| if n == 0 { |
| return nil, nil |
| } |
| |
| if n < 0 { |
| return nil, errInvalidArrayLength |
| } |
| |
| ret := make([]int32, n) |
| for i := range ret { |
| ret[i] = int32(binary.BigEndian.Uint32(rd.raw[rd.off:])) |
| rd.off += 4 |
| } |
| return ret, nil |
| } |
| |
| func (rd *realDecoder) getInt64Array() ([]int64, error) { |
| if rd.remaining() < 4 { |
| rd.off = len(rd.raw) |
| return nil, ErrInsufficientData |
| } |
| n := int(binary.BigEndian.Uint32(rd.raw[rd.off:])) |
| rd.off += 4 |
| |
| if rd.remaining() < 8*n { |
| rd.off = len(rd.raw) |
| return nil, ErrInsufficientData |
| } |
| |
| if n == 0 { |
| return nil, nil |
| } |
| |
| if n < 0 { |
| return nil, errInvalidArrayLength |
| } |
| |
| ret := make([]int64, n) |
| for i := range ret { |
| ret[i] = int64(binary.BigEndian.Uint64(rd.raw[rd.off:])) |
| rd.off += 8 |
| } |
| return ret, nil |
| } |
| |
| func (rd *realDecoder) getStringArray() ([]string, error) { |
| if rd.remaining() < 4 { |
| rd.off = len(rd.raw) |
| return nil, ErrInsufficientData |
| } |
| n := int(binary.BigEndian.Uint32(rd.raw[rd.off:])) |
| rd.off += 4 |
| |
| if n == 0 { |
| return nil, nil |
| } |
| |
| if n < 0 { |
| return nil, errInvalidArrayLength |
| } |
| |
| ret := make([]string, n) |
| for i := range ret { |
| str, err := rd.getString() |
| if err != nil { |
| return nil, err |
| } |
| |
| ret[i] = str |
| } |
| return ret, nil |
| } |
| |
| // subsets |
| |
| func (rd *realDecoder) remaining() int { |
| return len(rd.raw) - rd.off |
| } |
| |
| func (rd *realDecoder) getSubset(length int) (packetDecoder, error) { |
| buf, err := rd.getRawBytes(length) |
| if err != nil { |
| return nil, err |
| } |
| return &realDecoder{raw: buf}, nil |
| } |
| |
| func (rd *realDecoder) getRawBytes(length int) ([]byte, error) { |
| if length < 0 { |
| return nil, errInvalidByteSliceLength |
| } else if length > rd.remaining() { |
| rd.off = len(rd.raw) |
| return nil, ErrInsufficientData |
| } |
| |
| start := rd.off |
| rd.off += length |
| return rd.raw[start:rd.off], nil |
| } |
| |
| func (rd *realDecoder) peek(offset, length int) (packetDecoder, error) { |
| if rd.remaining() < offset+length { |
| return nil, ErrInsufficientData |
| } |
| off := rd.off + offset |
| return &realDecoder{raw: rd.raw[off : off+length]}, nil |
| } |
| |
| func (rd *realDecoder) peekInt8(offset int) (int8, error) { |
| const byteLen = 1 |
| if rd.remaining() < offset+byteLen { |
| return -1, ErrInsufficientData |
| } |
| return int8(rd.raw[rd.off+offset]), nil |
| } |
| |
| // stacks |
| |
| func (rd *realDecoder) push(in pushDecoder) error { |
| in.saveOffset(rd.off) |
| |
| var reserve int |
| if dpd, ok := in.(dynamicPushDecoder); ok { |
| if err := dpd.decode(rd); err != nil { |
| return err |
| } |
| } else { |
| reserve = in.reserveLength() |
| if rd.remaining() < reserve { |
| rd.off = len(rd.raw) |
| return ErrInsufficientData |
| } |
| } |
| |
| rd.stack = append(rd.stack, in) |
| |
| rd.off += reserve |
| |
| return nil |
| } |
| |
| func (rd *realDecoder) pop() error { |
| // this is go's ugly pop pattern (the inverse of append) |
| in := rd.stack[len(rd.stack)-1] |
| rd.stack = rd.stack[:len(rd.stack)-1] |
| |
| return in.check(rd.off, rd.raw) |
| } |