rc1
This commit is contained in:
459
conbuf/conbuf.go
Normal file
459
conbuf/conbuf.go
Normal file
@@ -0,0 +1,459 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user