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 }