спеціалізована СУБД для зберігання та обробки показань датчиків та лічильників
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.
 
 
diploma/conbuf/conbuf.go

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
}
}
}