Add decode impls for headers

next
Sayan Nandan 1 year ago
parent 96acecb2c3
commit 0e77946714
No known key found for this signature in database
GPG Key ID: 42EEDF4AE9D96B54

@ -58,14 +58,20 @@
use super::versions::HeaderVersion;
/// magic
const SR0_MAGIC: u64 = 0x4F48534159414E21;
const SR2_PTR_WIDTH: u8 = HostPointerWidth::new().value_u8();
const SR3_ENDIAN: u8 = HostEndian::new().value_u8();
const SR4_ARCH: u8 = HostArch::new().value_u8();
const SR5_OS: u8 = HostOS::new().value_u8();
/// host ptr width
const SR2_PTR_WIDTH: HostPointerWidth = HostPointerWidth::new();
/// host endian
const SR3_ENDIAN: HostEndian = HostEndian::new();
/// host arch
const SR4_ARCH: HostArch = HostArch::new();
/// host os
const SR5_OS: HostOS = HostOS::new();
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, sky_macros::EnumMethods)]
/// Host architecture enumeration for common platforms
pub enum HostArch {
X86 = 0,
X86_64 = 1,
@ -93,21 +99,28 @@ impl HostArch {
panic!("Unsupported target architecture")
}
}
pub const fn new_with_val(v: u8) -> Self {
match v {
pub const fn try_new_with_val(v: u8) -> Option<Self> {
Some(match v {
0 => HostArch::X86,
1 => HostArch::X86_64,
2 => HostArch::ARM,
3 => HostArch::ARM64,
4 => HostArch::MIPS,
5 => HostArch::PowerPC,
_ => panic!("unknown arch"),
_ => return None,
})
}
pub const fn new_with_val(v: u8) -> Self {
match Self::try_new_with_val(v) {
Some(v) => v,
None => panic!("unknown arch"),
}
}
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, sky_macros::EnumMethods)]
/// Host OS enumeration for common operating systems
pub enum HostOS {
// T1
Linux = 0,
@ -162,8 +175,8 @@ impl HostOS {
panic!("unknown os")
}
}
pub const fn new_with_val(v: u8) -> Self {
match v {
pub const fn try_new_with_val(v: u8) -> Option<Self> {
Some(match v {
0 => HostOS::Linux,
1 => HostOS::Windows,
2 => HostOS::MacOS,
@ -178,13 +191,20 @@ impl HostOS {
11 => HostOS::Fuchsia,
12 => HostOS::Redox,
13 => HostOS::DragonFly,
_ => panic!("unknown OS"),
_ => return None,
})
}
pub const fn new_with_val(v: u8) -> Self {
match Self::try_new_with_val(v) {
Some(v) => v,
None => panic!("unknown OS"),
}
}
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, sky_macros::EnumMethods)]
/// Host endian enumeration
pub enum HostEndian {
Big = 0,
Little = 1,
@ -198,17 +218,24 @@ impl HostEndian {
Self::Big
}
}
pub const fn new_with_val(v: u8) -> Self {
match v {
pub const fn try_new_with_val(v: u8) -> Option<Self> {
Some(match v {
0 => HostEndian::Big,
1 => HostEndian::Little,
_ => panic!("Unknown endian"),
_ => return None,
})
}
pub const fn new_with_val(v: u8) -> Self {
match Self::try_new_with_val(v) {
Some(v) => v,
None => panic!("Unknown endian"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, sky_macros::EnumMethods)]
#[repr(u8)]
/// Host pointer width enumeration
pub enum HostPointerWidth {
P32 = 0,
P64 = 1,
@ -222,22 +249,43 @@ impl HostPointerWidth {
_ => panic!("unknown pointer width"),
}
}
pub const fn new_with_val(v: u8) -> Self {
match v {
pub const fn try_new_with_val(v: u8) -> Option<Self> {
Some(match v {
0 => HostPointerWidth::P32,
1 => HostPointerWidth::P64,
_ => panic!("Unknown pointer width"),
_ => return None,
})
}
pub const fn new_with_val(v: u8) -> Self {
match Self::try_new_with_val(v) {
Some(v) => v,
None => panic!("Unknown pointer width"),
}
}
}
#[derive(Debug)]
#[derive(Debug, PartialEq)]
/// The static record
pub struct StaticRecordUV {
data: [u8; 16],
}
impl StaticRecordUV {
pub const fn create(sr1_version: HeaderVersion) -> Self {
const OFFSET_P0: usize = 0;
const OFFSET_P1: usize = sizeof!(u64);
const OFFSET_P2: usize = Self::OFFSET_P1 + sizeof!(u32);
const OFFSET_P3: usize = Self::OFFSET_P2 + 1;
const OFFSET_P4: usize = Self::OFFSET_P3 + 1;
const OFFSET_P5: usize = Self::OFFSET_P4 + 1;
const _ENSURE: () = assert!(Self::OFFSET_P5 == (sizeof!(Self) - 1));
#[inline(always)]
pub const fn new(
sr1_version: HeaderVersion,
sr2_ptr_width: HostPointerWidth,
sr3_endian: HostEndian,
sr4_arch: HostArch,
sr5_os: HostOS,
) -> Self {
let mut data = [0u8; 16];
let magic_buf = SR0_MAGIC.to_le_bytes();
let version_buf = sr1_version.little_endian_u64();
@ -247,61 +295,68 @@ impl StaticRecordUV {
data[i + sizeof!(u64)] = version_buf[i];
i += 1;
}
data[sizeof!(u64, 2) - 4] = SR2_PTR_WIDTH;
data[sizeof!(u64, 2) - 3] = SR3_ENDIAN;
data[sizeof!(u64, 2) - 2] = SR4_ARCH;
data[sizeof!(u64, 2) - 1] = SR5_OS;
data[sizeof!(u64, 2) - 4] = sr2_ptr_width.value_u8();
data[sizeof!(u64, 2) - 3] = sr3_endian.value_u8();
data[sizeof!(u64, 2) - 2] = sr4_arch.value_u8();
data[sizeof!(u64, 2) - 1] = sr5_os.value_u8();
Self { data }
}
#[inline(always)]
pub const fn create(sr1_version: HeaderVersion) -> Self {
Self::new(sr1_version, SR2_PTR_WIDTH, SR3_ENDIAN, SR4_ARCH, SR5_OS)
}
/// Decode and validate a SR
///
/// WARNING: NOT CONTEXTUAL! VALIDATE YOUR OWN STUFF!
pub fn decode(data: [u8; 16]) -> Option<Self> {
let _ = Self::_ENSURE;
let slf = Self { data };
// p0: magic; the magic HAS to be the same
if u64::from_le(slf.read_qword(Self::OFFSET_P0)) != SR0_MAGIC {
return None;
}
let sr2_ptr = HostPointerWidth::try_new_with_val(slf.read_byte(Self::OFFSET_P2))?; // p2: ptr width
let sr3_endian = HostEndian::try_new_with_val(slf.read_byte(Self::OFFSET_P3))?; // p3: endian
let sr4_arch = HostArch::try_new_with_val(slf.read_byte(Self::OFFSET_P4))?; // p4: arch
let sr5_os = HostOS::try_new_with_val(slf.read_byte(Self::OFFSET_P5))?; // p5: os
Some(Self::new(
HeaderVersion::__new(u32::from_le(slf.read_dword(Self::OFFSET_P1))),
sr2_ptr,
sr3_endian,
sr4_arch,
sr5_os,
))
}
}
impl StaticRecordUV {
pub const fn get_ref(&self) -> &[u8] {
&self.data
}
pub const fn read_p0_magic(&self) -> u64 {
self.read_qword(0)
self.read_qword(Self::OFFSET_P0)
}
pub const fn read_p1_header_version(&self) -> HeaderVersion {
HeaderVersion::__new(self.read_dword(sizeof!(u64)))
HeaderVersion::__new(self.read_dword(Self::OFFSET_P1))
}
pub const fn read_p2_ptr_width(&self) -> HostPointerWidth {
HostPointerWidth::new_with_val(self.read_byte(12))
HostPointerWidth::new_with_val(self.read_byte(Self::OFFSET_P2))
}
pub const fn read_p3_endian(&self) -> HostEndian {
HostEndian::new_with_val(self.read_byte(13))
HostEndian::new_with_val(self.read_byte(Self::OFFSET_P3))
}
pub const fn read_p4_arch(&self) -> HostArch {
HostArch::new_with_val(self.read_byte(14))
HostArch::new_with_val(self.read_byte(Self::OFFSET_P4))
}
pub const fn read_p5_os(&self) -> HostOS {
HostOS::new_with_val(self.read_byte(15))
HostOS::new_with_val(self.read_byte(Self::OFFSET_P5))
}
}
impl_stack_read_primitives!(unsafe impl for StaticRecordUV {});
/*
File identity
*/
/// The file scope
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, sky_macros::EnumMethods)]
pub enum FileScope {
TransactionLog = 0,
TransactionLogCompacted = 1,
}
impl FileScope {
pub const fn new(id: u32) -> Self {
match id {
0 => Self::TransactionLog,
1 => Self::TransactionLogCompacted,
_ => panic!("unknown filescope"),
}
}
}
#[test]
fn test_static_record_encode_decode() {
fn test_static_record() {
let static_record = StaticRecordUV::create(super::versions::v1::V1_HEADER_VERSION);
assert_eq!(static_record.read_p0_magic(), SR0_MAGIC);
assert_eq!(
@ -313,3 +368,10 @@ fn test_static_record_encode_decode() {
assert_eq!(static_record.read_p4_arch(), HostArch::new());
assert_eq!(static_record.read_p5_os(), HostOS::new());
}
#[test]
fn test_static_record_encode_decode() {
let static_record = StaticRecordUV::create(super::versions::v1::V1_HEADER_VERSION);
let static_record_decoded = StaticRecordUV::decode(static_record.data).unwrap();
assert_eq!(static_record, static_record_decoded);
}

@ -53,7 +53,7 @@
*/
use crate::engine::storage::{
header::{FileScope, StaticRecordUV},
header::StaticRecordUV,
versions::{self, DriverVersion, ServerVersion},
};
@ -78,8 +78,33 @@ impl StaticRecord {
| Server | Driver | File |File|Spec|
| version | Version | Scope |Spec|ID |
+----------+----------+----------+---------+
0, 63
*/
/// The file scope
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, sky_macros::EnumMethods)]
pub enum FileScope {
TransactionLog = 0,
TransactionLogCompacted = 1,
}
impl FileScope {
pub const fn try_new(id: u64) -> Option<Self> {
Some(match id {
0 => Self::TransactionLog,
1 => Self::TransactionLogCompacted,
_ => return None,
})
}
pub const fn new(id: u64) -> Self {
match Self::try_new(id) {
Some(v) => v,
None => panic!("unknown filescope"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, sky_macros::EnumMethods)]
#[repr(u8)]
pub enum FileSpecifier {
@ -87,9 +112,15 @@ pub enum FileSpecifier {
}
impl FileSpecifier {
pub const fn new(v: u32) -> Self {
match v {
pub const fn try_new(v: u32) -> Option<Self> {
Some(match v {
0 => Self::GNSTxnLog,
_ => return None,
})
}
pub const fn new(v: u32) -> Self {
match Self::try_new(v) {
Some(v) => v,
_ => panic!("unknown filespecifier"),
}
}
@ -110,16 +141,49 @@ pub struct MetadataRecord {
impl_stack_read_primitives!(unsafe impl for MetadataRecord {});
impl MetadataRecord {
pub const fn new(
/// Decodes a given metadata record, validating all data for correctness.
///
/// WARNING: That means you need to do contextual validation! This function is not aware of any context
pub fn decode(data: [u8; 32]) -> Option<Self> {
let slf = Self { data };
let server_version =
ServerVersion::__new(u64::from_le(slf.read_qword(Self::MDR_OFFSET_P0)));
let driver_version =
DriverVersion::__new(u64::from_le(slf.read_qword(Self::MDR_OFFSET_P1)));
let file_scope = FileScope::try_new(u64::from_le(slf.read_qword(Self::MDR_OFFSET_P2)))?;
let file_spec = FileSpecifier::try_new(u32::from_le(slf.read_dword(Self::MDR_OFFSET_P3)))?;
let file_spec_id =
FileSpecifierVersion::__new(u32::from_le(slf.read_dword(Self::MDR_OFFSET_P4)));
Some(Self::new_full(
server_version,
driver_version,
file_scope,
file_spec,
file_spec_id,
))
}
}
impl MetadataRecord {
const MDR_OFFSET_P0: usize = 0;
const MDR_OFFSET_P1: usize = sizeof!(u64);
const MDR_OFFSET_P2: usize = Self::MDR_OFFSET_P1 + sizeof!(u64);
const MDR_OFFSET_P3: usize = Self::MDR_OFFSET_P2 + sizeof!(u64);
const MDR_OFFSET_P4: usize = Self::MDR_OFFSET_P3 + sizeof!(u32);
const _ENSURE: () = assert!(Self::MDR_OFFSET_P4 == (sizeof!(Self) - sizeof!(u32)));
pub const fn new_full(
server_version: ServerVersion,
driver_version: DriverVersion,
scope: FileScope,
specifier: FileSpecifier,
specifier_id: FileSpecifierVersion,
) -> Self {
let _ = Self::_ENSURE;
let mut ret = [0u8; 32];
let mut i = 0;
// read buf
let server_version = versions::v1::V1_SERVER_VERSION.little_endian();
let driver_version = versions::v1::V1_DRIVER_VERSION.little_endian();
let server_version = server_version.little_endian();
let driver_version = driver_version.little_endian();
let file_scope = scope.value_qword().to_le_bytes();
// specifier + specifier ID
let file_specifier_and_id: u64 =
@ -134,20 +198,36 @@ impl MetadataRecord {
}
Self { data: ret }
}
pub const fn new(
scope: FileScope,
specifier: FileSpecifier,
specifier_id: FileSpecifierVersion,
) -> Self {
Self::new_full(
versions::v1::V1_SERVER_VERSION,
versions::v1::V1_DRIVER_VERSION,
scope,
specifier,
specifier_id,
)
}
}
impl MetadataRecord {
pub const fn read_p0_server_version(&self) -> ServerVersion {
ServerVersion::__new(self.read_qword(0))
ServerVersion::__new(self.read_qword(Self::MDR_OFFSET_P0))
}
pub const fn read_p1_driver_version(&self) -> DriverVersion {
DriverVersion::__new(self.read_qword(sizeof!(u64)))
DriverVersion::__new(self.read_qword(Self::MDR_OFFSET_P1))
}
pub const fn read_p2_file_scope(&self) -> FileScope {
FileScope::new(self.read_qword(sizeof!(u128)) as u32)
FileScope::new(self.read_qword(Self::MDR_OFFSET_P2))
}
pub const fn read_p3_file_spec(&self) -> FileSpecifier {
FileSpecifier::new(self.read_dword(sizeof!(u64, 3)))
FileSpecifier::new(self.read_dword(Self::MDR_OFFSET_P3))
}
pub const fn read_p4_file_spec_version(&self) -> FileSpecifierVersion {
FileSpecifierVersion(self.read_dword(sizeof!(u64, 3) + sizeof!(u32)))
FileSpecifierVersion(self.read_dword(Self::MDR_OFFSET_P4))
}
}
@ -173,11 +253,17 @@ pub enum HostRunMode {
}
impl HostRunMode {
pub const fn new_with_val(v: u8) -> Self {
match v {
pub const fn try_new_with_val(v: u8) -> Option<Self> {
Some(match v {
0 => Self::Dev,
1 => Self::Prod,
_ => panic!("unknown hostrunmode"),
_ => return None,
})
}
pub const fn new_with_val(v: u8) -> Self {
match Self::try_new_with_val(v) {
Some(v) => v,
None => panic!("unknown hostrunmode"),
}
}
}
@ -192,6 +278,13 @@ pub struct VariableHostRecord {
}
impl VariableHostRecord {
const VHR_OFFSET_P0: usize = 0;
const VHR_OFFSET_P1: usize = sizeof!(u128);
const VHR_OFFSET_P2A: usize = Self::VHR_OFFSET_P1 + sizeof!(u128);
const VHR_OFFSET_P2B: usize = Self::VHR_OFFSET_P2A + sizeof!(u32);
const VHR_OFFSET_P3: usize = Self::VHR_OFFSET_P2B + sizeof!(u32);
const VHR_OFFSET_P4: usize = Self::VHR_OFFSET_P3 + sizeof!(u64);
const _ENSURE: () = assert!(Self::VHR_OFFSET_P4 == sizeof!(VHRConstSection) - sizeof!(u64));
pub fn new(
p0_host_epoch_time: u128,
p1_host_uptime: u128,
@ -200,6 +293,7 @@ impl VariableHostRecord {
p3_host_startup_counter: u64,
p5_host_name: Box<[u8]>,
) -> Self {
let _ = Self::_ENSURE;
let p4_host_name_length = p5_host_name.len();
let mut variable_record_fl = [0u8; 56];
variable_record_fl[0..16].copy_from_slice(&p0_host_epoch_time.to_le_bytes());
@ -231,23 +325,26 @@ impl VariableHostRecord {
p5_host_name,
)
}
}
impl VariableHostRecord {
pub const fn read_p0_epoch_time(&self) -> u128 {
self.read_xmmword(0)
self.read_xmmword(Self::VHR_OFFSET_P0)
}
pub const fn read_p1_uptime(&self) -> u128 {
self.read_xmmword(sizeof!(u128))
self.read_xmmword(Self::VHR_OFFSET_P1)
}
pub const fn read_p2a_setting_version_id(&self) -> u32 {
self.read_dword(sizeof!(u128, 2))
self.read_dword(Self::VHR_OFFSET_P2A)
}
pub const fn read_p2b_run_mode(&self) -> HostRunMode {
HostRunMode::new_with_val(self.read_dword(sizeof!(u128, 2) + sizeof!(u32)) as u8)
HostRunMode::new_with_val(self.read_dword(Self::VHR_OFFSET_P2B) as u8)
}
pub const fn read_p3_startup_counter(&self) -> u64 {
self.read_qword(sizeof!(u128, 2) + sizeof!(u32, 2))
self.read_qword(Self::VHR_OFFSET_P3)
}
pub const fn read_p4_host_name_length(&self) -> u64 {
self.read_qword(sizeof!(u128, 2) + sizeof!(u32, 2) + sizeof!(u64))
self.read_qword(Self::VHR_OFFSET_P4)
}
pub fn read_p5_host_name(&self) -> &[u8] {
&self.host_name
@ -313,10 +410,10 @@ impl SDSSHeader {
self.dr_1_vhr.host_name.as_ref()
}
pub fn calculate_header_size(&self) -> usize {
sizeof!(StaticRecord)
+ sizeof!(MetadataRecord)
+ sizeof!(VHRConstSection)
+ self.dr_1_vhr.host_name.len()
Self::calculate_fixed_header_size() + self.dr_1_vhr.host_name.len()
}
pub const fn calculate_fixed_header_size() -> usize {
sizeof!(StaticRecord) + sizeof!(MetadataRecord) + sizeof!(VHRConstSection)
}
}

@ -31,24 +31,38 @@ use {
IoResult,
},
std::{
fs::{File, OpenOptions},
io::Write,
fs::{self, File, OpenOptions},
io::{Error as IoError, ErrorKind, Read, Seek, SeekFrom, Write},
},
};
#[derive(Debug)]
pub enum SDSSError {
SRVersionMismatch,
IoError(IoError),
}
impl From<IoError> for SDSSError {
fn from(e: IoError) -> Self {
Self::IoError(e)
}
}
pub type SDSSResult<T> = Result<T, SDSSError>;
/*
Writer interface
*/
pub trait RawWriterInterface: Sized {
fn open_truncated(fname: &str) -> IoResult<Self>;
fn open_create(fname: &str) -> IoResult<Self>;
fn fopen_truncated(fname: &str) -> IoResult<Self>;
fn fopen_create(fname: &str) -> IoResult<Self>;
fn fwrite_all(&mut self, bytes: &[u8]) -> IoResult<()>;
fn fsync_all(&mut self) -> IoResult<()>;
}
impl RawWriterInterface for File {
fn open_truncated(fname: &str) -> IoResult<Self> {
fn fopen_truncated(fname: &str) -> IoResult<Self> {
OpenOptions::new()
.write(true)
.truncate(true)
@ -56,7 +70,7 @@ impl RawWriterInterface for File {
.open(fname)
}
fn open_create(fname: &str) -> IoResult<Self> {
fn fopen_create(fname: &str) -> IoResult<Self> {
File::create(fname)
}
@ -80,7 +94,7 @@ pub struct SDSSWriter<W> {
impl<W: RawWriterInterface> SDSSWriter<W> {
pub fn open_create_with_header(file: &str, header: SDSSHeader) -> IoResult<Self> {
let mut w = W::open_create(file)?;
let mut w = W::fopen_create(file)?;
w.fwrite_all(header.get0_sr())?;
w.fwrite_all(header.get1_dr_0_mdr())?;
w.fwrite_all(header.get1_dr_1_vhr_0())?;
@ -102,3 +116,57 @@ impl<W: RawWriterInterface> SDSSWriter<W> {
self.fsync_write(num.be().repr())
}
}
/*
Read interface
*/
pub trait RawReaderInterface: Sized {
fn fopen(fname: &str) -> IoResult<Self>;
fn fread_exact_seek(&mut self, buf: &mut [u8]) -> IoResult<()>;
fn fread_to_end(&mut self, buf: &mut Vec<u8>) -> IoResult<()>;
}
impl RawReaderInterface for File {
fn fopen(fname: &str) -> IoResult<Self> {
File::open(fname)
}
fn fread_exact_seek(&mut self, buf: &mut [u8]) -> IoResult<()> {
self.read_exact(buf)?;
let _ = self.seek(SeekFrom::Start(buf.len() as _))?;
Ok(())
}
fn fread_to_end(&mut self, buf: &mut Vec<u8>) -> IoResult<()> {
match self.read_to_end(buf) {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
}
}
pub struct FileBuffered {
cursor: usize,
base: Vec<u8>,
}
impl RawReaderInterface for FileBuffered {
fn fopen(name: &str) -> IoResult<Self> {
Ok(Self {
base: fs::read(name)?,
cursor: 0,
})
}
fn fread_exact_seek(&mut self, buf: &mut [u8]) -> IoResult<()> {
let l = self.base[self.cursor..].len();
if l >= buf.len() {
self.cursor += buf.len();
Ok(buf.copy_from_slice(&self.base[self.cursor..]))
} else {
Err(ErrorKind::UnexpectedEof.into())
}
}
fn fread_to_end(&mut self, buf: &mut Vec<u8>) -> IoResult<()> {
buf.extend_from_slice(&self.base[self.cursor..]);
Ok(())
}
}

Loading…
Cancel
Save