200 lines
3.1 KiB
Go
200 lines
3.1 KiB
Go
package packer
|
|
|
|
import (
|
|
"slices"
|
|
)
|
|
|
|
type Unpacker struct {
|
|
data []byte
|
|
idx int
|
|
}
|
|
|
|
func (u *Unpacker) Reset(data []byte) {
|
|
u.data = slices.Clone(data)
|
|
u.idx = 0
|
|
}
|
|
|
|
// first byte of the current buffer
|
|
func (u *Unpacker) byte() byte {
|
|
return u.data[u.idx]
|
|
}
|
|
|
|
// consume one byte
|
|
func (u *Unpacker) getByte() byte {
|
|
b := u.data[u.idx]
|
|
u.idx++
|
|
return b
|
|
}
|
|
|
|
func (u *Unpacker) Data() []byte {
|
|
return u.data
|
|
}
|
|
|
|
func (u *Unpacker) Rest() []byte {
|
|
rest := u.data[u.idx:]
|
|
u.idx = u.Size()
|
|
return rest
|
|
}
|
|
|
|
func (u *Unpacker) Size() int {
|
|
return len(u.data)
|
|
}
|
|
|
|
func (u *Unpacker) RemainingSize() int {
|
|
return u.Size() - u.idx
|
|
}
|
|
|
|
const (
|
|
Sanitize = 1
|
|
SanitizeCC = 2
|
|
SanitizeSkipWhitespaces = 4
|
|
)
|
|
|
|
func (u *Unpacker) GetStringSanitized(sanitizeType int) string {
|
|
bytes := []byte{}
|
|
|
|
skipping := sanitizeType&SanitizeSkipWhitespaces != 0
|
|
|
|
for {
|
|
b := u.getByte()
|
|
if b == 0x00 {
|
|
break
|
|
}
|
|
|
|
if skipping {
|
|
if b == ' ' || b == '\t' || b == '\n' {
|
|
continue
|
|
}
|
|
skipping = false
|
|
}
|
|
|
|
if sanitizeType&SanitizeCC != 0 {
|
|
if b < 32 {
|
|
b = ' '
|
|
}
|
|
} else if sanitizeType&Sanitize != 0 {
|
|
if b < 32 && !(b == '\r') && !(b == '\n') && !(b == '\t') {
|
|
b = ' '
|
|
}
|
|
}
|
|
|
|
bytes = append(bytes, b)
|
|
}
|
|
|
|
return string(bytes)
|
|
}
|
|
|
|
func (u *Unpacker) GetString() string {
|
|
return u.GetStringSanitized(Sanitize)
|
|
}
|
|
|
|
func (u *Unpacker) GetInt() int {
|
|
sign := int(u.byte()>>6) & 1
|
|
res := int(u.byte() & 0x3F)
|
|
// fake loop should only loop once
|
|
// its the poor mans goto hack
|
|
for {
|
|
if (u.byte() & 0x80) == 0 {
|
|
break
|
|
}
|
|
u.idx += 1
|
|
res |= int(u.byte()&0x7F) << 6
|
|
|
|
if (u.byte() & 0x80) == 0 {
|
|
break
|
|
}
|
|
u.idx += 1
|
|
res |= int(u.byte()&0x7F) << (6 + 7)
|
|
|
|
if (u.byte() & 0x80) == 0 {
|
|
break
|
|
}
|
|
u.idx += 1
|
|
res |= int(u.byte()&0x7F) << (6 + 7 + 7)
|
|
|
|
if (u.byte() & 0x80) == 0 {
|
|
break
|
|
}
|
|
u.idx += 1
|
|
res |= int(u.byte()&0x7F) << (6 + 7 + 7 + 7)
|
|
break
|
|
}
|
|
|
|
u.idx += 1
|
|
res ^= -sign
|
|
return res
|
|
}
|
|
|
|
func PackStr(str string) []byte {
|
|
return slices.Concat(
|
|
[]byte(str),
|
|
[]byte{0x00},
|
|
)
|
|
}
|
|
|
|
func PackBool(b bool) []byte {
|
|
if b {
|
|
return []byte{0x01}
|
|
}
|
|
return []byte{0x00}
|
|
}
|
|
|
|
func PackInt(num int) []byte {
|
|
res := []byte{0x00}
|
|
idx := 0
|
|
if num < 0 {
|
|
res[0] |= 0x40 // set sign bit
|
|
num = ^num
|
|
}
|
|
|
|
res[0] |= byte(num & 0x3F) // pack 6 bit into dst
|
|
num >>= 6 // discard 6 bits
|
|
for num != 0 {
|
|
res = append(res, 0x00)
|
|
|
|
res[idx] |= 0x80 // set extend bit
|
|
idx++
|
|
res[idx] = byte(num & 0x7F) // pack 7 bit
|
|
num >>= 7 // discard 7 bits
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
func UnpackInt(data []byte) int {
|
|
sign := int(data[0]>>6) & 1
|
|
res := int(data[0] & 0x3F)
|
|
i := 0
|
|
// fake loop should only loop once
|
|
// its the poor mans goto hack
|
|
for {
|
|
if (data[i] & 0x80) == 0 {
|
|
break
|
|
}
|
|
i += 1
|
|
res |= int(data[i]&0x7F) << 6
|
|
|
|
if (data[i] & 0x80) == 0 {
|
|
break
|
|
}
|
|
i += 1
|
|
res |= int(data[i]&0x7F) << (6 + 7)
|
|
|
|
if (data[i] & 0x80) == 0 {
|
|
break
|
|
}
|
|
i += 1
|
|
res |= int(data[i]&0x7F) << (6 + 7 + 7)
|
|
|
|
if (data[i] & 0x80) == 0 {
|
|
break
|
|
}
|
|
i += 1
|
|
res |= int(data[i]&0x7F) << (6 + 7 + 7 + 7)
|
|
break
|
|
}
|
|
|
|
res ^= -sign
|
|
return res
|
|
}
|