package protocol import ( "encoding/binary" "errors" "math" ) type UxReader struct { data []byte pos int } func NewUxReader(data []byte) *UxReader { return &UxReader{data: data, pos: 0} } func (r *UxReader) Position() int { return r.pos } func (r *UxReader) Remaining() int { return len(r.data) - r.pos } func (r *UxReader) U8() (byte, error) { if r.pos >= len(r.data) { return 0, errors.New("EOF") } b := r.data[r.pos] r.pos++ return b, nil } func (r *UxReader) Bool() (bool, error) { b, err := r.U8() return b != 0, err } func (r *UxReader) I16() (int16, error) { if r.Remaining() < 2 { return 0, errors.New("EOF") } v := int16(binary.LittleEndian.Uint16(r.data[r.pos : r.pos+2])) r.pos += 2 return v, nil } func (r *UxReader) U16() (uint16, error) { if r.Remaining() < 2 { return 0, errors.New("EOF") } v := binary.LittleEndian.Uint16(r.data[r.pos : r.pos+2]) r.pos += 2 return v, nil } func (r *UxReader) I32() (int32, error) { if r.Remaining() < 4 { return 0, errors.New("EOF") } v := int32(binary.LittleEndian.Uint32(r.data[r.pos : r.pos+4])) r.pos += 4 return v, nil } func (r *UxReader) U32() (uint32, error) { if r.Remaining() < 4 { return 0, errors.New("EOF") } v := binary.LittleEndian.Uint32(r.data[r.pos : r.pos+4]) r.pos += 4 return v, nil } func (r *UxReader) I64() (int64, error) { if r.Remaining() < 8 { return 0, errors.New("EOF") } v := int64(binary.LittleEndian.Uint64(r.data[r.pos : r.pos+8])) r.pos += 8 return v, nil } func (r *UxReader) U64() (ulong uint64, err error) { if r.Remaining() < 8 { return 0, errors.New("EOF") } v := binary.LittleEndian.Uint64(r.data[r.pos : r.pos+8]) r.pos += 8 return v, nil } func (r *UxReader) F32() (float32, error) { bits, err := r.U32() if err != nil { return 0, err } return math.Float32frombits(bits), nil } func (r *UxReader) F64() (float64, error) { bits, err := r.U64() if err != nil { return 0, err } return math.Float64frombits(bits), nil } func (r *UxReader) ReadVarInt() (int, error) { shift := 0 result := 0 for r.pos < len(r.data) { b := r.data[r.pos] r.pos++ result |= int(b&0x7F) << shift if (b & 0x80) == 0 { return result, nil } shift += 7 } return result, errors.New("unterminated varint") } func (r *UxReader) Int7() (int, error) { v, err := r.ReadVarInt() if err != nil { return 0, err } if v < 1 { return 0, nil } return v - 1, nil } func (r *UxReader) Raw7() (int, error) { return r.ReadVarInt() } func (r *UxReader) UxString() (string, error) { lenPlus, err := r.ReadVarInt() if err != nil { return "", err } if lenPlus == 0 { return "", nil } length := lenPlus - 1 if length > r.Remaining() { return "", errors.New("string length exceeds buffer") } str := string(r.data[r.pos : r.pos+length]) r.pos += length return str, nil } func (r *UxReader) Bytes(count int) ([]byte, error) { if count < 0 || count > r.Remaining() { return nil, errors.New("requested count exceeds buffer") } res := make([]byte, count) copy(res, r.data[r.pos:r.pos+count]) r.pos += count return res, nil }