heroagent/pkg/data/ourdb/location.go
2025-04-23 04:18:28 +02:00

151 lines
4.2 KiB
Go

package ourdb
import (
"errors"
"fmt"
)
// Location represents a position in a database file
type Location struct {
FileNr uint16
Position uint32
}
// LocationNew creates a new Location from bytes
func (lut *LookupTable) LocationNew(b_ []byte) (Location, error) {
newLocation := Location{
FileNr: 0,
Position: 0,
}
// First verify keysize is valid
if lut.KeySize != 2 && lut.KeySize != 3 && lut.KeySize != 4 && lut.KeySize != 6 {
return newLocation, errors.New("keysize must be 2, 3, 4 or 6")
}
// Create padded b
b := make([]byte, lut.KeySize)
startIdx := int(lut.KeySize) - len(b_)
if startIdx < 0 {
return newLocation, errors.New("input bytes exceed keysize")
}
for i := 0; i < len(b_); i++ {
b[startIdx+i] = b_[i]
}
switch lut.KeySize {
case 2:
// Only position, 2 bytes big endian
newLocation.Position = uint32(b[0])<<8 | uint32(b[1])
newLocation.FileNr = 0
case 3:
// Only position, 3 bytes big endian
newLocation.Position = uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2])
newLocation.FileNr = 0
case 4:
// Only position, 4 bytes big endian
newLocation.Position = uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])
newLocation.FileNr = 0
case 6:
// 2 bytes file_nr + 4 bytes position, all big endian
newLocation.FileNr = uint16(b[0])<<8 | uint16(b[1])
newLocation.Position = uint32(b[2])<<24 | uint32(b[3])<<16 | uint32(b[4])<<8 | uint32(b[5])
}
// Verify limits based on keysize
switch lut.KeySize {
case 2:
if newLocation.Position > 0xFFFF {
return newLocation, errors.New("position exceeds max value for keysize=2 (max 65535)")
}
if newLocation.FileNr != 0 {
return newLocation, errors.New("file_nr must be 0 for keysize=2")
}
case 3:
if newLocation.Position > 0xFFFFFF {
return newLocation, errors.New("position exceeds max value for keysize=3 (max 16777215)")
}
if newLocation.FileNr != 0 {
return newLocation, errors.New("file_nr must be 0 for keysize=3")
}
case 4:
if newLocation.FileNr != 0 {
return newLocation, errors.New("file_nr must be 0 for keysize=4")
}
case 6:
// For keysize 6: both file_nr and position can use their full range
// No additional checks needed as u16 and u32 already enforce limits
}
return newLocation, nil
}
// ToBytes converts a Location to a 6-byte array
func (loc Location) ToBytes() ([]byte, error) {
bytes := make([]byte, 6)
// Put file_nr first (2 bytes)
bytes[0] = byte(loc.FileNr >> 8)
bytes[1] = byte(loc.FileNr)
// Put position next (4 bytes)
bytes[2] = byte(loc.Position >> 24)
bytes[3] = byte(loc.Position >> 16)
bytes[4] = byte(loc.Position >> 8)
bytes[5] = byte(loc.Position)
return bytes, nil
}
// ToLookupBytes converts a Location to bytes according to the keysize
func (loc Location) ToLookupBytes(keysize uint8) ([]byte, error) {
bytes := make([]byte, keysize)
switch keysize {
case 2:
if loc.Position > 0xFFFF {
return nil, errors.New("position exceeds max value for keysize=2 (max 65535)")
}
if loc.FileNr != 0 {
return nil, errors.New("file_nr must be 0 for keysize=2")
}
bytes[0] = byte(loc.Position >> 8)
bytes[1] = byte(loc.Position)
case 3:
if loc.Position > 0xFFFFFF {
return nil, errors.New("position exceeds max value for keysize=3 (max 16777215)")
}
if loc.FileNr != 0 {
return nil, errors.New("file_nr must be 0 for keysize=3")
}
bytes[0] = byte(loc.Position >> 16)
bytes[1] = byte(loc.Position >> 8)
bytes[2] = byte(loc.Position)
case 4:
if loc.FileNr != 0 {
return nil, errors.New("file_nr must be 0 for keysize=4")
}
bytes[0] = byte(loc.Position >> 24)
bytes[1] = byte(loc.Position >> 16)
bytes[2] = byte(loc.Position >> 8)
bytes[3] = byte(loc.Position)
case 6:
bytes[0] = byte(loc.FileNr >> 8)
bytes[1] = byte(loc.FileNr)
bytes[2] = byte(loc.Position >> 24)
bytes[3] = byte(loc.Position >> 16)
bytes[4] = byte(loc.Position >> 8)
bytes[5] = byte(loc.Position)
default:
return nil, fmt.Errorf("invalid keysize: %d", keysize)
}
return bytes, nil
}
// ToUint64 converts a Location to uint64, with file_nr as most significant (big endian)
func (loc Location) ToUint64() (uint64, error) {
return (uint64(loc.FileNr) << 32) | uint64(loc.Position), nil
}