port ourdb from vlang
This commit is contained in:
168
ourdb/src/location.rs
Normal file
168
ourdb/src/location.rs
Normal file
@@ -0,0 +1,168 @@
|
||||
use crate::error::Error;
|
||||
|
||||
/// Location represents a physical position in a database file
|
||||
///
|
||||
/// It consists of a file number and a position within that file.
|
||||
/// This allows OurDB to span multiple files for large datasets.
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub struct Location {
|
||||
/// File number (0-65535)
|
||||
pub file_nr: u16,
|
||||
/// Position within the file
|
||||
pub position: u32,
|
||||
}
|
||||
|
||||
impl Location {
|
||||
/// Creates a new Location from bytes based on keysize
|
||||
///
|
||||
/// - keysize = 2: Only position (2 bytes), file_nr = 0
|
||||
/// - keysize = 3: Only position (3 bytes), file_nr = 0
|
||||
/// - keysize = 4: Only position (4 bytes), file_nr = 0
|
||||
/// - keysize = 6: file_nr (2 bytes) + position (4 bytes)
|
||||
pub fn from_bytes(bytes: &[u8], keysize: u8) -> Result<Self, Error> {
|
||||
// Validate keysize
|
||||
if ![2, 3, 4, 6].contains(&keysize) {
|
||||
return Err(Error::InvalidOperation(format!("Invalid keysize: {}", keysize)));
|
||||
}
|
||||
|
||||
// Create padded bytes
|
||||
let mut padded = vec![0u8; keysize as usize];
|
||||
let start_idx = keysize as usize - bytes.len();
|
||||
if start_idx < 0 {
|
||||
return Err(Error::InvalidOperation("Input bytes exceed keysize".to_string()));
|
||||
}
|
||||
|
||||
for (i, &b) in bytes.iter().enumerate() {
|
||||
if i + start_idx < padded.len() {
|
||||
padded[start_idx + i] = b;
|
||||
}
|
||||
}
|
||||
|
||||
let mut location = Location::default();
|
||||
|
||||
match keysize {
|
||||
2 => {
|
||||
// Only position, 2 bytes big endian
|
||||
location.position = u32::from(padded[0]) << 8 | u32::from(padded[1]);
|
||||
location.file_nr = 0;
|
||||
|
||||
// Verify limits
|
||||
if location.position > 0xFFFF {
|
||||
return Err(Error::InvalidOperation(
|
||||
"Position exceeds max value for keysize=2 (max 65535)".to_string()
|
||||
));
|
||||
}
|
||||
},
|
||||
3 => {
|
||||
// Only position, 3 bytes big endian
|
||||
location.position = u32::from(padded[0]) << 16 | u32::from(padded[1]) << 8 | u32::from(padded[2]);
|
||||
location.file_nr = 0;
|
||||
|
||||
// Verify limits
|
||||
if location.position > 0xFFFFFF {
|
||||
return Err(Error::InvalidOperation(
|
||||
"Position exceeds max value for keysize=3 (max 16777215)".to_string()
|
||||
));
|
||||
}
|
||||
},
|
||||
4 => {
|
||||
// Only position, 4 bytes big endian
|
||||
location.position = u32::from(padded[0]) << 24 | u32::from(padded[1]) << 16
|
||||
| u32::from(padded[2]) << 8 | u32::from(padded[3]);
|
||||
location.file_nr = 0;
|
||||
},
|
||||
6 => {
|
||||
// 2 bytes file_nr + 4 bytes position, all big endian
|
||||
location.file_nr = u16::from(padded[0]) << 8 | u16::from(padded[1]);
|
||||
location.position = u32::from(padded[2]) << 24 | u32::from(padded[3]) << 16
|
||||
| u32::from(padded[4]) << 8 | u32::from(padded[5]);
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
Ok(location)
|
||||
}
|
||||
|
||||
/// Converts the location to bytes (always 6 bytes)
|
||||
///
|
||||
/// Format: [file_nr (2 bytes)][position (4 bytes)]
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::with_capacity(6);
|
||||
|
||||
// Put file_nr first (2 bytes)
|
||||
bytes.push((self.file_nr >> 8) as u8);
|
||||
bytes.push(self.file_nr as u8);
|
||||
|
||||
// Put position next (4 bytes)
|
||||
bytes.push((self.position >> 24) as u8);
|
||||
bytes.push((self.position >> 16) as u8);
|
||||
bytes.push((self.position >> 8) as u8);
|
||||
bytes.push(self.position as u8);
|
||||
|
||||
bytes
|
||||
}
|
||||
|
||||
/// Converts the location to a u64 value
|
||||
///
|
||||
/// The file_nr is stored in the most significant bits
|
||||
pub fn to_u64(&self) -> u64 {
|
||||
(u64::from(self.file_nr) << 32) | u64::from(self.position)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_location_from_bytes_keysize_2() {
|
||||
let bytes = vec![0x12, 0x34];
|
||||
let location = Location::from_bytes(&bytes, 2).unwrap();
|
||||
assert_eq!(location.file_nr, 0);
|
||||
assert_eq!(location.position, 0x1234);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_location_from_bytes_keysize_3() {
|
||||
let bytes = vec![0x12, 0x34, 0x56];
|
||||
let location = Location::from_bytes(&bytes, 3).unwrap();
|
||||
assert_eq!(location.file_nr, 0);
|
||||
assert_eq!(location.position, 0x123456);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_location_from_bytes_keysize_4() {
|
||||
let bytes = vec![0x12, 0x34, 0x56, 0x78];
|
||||
let location = Location::from_bytes(&bytes, 4).unwrap();
|
||||
assert_eq!(location.file_nr, 0);
|
||||
assert_eq!(location.position, 0x12345678);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_location_from_bytes_keysize_6() {
|
||||
let bytes = vec![0xAB, 0xCD, 0x12, 0x34, 0x56, 0x78];
|
||||
let location = Location::from_bytes(&bytes, 6).unwrap();
|
||||
assert_eq!(location.file_nr, 0xABCD);
|
||||
assert_eq!(location.position, 0x12345678);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_location_to_bytes() {
|
||||
let location = Location {
|
||||
file_nr: 0xABCD,
|
||||
position: 0x12345678,
|
||||
};
|
||||
let bytes = location.to_bytes();
|
||||
assert_eq!(bytes, vec![0xAB, 0xCD, 0x12, 0x34, 0x56, 0x78]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_location_to_u64() {
|
||||
let location = Location {
|
||||
file_nr: 0xABCD,
|
||||
position: 0x12345678,
|
||||
};
|
||||
let value = location.to_u64();
|
||||
assert_eq!(value, 0xABCD_0000_0000 | 0x12345678);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user