You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
459 lines
8.7 KiB
459 lines
8.7 KiB
package conbuf
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
|
|
"gordenko.dev/dima/diploma/bin"
|
|
)
|
|
|
|
const chunkSize = 512
|
|
|
|
var (
|
|
ErrOutOfRange = errors.New("out of range")
|
|
)
|
|
|
|
type ContinuousBuffer struct {
|
|
chunks [][]byte
|
|
}
|
|
|
|
func NewFromBuffer(buf []byte) *ContinuousBuffer {
|
|
var (
|
|
chunks [][]byte
|
|
copied = 0
|
|
)
|
|
|
|
for copied < len(buf) {
|
|
chunk := make([]byte, chunkSize)
|
|
end := min(copied+chunkSize, len(buf))
|
|
copy(chunk, buf[copied:end])
|
|
chunks = append(chunks, chunk)
|
|
copied += end - copied
|
|
}
|
|
|
|
return &ContinuousBuffer{
|
|
chunks: chunks,
|
|
}
|
|
}
|
|
|
|
func New(chunks [][]byte) *ContinuousBuffer {
|
|
for i, chunk := range chunks {
|
|
if len(chunk) != chunkSize {
|
|
panic(fmt.Sprintf("wrong chunk #%d size %d", i, len(chunk)))
|
|
}
|
|
}
|
|
return &ContinuousBuffer{
|
|
chunks: chunks,
|
|
}
|
|
}
|
|
|
|
func (s *ContinuousBuffer) Chunks() [][]byte {
|
|
return s.chunks
|
|
}
|
|
|
|
// [0, pos)
|
|
func (s *ContinuousBuffer) GetByte(idx int) byte {
|
|
chunkIdx := idx / chunkSize
|
|
if chunkIdx >= len(s.chunks) {
|
|
panic(ErrOutOfRange)
|
|
}
|
|
byteIdx := idx % chunkSize
|
|
return s.chunks[chunkIdx][byteIdx]
|
|
}
|
|
|
|
func (s *ContinuousBuffer) SetByte(idx int, b byte) {
|
|
chunkIdx := idx / chunkSize
|
|
if chunkIdx > len(s.chunks) {
|
|
panic(ErrOutOfRange)
|
|
}
|
|
if chunkIdx == len(s.chunks) {
|
|
s.chunks = append(s.chunks, make([]byte, chunkSize))
|
|
}
|
|
byteIdx := idx % chunkSize
|
|
s.chunks[chunkIdx][byteIdx] = b
|
|
}
|
|
|
|
func (s *ContinuousBuffer) SetFlag(idx int, flag byte) {
|
|
chunkIdx := idx / chunkSize
|
|
if chunkIdx > len(s.chunks) {
|
|
panic(ErrOutOfRange)
|
|
}
|
|
if chunkIdx == len(s.chunks) {
|
|
s.chunks = append(s.chunks, make([]byte, chunkSize))
|
|
}
|
|
byteIdx := idx % chunkSize
|
|
s.chunks[chunkIdx][byteIdx] |= flag
|
|
}
|
|
|
|
func (s *ContinuousBuffer) UnsetFlag(idx int, flag byte) {
|
|
chunkIdx := idx / chunkSize
|
|
if chunkIdx > len(s.chunks) {
|
|
panic(ErrOutOfRange)
|
|
}
|
|
if chunkIdx == len(s.chunks) {
|
|
s.chunks = append(s.chunks, make([]byte, chunkSize))
|
|
}
|
|
byteIdx := idx % chunkSize
|
|
s.chunks[chunkIdx][byteIdx] &^= flag
|
|
}
|
|
|
|
// [since, until)
|
|
func (s *ContinuousBuffer) ShiftOnePosToRight(since int, until int) {
|
|
if since < 0 {
|
|
panic("since < 0")
|
|
}
|
|
if since >= until {
|
|
panic("since >= until")
|
|
}
|
|
|
|
chunkIdx := until / chunkSize
|
|
byteIdx := until % chunkSize
|
|
|
|
if chunkIdx > len(s.chunks) {
|
|
panic(ErrOutOfRange)
|
|
}
|
|
|
|
if chunkIdx == len(s.chunks) {
|
|
if byteIdx == 0 {
|
|
s.chunks = append(s.chunks, make([]byte, chunkSize))
|
|
} else {
|
|
panic(ErrOutOfRange)
|
|
}
|
|
}
|
|
|
|
var (
|
|
qty = until - since
|
|
prevChunkIdx int
|
|
prevByteIdx int
|
|
)
|
|
|
|
for range qty {
|
|
prevChunkIdx = chunkIdx
|
|
prevByteIdx = byteIdx - 1
|
|
if prevByteIdx < 0 {
|
|
prevChunkIdx = chunkIdx - 1
|
|
prevByteIdx = chunkSize - 1
|
|
}
|
|
|
|
s.chunks[chunkIdx][byteIdx] = s.chunks[prevChunkIdx][prevByteIdx]
|
|
|
|
if byteIdx > 0 {
|
|
byteIdx--
|
|
} else {
|
|
chunkIdx--
|
|
byteIdx = chunkSize - 1
|
|
}
|
|
}
|
|
}
|
|
|
|
// [since, until)
|
|
func (s *ContinuousBuffer) ShiftOnePosToLeft(since int, until int) {
|
|
if since <= 0 {
|
|
panic("since <= 0")
|
|
}
|
|
if since >= until {
|
|
panic("since >= until")
|
|
}
|
|
|
|
chunkIdx := since / chunkSize
|
|
byteIdx := since % chunkSize
|
|
|
|
if until > len(s.chunks)*chunkSize {
|
|
panic(ErrOutOfRange)
|
|
}
|
|
|
|
var (
|
|
qty = until - since
|
|
prevChunkIdx int
|
|
prevByteIdx int
|
|
)
|
|
|
|
for range qty {
|
|
prevChunkIdx = chunkIdx
|
|
prevByteIdx = byteIdx - 1
|
|
if prevByteIdx < 0 {
|
|
prevChunkIdx = chunkIdx - 1
|
|
prevByteIdx = chunkSize - 1
|
|
}
|
|
|
|
s.chunks[prevChunkIdx][prevByteIdx] = s.chunks[chunkIdx][byteIdx]
|
|
|
|
byteIdx++
|
|
if byteIdx == chunkSize {
|
|
chunkIdx++
|
|
byteIdx = 0
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *ContinuousBuffer) PutUint32(pos int, num uint32) {
|
|
s.CopyTo(pos,
|
|
[]byte{
|
|
byte(num),
|
|
byte(num >> 8),
|
|
byte(num >> 16),
|
|
byte(num >> 24),
|
|
})
|
|
}
|
|
|
|
func (s *ContinuousBuffer) GetUint32(pos int) uint32 {
|
|
arr := s.Slice(pos, pos+4)
|
|
return uint32(arr[0]) | (uint32(arr[1]) << 8) |
|
|
(uint32(arr[2]) << 16) | (uint32(arr[3]) << 24)
|
|
}
|
|
|
|
func (s *ContinuousBuffer) ReversePutVarUint64(pos int, num uint64) int {
|
|
var tmp [9]byte
|
|
for i := range 8 {
|
|
tmp[i] = byte(num & 127)
|
|
num >>= 7
|
|
if num == 0 {
|
|
tmp[i] |= 128
|
|
n := i + 1
|
|
s.ReverseCopyTo(pos, tmp[:n])
|
|
return n
|
|
}
|
|
}
|
|
tmp[8] = byte(num)
|
|
n := 9
|
|
s.ReverseCopyTo(pos, tmp[:])
|
|
return n
|
|
}
|
|
|
|
func (s *ContinuousBuffer) ReverseGetVarUint64(idx int) (num uint64, n int, err error) {
|
|
chunkIdx := idx / chunkSize
|
|
if chunkIdx >= len(s.chunks) {
|
|
panic(ErrOutOfRange)
|
|
}
|
|
|
|
var (
|
|
byteIdx = idx % chunkSize
|
|
chunk = s.chunks[chunkIdx]
|
|
b byte
|
|
)
|
|
|
|
for i := range 8 {
|
|
b = chunk[byteIdx]
|
|
if b >= 128 {
|
|
num |= uint64(b&127) << uint(i*7)
|
|
return num, i + 1, nil
|
|
}
|
|
num |= uint64(b) << uint(i*7)
|
|
|
|
if byteIdx > 0 {
|
|
byteIdx--
|
|
} else {
|
|
if chunkIdx == 0 {
|
|
return 0, 0, io.EOF
|
|
} else {
|
|
chunkIdx--
|
|
chunk = s.chunks[chunkIdx]
|
|
byteIdx = chunkSize - 1
|
|
}
|
|
}
|
|
}
|
|
return num | uint64(chunk[byteIdx])<<56, 9, nil
|
|
}
|
|
|
|
func (s *ContinuousBuffer) ReversePutVarInt64(pos int, x int64) int {
|
|
return s.ReversePutVarUint64(pos, bin.EncodeZigZag(x))
|
|
}
|
|
|
|
func (s *ContinuousBuffer) ReverseGetVarInt64(idx int) (int64, int, error) {
|
|
u64, n, err := s.ReverseGetVarUint64(idx)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
return bin.DecodeZigZag(u64), n, nil
|
|
}
|
|
|
|
func (s *ContinuousBuffer) PutVarUint64(pos int, num uint64) int {
|
|
var tmp [9]byte
|
|
for i := range 8 {
|
|
tmp[i] = byte(num & 127)
|
|
num >>= 7
|
|
if num == 0 {
|
|
tmp[i] |= 128
|
|
n := i + 1
|
|
s.CopyTo(pos, tmp[:n])
|
|
return n
|
|
}
|
|
}
|
|
tmp[8] = byte(num)
|
|
s.CopyTo(pos, tmp[:])
|
|
return 9
|
|
}
|
|
|
|
func (s *ContinuousBuffer) GetVarUint64(idx int) (num uint64, n int, err error) {
|
|
chunkIdx := idx / chunkSize
|
|
if chunkIdx >= len(s.chunks) {
|
|
panic(ErrOutOfRange)
|
|
}
|
|
|
|
var (
|
|
byteIdx = idx % chunkSize
|
|
chunk = s.chunks[chunkIdx]
|
|
b byte
|
|
)
|
|
|
|
for i := range 8 {
|
|
b = chunk[byteIdx]
|
|
if b >= 128 {
|
|
num |= uint64(b&127) << uint(i*7)
|
|
return num, i + 1, nil
|
|
}
|
|
num |= uint64(b) << uint(i*7)
|
|
|
|
byteIdx++
|
|
if byteIdx == chunkSize {
|
|
chunkIdx++
|
|
if chunkIdx == len(s.chunks) {
|
|
return 0, 0, io.EOF
|
|
}
|
|
chunk = s.chunks[chunkIdx]
|
|
byteIdx = 0
|
|
}
|
|
}
|
|
return num | uint64(chunk[byteIdx])<<56, 9, nil
|
|
}
|
|
|
|
func (s *ContinuousBuffer) PutVarInt64(idx int, x int64) int {
|
|
return s.PutVarUint64(idx, bin.EncodeZigZag(x))
|
|
}
|
|
|
|
func (s *ContinuousBuffer) GetVarInt64(idx int) (int64, int, error) {
|
|
u64, n, err := s.GetVarUint64(idx)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
return bin.DecodeZigZag(u64), n, nil
|
|
}
|
|
|
|
func (s *ContinuousBuffer) CopyTo(idx int, data []byte) {
|
|
chunkIdx := idx / chunkSize
|
|
if chunkIdx > len(s.chunks) {
|
|
panic(ErrOutOfRange)
|
|
}
|
|
if chunkIdx == len(s.chunks) {
|
|
s.chunks = append(s.chunks, make([]byte, chunkSize))
|
|
}
|
|
byteIdx := idx % chunkSize
|
|
chunk := s.chunks[chunkIdx]
|
|
copied := 0
|
|
|
|
for _, b := range data {
|
|
chunk[byteIdx] = b
|
|
copied++
|
|
byteIdx++
|
|
if byteIdx == chunkSize {
|
|
byteIdx = 0
|
|
chunkIdx++
|
|
if chunkIdx == len(s.chunks) {
|
|
if copied == len(data) {
|
|
return
|
|
}
|
|
s.chunks = append(s.chunks, make([]byte, chunkSize))
|
|
}
|
|
chunk = s.chunks[chunkIdx]
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *ContinuousBuffer) ReverseCopyTo(idx int, data []byte) {
|
|
chunkIdx := idx / chunkSize
|
|
if chunkIdx > len(s.chunks) {
|
|
panic(ErrOutOfRange)
|
|
}
|
|
if chunkIdx == len(s.chunks) {
|
|
s.chunks = append(s.chunks, make([]byte, chunkSize))
|
|
}
|
|
byteIdx := idx % chunkSize
|
|
chunk := s.chunks[chunkIdx]
|
|
copied := 0
|
|
|
|
for i := len(data) - 1; i >= 0; i-- {
|
|
chunk[byteIdx] = data[i]
|
|
copied++
|
|
byteIdx++
|
|
if byteIdx == chunkSize {
|
|
byteIdx = 0
|
|
chunkIdx++
|
|
if chunkIdx == len(s.chunks) {
|
|
if copied == len(data) {
|
|
return
|
|
}
|
|
s.chunks = append(s.chunks, make([]byte, chunkSize))
|
|
}
|
|
chunk = s.chunks[chunkIdx]
|
|
}
|
|
}
|
|
}
|
|
|
|
// [since, until)
|
|
func (s *ContinuousBuffer) Slice(since int, until int) []byte {
|
|
if since >= until {
|
|
return nil
|
|
}
|
|
size := len(s.chunks) * chunkSize
|
|
if until >= size {
|
|
panic(ErrOutOfRange)
|
|
}
|
|
|
|
data := make([]byte, until-since)
|
|
|
|
chunkIdx := since / chunkSize
|
|
byteIdx := since % chunkSize
|
|
chunk := s.chunks[chunkIdx]
|
|
|
|
for i := range len(data) {
|
|
data[i] = chunk[byteIdx]
|
|
byteIdx++
|
|
if byteIdx == chunkSize {
|
|
byteIdx = 0
|
|
chunkIdx++
|
|
chunk = s.chunks[chunkIdx]
|
|
}
|
|
}
|
|
return data
|
|
}
|
|
|
|
func (s *ContinuousBuffer) DecodeRunLength(pos int) (length uint16, n int) {
|
|
b1 := s.GetByte(pos)
|
|
pos--
|
|
if b1 < 128 {
|
|
length = uint16(b1)
|
|
n = 1
|
|
} else {
|
|
b2 := s.GetByte(pos)
|
|
length = uint16(b1&127) | (uint16(b2) << 7)
|
|
n = 2
|
|
}
|
|
length += 2
|
|
return
|
|
}
|
|
|
|
func (s *ContinuousBuffer) Copy() *ContinuousBuffer {
|
|
var copies [][]byte
|
|
for _, chunk := range s.chunks {
|
|
buf := make([]byte, chunkSize)
|
|
copy(buf, chunk)
|
|
copies = append(copies, buf)
|
|
}
|
|
return New(copies)
|
|
}
|
|
|
|
// size to copy
|
|
func (s *ContinuousBuffer) CopyChunksToOneBuffer(dst []byte, size int) {
|
|
pos := 0
|
|
for _, chunk := range s.chunks {
|
|
if size >= len(chunk) {
|
|
copy(dst[pos:], chunk)
|
|
size -= len(chunk)
|
|
pos += len(chunk)
|
|
} else {
|
|
copy(dst[pos:], chunk[:size])
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|