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.
214 lines
4.7 KiB
214 lines
4.7 KiB
package atree
|
|
|
|
import (
|
|
"hash/crc32"
|
|
|
|
"gordenko.dev/dima/diploma/bin"
|
|
)
|
|
|
|
type ValueAtComparator struct {
|
|
buf []byte
|
|
timestamp uint32
|
|
}
|
|
|
|
func (s ValueAtComparator) CompareTo(elemIdx int) int {
|
|
var (
|
|
pos = elemIdx * timestampSize
|
|
elem = bin.GetUint32(s.buf[pos:])
|
|
)
|
|
|
|
if s.timestamp < elem {
|
|
return -1
|
|
} else if s.timestamp > elem {
|
|
return 1
|
|
} else {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func BinarySearch(qty int, keyComparator bin.KeyComparator) (elemIdx int, isFound bool) {
|
|
if qty == 0 {
|
|
return
|
|
}
|
|
a := 0
|
|
b := qty - 1
|
|
for {
|
|
var (
|
|
elemIdx = (b-a)/2 + a
|
|
code = keyComparator.CompareTo(elemIdx)
|
|
)
|
|
if code == 1 {
|
|
a = elemIdx + 1
|
|
if a > b {
|
|
return elemIdx, false // +1
|
|
}
|
|
} else if code == -1 {
|
|
b = elemIdx - 1
|
|
if b < a {
|
|
if elemIdx == 0 {
|
|
return 0, false
|
|
} else {
|
|
return elemIdx - 1, false
|
|
}
|
|
}
|
|
} else {
|
|
return elemIdx, true
|
|
}
|
|
}
|
|
}
|
|
|
|
type chunksToDataPageReq struct {
|
|
PrevPageNo uint32
|
|
TimestampsChunks [][]byte
|
|
TimestampsSize uint16
|
|
ValuesChunks [][]byte
|
|
ValuesSize uint16
|
|
}
|
|
|
|
func chunksToDataPage(buf []byte, req chunksToDataPageReq) {
|
|
bin.PutUint32(buf[prevPageIdx:], req.PrevPageNo)
|
|
bin.PutUint16(buf[timestampsSizeIdx:], req.TimestampsSize)
|
|
bin.PutUint16(buf[valuesSizeIdx:], req.ValuesSize)
|
|
|
|
var (
|
|
remainingSize = int(req.TimestampsSize)
|
|
pos = 0
|
|
)
|
|
for _, chunk := range req.TimestampsChunks {
|
|
if remainingSize >= len(chunk) {
|
|
copy(buf[pos:], chunk)
|
|
remainingSize -= len(chunk)
|
|
pos += len(chunk)
|
|
} else {
|
|
copy(buf[pos:], chunk[:remainingSize])
|
|
break
|
|
}
|
|
}
|
|
|
|
remainingSize = int(req.ValuesSize)
|
|
pos = int(req.TimestampsSize)
|
|
|
|
for _, chunk := range req.ValuesChunks {
|
|
if remainingSize >= len(chunk) {
|
|
copy(buf[pos:], chunk)
|
|
remainingSize -= len(chunk)
|
|
pos += len(chunk)
|
|
} else {
|
|
copy(buf[pos:], chunk[:remainingSize])
|
|
break
|
|
}
|
|
}
|
|
bin.PutUint32(buf[dataCRC32Idx:], crc32.ChecksumIEEE(buf[:dataCRC32Idx]))
|
|
}
|
|
|
|
func setPrevPageNo(buf []byte, pageNo uint32) {
|
|
bin.PutUint32(buf[prevPageIdx:], pageNo)
|
|
}
|
|
|
|
func getPrevPageNo(buf []byte) uint32 {
|
|
return bin.GetUint32(buf[prevPageIdx:])
|
|
}
|
|
|
|
func findPageNo(buf []byte, timestamp uint32) (pageNo uint32) {
|
|
var (
|
|
qty = bin.GetUint16AsInt(buf[indexRecordsQtyIdx:])
|
|
comparator = ValueAtComparator{
|
|
buf: buf,
|
|
timestamp: timestamp,
|
|
}
|
|
)
|
|
elemIdx, _ := BinarySearch(qty, comparator)
|
|
pos := indexFooterIdx - (elemIdx+1)*PageNoSize
|
|
return bin.GetUint32(buf[pos:])
|
|
}
|
|
|
|
func findPageNoIdx(buf []byte, timestamp uint32) (idx int) {
|
|
var (
|
|
qty = bin.GetUint16AsInt(buf[indexRecordsQtyIdx:])
|
|
comparator = ValueAtComparator{
|
|
buf: buf,
|
|
timestamp: timestamp,
|
|
}
|
|
)
|
|
elemIdx, _ := BinarySearch(qty, comparator)
|
|
return elemIdx
|
|
}
|
|
|
|
func findPageNoForDeleteSince(buf []byte, since uint32) (uint32, int, int) {
|
|
var (
|
|
qty = bin.GetUint16AsInt(buf[indexRecordsQtyIdx:])
|
|
comparator = ValueAtComparator{
|
|
buf: buf,
|
|
timestamp: since,
|
|
}
|
|
)
|
|
elemIdx, _ := BinarySearch(qty, comparator)
|
|
pos := elemIdx * timestampSize
|
|
timestamp := bin.GetUint32(buf[pos:])
|
|
|
|
if timestamp == since {
|
|
if elemIdx == 0 {
|
|
return 0, qty, -1
|
|
}
|
|
elemIdx--
|
|
}
|
|
pos = indexFooterIdx - (elemIdx+1)*PageNoSize
|
|
return bin.GetUint32(buf[pos:]), qty, elemIdx
|
|
}
|
|
|
|
func getLastPageNo(buf []byte) (pageNo uint32) {
|
|
qty := bin.GetUint16AsInt(buf[indexRecordsQtyIdx:])
|
|
pos := indexFooterIdx - qty*PageNoSize
|
|
return bin.GetUint32(buf[pos:])
|
|
}
|
|
|
|
func getPageNo(buf []byte, idx int) (pageNo uint32) {
|
|
pos := indexFooterIdx - (idx+1)*PageNoSize
|
|
return bin.GetUint32(buf[pos:])
|
|
}
|
|
|
|
func listPageNumbers(buf []byte) (pageNumbers []uint32) {
|
|
qty := bin.GetUint16AsInt(buf[indexRecordsQtyIdx:])
|
|
pos := indexFooterIdx - PageNoSize
|
|
for range qty {
|
|
pageNumbers = append(pageNumbers, bin.GetUint32(buf[pos:]))
|
|
pos -= PageNoSize
|
|
}
|
|
return
|
|
}
|
|
|
|
// include since timestamp
|
|
func listPageNumbersSince(buf []byte, timestamp uint32) (pageNumbers []uint32) {
|
|
var (
|
|
qty = bin.GetUint16AsInt(buf[indexRecordsQtyIdx:])
|
|
comparator = ValueAtComparator{
|
|
buf: buf,
|
|
timestamp: timestamp,
|
|
}
|
|
)
|
|
elemIdx, _ := BinarySearch(qty, comparator)
|
|
pos := indexFooterIdx - (elemIdx+1)*PageNoSize
|
|
for range qty {
|
|
pageNumbers = append(pageNumbers, bin.GetUint32(buf[pos:]))
|
|
pos -= PageNoSize
|
|
}
|
|
return
|
|
}
|
|
|
|
func getSince(buf []byte) uint32 {
|
|
return bin.GetUint32(buf[0:])
|
|
}
|
|
|
|
func appendPair(buf []byte, timestamp uint32, pageNo uint32) bool {
|
|
qty := bin.GetUint16AsInt(buf[indexRecordsQtyIdx:])
|
|
free := indexFooterIdx - qty*pairSize
|
|
if free < pairSize {
|
|
return false
|
|
}
|
|
pos := qty * timestampSize
|
|
bin.PutUint32(buf[pos:], timestamp)
|
|
pos = indexFooterIdx - (qty+1)*PageNoSize
|
|
bin.PutUint32(buf[pos:], pageNo)
|
|
bin.PutIntAsUint16(buf[indexRecordsQtyIdx:], qty+1)
|
|
return true
|
|
}
|
|
|