2026-06-15 10:47:24 +00:00
|
|
|
|
package storage
|
|
|
|
|
|
|
2026-06-19 07:25:46 +03:00
|
|
|
|
import (
|
|
|
|
|
|
bin "gordenko.dev/dima/bin/little"
|
|
|
|
|
|
)
|
2026-06-15 10:47:24 +00:00
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
|
PageNoSize = 4
|
|
|
|
|
|
)
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
2026-06-10 06:18:45 +03:00
|
|
|
|
type KeyComparator interface {
|
|
|
|
|
|
CompareTo(int) int
|
|
|
|
|
|
}
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
|
|
|
|
|
type ValueAtComparator struct {
|
|
|
|
|
|
buf []byte
|
|
|
|
|
|
timestamp uint32
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s ValueAtComparator) CompareTo(elemIdx int) int {
|
2026-06-15 10:47:24 +00:00
|
|
|
|
var (
|
|
|
|
|
|
pos = elemIdx * IndexRecordSize
|
|
|
|
|
|
elem, _ = bin.GetUint32(s.buf[pos:])
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if s.timestamp < elem {
|
|
|
|
|
|
return -1
|
|
|
|
|
|
} else if s.timestamp > elem {
|
|
|
|
|
|
return 1
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return 0
|
|
|
|
|
|
}
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-10 06:18:45 +03:00
|
|
|
|
func BinarySearch(qty int, keyComparator KeyComparator) (elemIdx int, isFound bool) {
|
2026-02-10 14:02:11 +00:00
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-15 10:47:24 +00:00
|
|
|
|
func FindPageOnIndexLevelTail(level IndexLevelTail, timestamp uint32) (pageNo uint32) {
|
|
|
|
|
|
comparator := ValueAtComparator{
|
|
|
|
|
|
buf: level.Buffer,
|
|
|
|
|
|
timestamp: timestamp,
|
|
|
|
|
|
}
|
|
|
|
|
|
elemIdx, _ := BinarySearch(level.RecordsCount, comparator)
|
|
|
|
|
|
pos := elemIdx*IndexRecordSize + 4 // timestamp size
|
|
|
|
|
|
pageNo, _ = bin.GetUint32(level.Buffer[pos:])
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func FindPageOnIndexPage(page []byte, timestamp uint32) (pageNo uint32) {
|
|
|
|
|
|
comparator := ValueAtComparator{
|
|
|
|
|
|
buf: page,
|
|
|
|
|
|
timestamp: timestamp,
|
|
|
|
|
|
}
|
|
|
|
|
|
count, _ := bin.GetUint16(page[indexRecordsCountIdx:])
|
|
|
|
|
|
elemIdx, _ := BinarySearch(int(count), comparator)
|
|
|
|
|
|
pos := elemIdx*IndexRecordSize + 4 // timestamp size
|
|
|
|
|
|
pageNo, _ = bin.GetUint32(page[pos:])
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func IsZeroLevelPage(buf []byte) bool {
|
|
|
|
|
|
return buf[isZeroLevelIdx] == 1
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// можна перевірити хвости від 0 до ... Перевіряю since кожного хвоста.
|
|
|
|
|
|
// Якщо timestamp >=, шукаю pageNo бінарним пошуком. І сторінка мені однозначно підходить.
|
|
|
|
|
|
// Якщо timestamp <, піднімаюсь вище. Якщо рівнів більше немає - until вказано за межами Range показань.
|
|
|
|
|
|
func FindPageOnIndexLevelTails(levels []IndexLevelTail, timestamp uint32) (pageNo uint32, isDataPage bool) {
|
|
|
|
|
|
for i, level := range levels {
|
|
|
|
|
|
tailSince, _ := bin.GetUint32(level.Buffer)
|
|
|
|
|
|
if timestamp >= tailSince {
|
|
|
|
|
|
pageNo = FindPageOnIndexLevelTail(level, timestamp)
|
|
|
|
|
|
return pageNo, i == 0
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-19 07:25:46 +03:00
|
|
|
|
type DeleteSinceOnIndexPageResult struct {
|
|
|
|
|
|
Buffer []byte
|
|
|
|
|
|
RecordsCount int
|
|
|
|
|
|
PageNumbers []uint32
|
|
|
|
|
|
//Idx int
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func DeleteSinceOnIndexPage(buf []byte, since uint32) DeleteSinceOnIndexPageResult {
|
|
|
|
|
|
comparator := ValueAtComparator{
|
|
|
|
|
|
buf: buf,
|
|
|
|
|
|
timestamp: since,
|
|
|
|
|
|
}
|
|
|
|
|
|
count, _ := bin.GetUint16(buf[indexRecordsCountIdx:])
|
|
|
|
|
|
elemIdx, _ := BinarySearch(int(count), comparator)
|
|
|
|
|
|
newbuf := make([]byte, IndexPageSize)
|
|
|
|
|
|
copy(newbuf, buf[:elemIdx*IndexRecordSize]) // [0..idx)
|
|
|
|
|
|
pos := elemIdx*IndexRecordSize + 4 // timestamp size
|
|
|
|
|
|
deleteCount := int(count) - elemIdx
|
|
|
|
|
|
var pageNumbers []uint32
|
|
|
|
|
|
for range deleteCount {
|
|
|
|
|
|
pageNo, _ := bin.GetUint32(buf[pos:])
|
|
|
|
|
|
pageNumbers = append(pageNumbers, pageNo)
|
|
|
|
|
|
pos += IndexRecordSize
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return DeleteSinceOnIndexPageResult{
|
|
|
|
|
|
Buffer: newbuf,
|
|
|
|
|
|
RecordsCount: elemIdx,
|
|
|
|
|
|
PageNumbers: pageNumbers,
|
|
|
|
|
|
//Idx: elemIdx,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type DeleteOnIndexTailResult struct {
|
|
|
|
|
|
RecordsCount int
|
|
|
|
|
|
PageNumbers []uint32
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func DeleteSinceOnIndexTail(level IndexLevelTail, since uint32) DeleteOnIndexTailResult {
|
|
|
|
|
|
comparator := ValueAtComparator{
|
|
|
|
|
|
buf: level.Buffer,
|
|
|
|
|
|
timestamp: since,
|
|
|
|
|
|
}
|
|
|
|
|
|
elemIdx, _ := BinarySearch(level.RecordsCount, comparator)
|
|
|
|
|
|
pos := elemIdx*IndexRecordSize + 4 // timestamp size
|
|
|
|
|
|
deleteCount := level.RecordsCount - elemIdx
|
|
|
|
|
|
var pageNumbers []uint32
|
|
|
|
|
|
for range deleteCount {
|
|
|
|
|
|
pageNo, _ := bin.GetUint32(level.Buffer[pos:])
|
|
|
|
|
|
pageNumbers = append(pageNumbers, pageNo)
|
|
|
|
|
|
pos += IndexRecordSize
|
|
|
|
|
|
}
|
|
|
|
|
|
return DeleteOnIndexTailResult{
|
|
|
|
|
|
RecordsCount: elemIdx,
|
|
|
|
|
|
PageNumbers: pageNumbers,
|
|
|
|
|
|
//Idx: elemIdx,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func DeleteUntilOnIndexTail(level IndexLevelTail, until uint32) DeleteOnIndexTailResult {
|
|
|
|
|
|
comparator := ValueAtComparator{
|
|
|
|
|
|
buf: level.Buffer,
|
|
|
|
|
|
timestamp: until,
|
|
|
|
|
|
}
|
|
|
|
|
|
elemIdx, _ := BinarySearch(level.RecordsCount, comparator)
|
|
|
|
|
|
pos := 4 // timestamp size
|
|
|
|
|
|
deleteCount := elemIdx + 1
|
|
|
|
|
|
var pageNumbers []uint32
|
|
|
|
|
|
for range deleteCount {
|
|
|
|
|
|
pageNo, _ := bin.GetUint32(level.Buffer[pos:])
|
|
|
|
|
|
pageNumbers = append(pageNumbers, pageNo)
|
|
|
|
|
|
pos += IndexRecordSize
|
|
|
|
|
|
}
|
|
|
|
|
|
copy(level.Buffer, level.Buffer[:])
|
|
|
|
|
|
return DeleteOnIndexTailResult{
|
|
|
|
|
|
RecordsCount: elemIdx,
|
|
|
|
|
|
PageNumbers: pageNumbers,
|
|
|
|
|
|
//Idx: elemIdx,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// func getPrevPageNo(buf []byte) uint32 {
|
|
|
|
|
|
// pageNo, _ := bin.GetUint32(buf[prevPageIdx:])
|
|
|
|
|
|
// return pageNo
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// func GetIndexRecordsSince(buf []byte) (pageNo uint32) {
|
|
|
|
|
|
// pageNo, _ = bin.GetUint32(buf)
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// func FindPageOnRecords(records []byte, timestamp uint32) (pageNo uint32) {
|
|
|
|
|
|
// comparator := ValueAtComparator{
|
|
|
|
|
|
// buf: records,
|
|
|
|
|
|
// timestamp: timestamp,
|
|
|
|
|
|
// }
|
|
|
|
|
|
// count := len(records) / IndexRecordSize
|
|
|
|
|
|
// elemIdx, _ := BinarySearch(count, comparator)
|
|
|
|
|
|
// pageNo, _ = bin.GetUint32(records[elemIdx*IndexRecordSize:])
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
2026-06-10 06:18:45 +03:00
|
|
|
|
// func findPageNoIdx(buf []byte, timestamp uint32) (idx int) {
|
|
|
|
|
|
// comparator := ValueAtComparator{
|
|
|
|
|
|
// buf: buf,
|
|
|
|
|
|
// timestamp: timestamp,
|
2026-05-31 20:01:28 +00:00
|
|
|
|
// }
|
2026-06-10 06:18:45 +03:00
|
|
|
|
// qty, _ := bin.GetUint16(buf[indexRecordsQtyIdx:])
|
|
|
|
|
|
// elemIdx, _ := BinarySearch(int(qty), comparator)
|
|
|
|
|
|
// return elemIdx
|
2026-05-31 20:01:28 +00:00
|
|
|
|
// }
|
|
|
|
|
|
|
2026-06-10 06:18:45 +03:00
|
|
|
|
// func findPageNoForDeleteSince(buf []byte, since uint32) (uint32, int, int) {
|
|
|
|
|
|
// comparator := ValueAtComparator{
|
|
|
|
|
|
// buf: buf,
|
|
|
|
|
|
// timestamp: since,
|
|
|
|
|
|
// }
|
|
|
|
|
|
// qty, _ := bin.GetUint16(buf[indexRecordsQtyIdx:])
|
|
|
|
|
|
// elemIdx, _ := BinarySearch(int(qty), comparator)
|
|
|
|
|
|
// pos := elemIdx * timestampSize
|
|
|
|
|
|
// timestamp, _ := bin.GetUint32(buf[pos:])
|
|
|
|
|
|
|
|
|
|
|
|
// if timestamp == since {
|
|
|
|
|
|
// if elemIdx == 0 {
|
|
|
|
|
|
// return 0, int(qty), -1
|
|
|
|
|
|
// }
|
|
|
|
|
|
// elemIdx--
|
|
|
|
|
|
// }
|
|
|
|
|
|
// pos = indexFooterIdx - (elemIdx+1)*PageNoSize
|
|
|
|
|
|
// pageNo, _ := bin.GetUint32(buf[pos:])
|
|
|
|
|
|
// return pageNo, int(qty), elemIdx
|
2026-05-31 20:01:28 +00:00
|
|
|
|
// }
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
2026-06-10 06:18:45 +03:00
|
|
|
|
// func getLastPageNo(buf []byte) (pageNo uint32) {
|
|
|
|
|
|
// qty, _ := bin.GetUint16(buf[indexRecordsQtyIdx:])
|
|
|
|
|
|
// pos := indexFooterIdx - qty*PageNoSize
|
|
|
|
|
|
// pageNo, _ = bin.GetUint32(buf[pos:])
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
2026-06-10 06:18:45 +03:00
|
|
|
|
// func getPageNo(buf []byte, idx int) (pageNo uint32) {
|
|
|
|
|
|
// pos := indexFooterIdx - (idx+1)*PageNoSize
|
|
|
|
|
|
// pageNo, _ = bin.GetUint32(buf[pos:])
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
2026-06-19 07:25:46 +03:00
|
|
|
|
// from index page
|
|
|
|
|
|
func ListPageNumbers(buf []byte) (pageNumbers []uint32) {
|
|
|
|
|
|
count, _ := bin.GetUint16(buf[indexRecordsCountIdx:])
|
|
|
|
|
|
pos := timestampSize
|
|
|
|
|
|
for range count {
|
|
|
|
|
|
pageNo, _ := bin.GetUint32(buf[pos:])
|
|
|
|
|
|
pageNumbers = append(pageNumbers, pageNo)
|
|
|
|
|
|
pos += IndexRecordSize
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
2026-06-10 06:18:45 +03:00
|
|
|
|
// // include since timestamp
|
|
|
|
|
|
// func listPageNumbersSince(buf []byte, timestamp uint32) (pageNumbers []uint32) {
|
|
|
|
|
|
// comparator := ValueAtComparator{
|
|
|
|
|
|
// buf: buf,
|
|
|
|
|
|
// timestamp: timestamp,
|
|
|
|
|
|
// }
|
|
|
|
|
|
// qty, _ := bin.GetUint16(buf[indexRecordsQtyIdx:])
|
|
|
|
|
|
// elemIdx, _ := BinarySearch(int(qty), comparator)
|
|
|
|
|
|
// pos := indexFooterIdx - (elemIdx+1)*PageNoSize
|
|
|
|
|
|
// for range qty {
|
|
|
|
|
|
// pageNo, _ := bin.GetUint32(buf[pos:])
|
|
|
|
|
|
// pageNumbers = append(pageNumbers, pageNo)
|
|
|
|
|
|
// pos -= PageNoSize
|
|
|
|
|
|
// }
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
// 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
|
|
|
|
|
|
// }
|