Optimize header implementation

The SDSS header impls were way too complex, and unnecessarily linked
to the file system API abstractions. Now we have a more decoupled API
with optional variable headers.
next
Sayan Nandan 12 months ago
parent c4d51ac8e7
commit ef3d71f593
No known key found for this signature in database
GPG Key ID: 42EEDF4AE9D96B54

@ -25,10 +25,7 @@
*/
use {
crate::engine::{
error::{Error, QueryResult},
storage::v1::header_meta::HostRunMode,
},
crate::engine::error::{Error, QueryResult},
parking_lot::RwLock,
std::collections::{hash_map::Entry, HashMap},
};
@ -53,7 +50,7 @@ impl SysConfig {
pub(super) fn test_default() -> Self {
Self {
auth_data: RwLock::new(None),
host_data: SysHostData::new(0, HostRunMode::Prod, 0),
host_data: SysHostData::new(0, 0),
}
}
/// Returns a handle to the authentication data
@ -70,25 +67,20 @@ impl SysConfig {
/// The host data section (system.host)
pub struct SysHostData {
startup_counter: u64,
run_mode: HostRunMode,
settings_version: u32,
}
impl SysHostData {
/// New [`SysHostData`]
pub fn new(startup_counter: u64, run_mode: HostRunMode, settings_version: u32) -> Self {
pub fn new(startup_counter: u64, settings_version: u32) -> Self {
Self {
startup_counter,
run_mode,
settings_version,
}
}
pub fn startup_counter(&self) -> u64 {
self.startup_counter
}
pub fn run_mode(&self) -> HostRunMode {
self.run_mode
}
pub fn settings_version(&self) -> u32 {
self.settings_version
}

@ -176,14 +176,10 @@ impl GlobalInstanceLike for Global {
space_name, space_uuid, model_name, model_uuid,
))?;
// init driver
let driver = storage::v1::data_batch::create(
&storage::v1::loader::SEInitState::model_path(
let driver =
storage::v1::data_batch::create(&storage::v1::loader::SEInitState::model_path(
space_name, space_uuid, model_name, model_uuid,
),
self.sys_cfg().host_data().settings_version(),
self.sys_cfg().host_data().run_mode(),
self.sys_cfg().host_data().startup_counter(),
)?;
))?;
self.get_state().mdl_driver.write().insert(
ModelUniqueID::new(space_name, model_name, model_uuid),
drivers::FractalModelDriver::init(driver),

@ -35,7 +35,6 @@ use {
storage::{
self,
v1::{
header_meta::HostRunMode,
memfs::{NullFS, VirtualFS},
RawFSInterface,
},
@ -78,7 +77,7 @@ impl<Fs: RawFSInterface> TestGlobal<Fs> {
impl<Fs: RawFSInterface> TestGlobal<Fs> {
pub fn new_with_driver_id(log_name: &str) -> Self {
let gns = GlobalNS::empty();
let driver = storage::v1::loader::open_gns_driver(log_name, 0, HostRunMode::Dev, 0, &gns)
let driver = storage::v1::loader::open_gns_driver(log_name, &gns)
.unwrap()
.into_inner();
Self::new(gns, 0, GNSTransactionDriverAnyFS::new(driver))
@ -131,14 +130,10 @@ impl<Fs: RawFSInterface> GlobalInstanceLike for TestGlobal<Fs> {
Fs::fs_create_dir(&storage::v1::loader::SEInitState::model_dir(
space_name, space_uuid, model_name, model_uuid,
))?;
let driver = storage::v1::data_batch::create(
&storage::v1::loader::SEInitState::model_path(
let driver =
storage::v1::data_batch::create(&storage::v1::loader::SEInitState::model_path(
space_name, space_uuid, model_name, model_uuid,
),
self.sys_cfg().host_data().settings_version(),
self.sys_cfg().host_data().run_mode(),
self.sys_cfg().host_data().startup_counter(),
)?;
))?;
self.model_drivers.write().insert(
ModelUniqueID::new(space_name, model_name, model_uuid),
FractalModelDriver::init(driver),

@ -370,7 +370,7 @@ impl StaticRecordUVRaw {
if u64::from_le(slf.data.read_qword(Self::OFFSET_P0)) != SR0_MAGIC {
return None;
}
let sr1_header_version = HeaderVersion::__new(slf.data.read_dword(Self::OFFSET_P1));
let sr1_header_version = HeaderVersion::__new(slf.data.read_dword(Self::OFFSET_P1) as _);
let sr2_ptr = HostPointerWidth::try_new_with_val(slf.data.read_byte(Self::OFFSET_P2))?; // p2: ptr width
let sr3_endian = HostEndian::try_new_with_val(slf.data.read_byte(Self::OFFSET_P3))?; // p3: endian
let sr4_arch = HostArch::try_new_with_val(slf.data.read_byte(Self::OFFSET_P4))?; // p4: arch
@ -393,7 +393,7 @@ impl StaticRecordUVRaw {
self.data.read_qword(Self::OFFSET_P0)
}
pub const fn read_p1_header_version(&self) -> HeaderVersion {
HeaderVersion::__new(self.data.read_dword(Self::OFFSET_P1))
HeaderVersion::__new(self.data.read_dword(Self::OFFSET_P1) as _)
}
pub const fn read_p2_ptr_width(&self) -> HostPointerWidth {
HostPointerWidth::new_with_val(self.data.read_byte(Self::OFFSET_P2))

@ -45,24 +45,16 @@ pub(super) use restore::{DecodedBatchEvent, DecodedBatchEventKind, NormalBatch};
pub use {persist::DataBatchPersistDriver, restore::DataBatchRestoreDriver};
use {
super::{header_meta, rw::SDSSFileIO, RawFSInterface, SDSSResult},
super::{rw::SDSSFileIO, spec, RawFSInterface, SDSSResult},
crate::engine::core::model::Model,
};
const LOG_SPECIFIER_VERSION: header_meta::FileSpecifierVersion =
header_meta::FileSpecifierVersion::__new(0);
/// Re-initialize an existing batch journal and read all its data into model
pub fn reinit<Fs: RawFSInterface>(
name: &str,
model: &Model,
) -> SDSSResult<DataBatchPersistDriver<Fs>> {
let (_header, f) = SDSSFileIO::<Fs>::open::<false>(
name,
header_meta::FileScope::Journal,
header_meta::FileSpecifier::TableDataBatch,
LOG_SPECIFIER_VERSION,
)?;
let (f, _header) = SDSSFileIO::<Fs>::open::<spec::DataBatchJournalV1>(name)?;
// restore
let mut restore_driver = DataBatchRestoreDriver::new(f)?;
restore_driver.read_data_batch_into_model(model)?;
@ -70,20 +62,7 @@ pub fn reinit<Fs: RawFSInterface>(
}
/// Create a new batch journal
pub fn create<Fs: RawFSInterface>(
path: &str,
host_setting_version: u32,
host_run_mode: header_meta::HostRunMode,
host_startup_counter: u64,
) -> SDSSResult<DataBatchPersistDriver<Fs>> {
let f = SDSSFileIO::<Fs>::create(
path,
header_meta::FileScope::Journal,
header_meta::FileSpecifier::TableDataBatch,
LOG_SPECIFIER_VERSION,
host_setting_version,
host_run_mode,
host_startup_counter,
)?;
pub fn create<Fs: RawFSInterface>(path: &str) -> SDSSResult<DataBatchPersistDriver<Fs>> {
let f = SDSSFileIO::<Fs>::create::<spec::DataBatchJournalV1>(path)?;
DataBatchPersistDriver::new(f, true)
}

@ -1,472 +0,0 @@
/*
* Created on Thu May 25 2023
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2023, Sayan Nandan <ohsayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
use crate::{
engine::{
mem::ByteStack,
storage::{
header::{HostArch, HostEndian, HostOS, HostPointerWidth},
v1::{header_impl::FileSpecifierVersion, SDSSError, SDSSResult},
versions::{self, DriverVersion, ServerVersion},
},
},
util,
};
/*
Dynamic record (1/2): Host signature
---
- 8B: Server version
- 8B: Driver version
- 4B: File specifier ID
- 1B: Endian
- 1B: Pointer width
- 1B: Arch
- 1B: OS
*/
#[derive(Debug, PartialEq, Clone)]
pub struct DRHostSignature {
server_version: ServerVersion,
driver_version: DriverVersion,
file_specifier_version: FileSpecifierVersion,
endian: HostEndian,
ptr_width: HostPointerWidth,
arch: HostArch,
os: HostOS,
}
impl DRHostSignature {
pub fn verify(&self, expected_file_specifier_version: FileSpecifierVersion) -> SDSSResult<()> {
if self.server_version() != versions::v1::V1_SERVER_VERSION {
return Err(SDSSError::HeaderDecodeServerVersionMismatch);
}
if self.driver_version() != versions::v1::V1_DRIVER_VERSION {
return Err(SDSSError::HeaderDecodeDriverVersionMismatch);
}
if self.file_specifier_version() != expected_file_specifier_version {
return Err(SDSSError::HeaderDecodeDataMismatch);
}
Ok(())
}
}
impl DRHostSignature {
/// Decode the [`DRHostSignature`] from the given bytes
///
/// **☢ WARNING ☢: This only decodes; it doesn't validate expected values!**
pub fn decode_noverify(bytes: [u8; sizeof!(DRHostSignatureRaw)]) -> Option<Self> {
let ns = ByteStack::new(bytes);
let server_version = ServerVersion::__new(u64::from_le(
ns.read_qword(DRHostSignatureRaw::DRHS_OFFSET_P0),
));
let driver_version = DriverVersion::__new(u64::from_le(
ns.read_qword(DRHostSignatureRaw::DRHS_OFFSET_P1),
));
let file_specifier_id = FileSpecifierVersion::__new(u32::from_le(
ns.read_dword(DRHostSignatureRaw::DRHS_OFFSET_P2),
));
let endian =
HostEndian::try_new_with_val(ns.read_byte(DRHostSignatureRaw::DRHS_OFFSET_P3))?;
let ptr_width =
HostPointerWidth::try_new_with_val(ns.read_byte(DRHostSignatureRaw::DRHS_OFFSET_P4))?;
let arch = HostArch::try_new_with_val(ns.read_byte(DRHostSignatureRaw::DRHS_OFFSET_P5))?;
let os = HostOS::try_new_with_val(ns.read_byte(DRHostSignatureRaw::DRHS_OFFSET_P6))?;
Some(Self::new(
server_version,
driver_version,
file_specifier_id,
endian,
ptr_width,
arch,
os,
))
}
}
impl DRHostSignature {
pub const fn new(
server_version: ServerVersion,
driver_version: DriverVersion,
file_specifier_version: FileSpecifierVersion,
endian: HostEndian,
ptr_width: HostPointerWidth,
arch: HostArch,
os: HostOS,
) -> Self {
Self {
server_version,
driver_version,
file_specifier_version,
endian,
ptr_width,
arch,
os,
}
}
pub const fn server_version(&self) -> ServerVersion {
self.server_version
}
pub const fn driver_version(&self) -> DriverVersion {
self.driver_version
}
pub const fn file_specifier_version(&self) -> FileSpecifierVersion {
self.file_specifier_version
}
pub const fn endian(&self) -> HostEndian {
self.endian
}
pub const fn ptr_width(&self) -> HostPointerWidth {
self.ptr_width
}
pub const fn arch(&self) -> HostArch {
self.arch
}
pub const fn os(&self) -> HostOS {
self.os
}
pub const fn encoded(&self) -> DRHostSignatureRaw {
DRHostSignatureRaw::new_full(
self.server_version(),
self.driver_version(),
self.file_specifier_version(),
self.endian(),
self.ptr_width(),
self.arch(),
self.os(),
)
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct DRHostSignatureRaw {
pub(super) data: ByteStack<24>,
}
impl DRHostSignatureRaw {
const DRHS_OFFSET_P0: usize = 0;
const DRHS_OFFSET_P1: usize = sizeof!(u64);
const DRHS_OFFSET_P2: usize = Self::DRHS_OFFSET_P1 + sizeof!(u64);
const DRHS_OFFSET_P3: usize = Self::DRHS_OFFSET_P2 + sizeof!(u32);
const DRHS_OFFSET_P4: usize = Self::DRHS_OFFSET_P3 + 1;
const DRHS_OFFSET_P5: usize = Self::DRHS_OFFSET_P4 + 1;
const DRHS_OFFSET_P6: usize = Self::DRHS_OFFSET_P5 + 1;
const _ENSURE: () = assert!(Self::DRHS_OFFSET_P6 == sizeof!(Self) - 1);
pub const fn new_auto(file_specifier_version: FileSpecifierVersion) -> Self {
Self::new(
versions::v1::V1_SERVER_VERSION,
versions::v1::V1_DRIVER_VERSION,
file_specifier_version,
)
}
pub const fn new(
server_version: ServerVersion,
driver_version: DriverVersion,
file_specifier_id: FileSpecifierVersion,
) -> Self {
Self::new_full(
server_version,
driver_version,
file_specifier_id,
HostEndian::new(),
HostPointerWidth::new(),
HostArch::new(),
HostOS::new(),
)
}
pub const fn new_full(
server_version: ServerVersion,
driver_version: DriverVersion,
file_specifier_id: FileSpecifierVersion,
endian: HostEndian,
ptr_width: HostPointerWidth,
arch: HostArch,
os: HostOS,
) -> Self {
let _ = Self::_ENSURE;
let bytes: [u8; 24] = unsafe {
let [qw_a, qw_b]: [u64; 2] = core::mem::transmute([
server_version.little_endian(),
driver_version.little_endian(),
]);
let dw: u32 = core::mem::transmute([
endian.value_u8(),
ptr_width.value_u8(),
arch.value_u8(),
os.value_u8(),
]);
let qw_c: u64 = core::mem::transmute([(file_specifier_id.0.to_le(), dw.to_le())]);
core::mem::transmute([qw_a, qw_b, qw_c])
};
Self {
data: ByteStack::new(bytes),
}
}
}
impl DRHostSignatureRaw {
pub const fn read_p0_server_version(&self) -> ServerVersion {
ServerVersion::__new(self.data.read_qword(Self::DRHS_OFFSET_P0))
}
pub const fn read_p1_driver_version(&self) -> DriverVersion {
DriverVersion::__new(self.data.read_qword(Self::DRHS_OFFSET_P1))
}
pub const fn read_p2_file_specifier_id(&self) -> FileSpecifierVersion {
FileSpecifierVersion::__new(self.data.read_dword(Self::DRHS_OFFSET_P2))
}
pub const fn read_p3_endian(&self) -> HostEndian {
HostEndian::new_with_val(self.data.read_byte(Self::DRHS_OFFSET_P3))
}
pub const fn read_p4_pointer_width(&self) -> HostPointerWidth {
HostPointerWidth::new_with_val(self.data.read_byte(Self::DRHS_OFFSET_P4))
}
pub const fn read_p5_arch(&self) -> HostArch {
HostArch::new_with_val(self.data.read_byte(Self::DRHS_OFFSET_P5))
}
pub const fn read_p6_os(&self) -> HostOS {
HostOS::new_with_val(self.data.read_byte(Self::DRHS_OFFSET_P6))
}
pub const fn decoded(&self) -> DRHostSignature {
DRHostSignature::new(
self.read_p0_server_version(),
self.read_p1_driver_version(),
self.read_p2_file_specifier_id(),
self.read_p3_endian(),
self.read_p4_pointer_width(),
self.read_p5_arch(),
self.read_p6_os(),
)
}
}
/*
Dynamic record (2/2): Runtime signature
---
- 8B: Dynamic record modify count
- 16B: Host epoch time
- 16B: Host uptime
- 1B: Host name length
- 255B: Host name (nulled)
= 296B
*/
#[derive(Debug, PartialEq, Clone)]
pub struct DRRuntimeSignature {
modify_count: u64,
epoch_time: u128,
host_uptime: u128,
host_name_length: u8,
host_name_raw: [u8; 255],
}
impl DRRuntimeSignature {
pub fn verify(&self) -> SDSSResult<()> {
let et = util::os::get_epoch_time();
if self.epoch_time() > et || self.host_uptime() > et {
// a file from the future?
return Err(SDSSError::HeaderTimeConflict);
}
Ok(())
}
pub fn decode_noverify(bytes: [u8; sizeof!(DRRuntimeSignatureRaw)]) -> Option<Self> {
let bytes = ByteStack::new(bytes);
// check
let modify_count = u64::from_le(bytes.read_qword(DRRuntimeSignatureRaw::DRRS_OFFSET_P0));
let epoch_time = u128::from_le(bytes.read_xmmword(DRRuntimeSignatureRaw::DRRS_OFFSET_P1));
let host_uptime = u128::from_le(bytes.read_xmmword(DRRuntimeSignatureRaw::DRRS_OFFSET_P2));
let host_name_length = bytes.read_byte(DRRuntimeSignatureRaw::DRRS_OFFSET_P3);
let host_name_raw =
util::copy_slice_to_array(&bytes.slice()[DRRuntimeSignatureRaw::DRRS_OFFSET_P4..]);
if cfg!(debug_assertions) {
assert_eq!(
255 - host_name_raw.iter().filter(|b| **b == 0u8).count(),
host_name_length as _
);
}
Some(Self {
modify_count,
epoch_time,
host_uptime,
host_name_length,
host_name_raw,
})
}
}
impl DRRuntimeSignature {
pub const fn new(
modify_count: u64,
epoch_time: u128,
host_uptime: u128,
host_name_length: u8,
host_name_raw: [u8; 255],
) -> Self {
Self {
modify_count,
epoch_time,
host_uptime,
host_name_length,
host_name_raw,
}
}
pub const fn modify_count(&self) -> u64 {
self.modify_count
}
pub const fn epoch_time(&self) -> u128 {
self.epoch_time
}
pub const fn host_uptime(&self) -> u128 {
self.host_uptime
}
pub const fn host_name_length(&self) -> u8 {
self.host_name_length
}
pub const fn host_name_raw(&self) -> [u8; 255] {
self.host_name_raw
}
pub fn host_name(&self) -> &[u8] {
&self.host_name_raw[..self.host_name_length() as usize]
}
pub fn encoded(&self) -> DRRuntimeSignatureRaw {
DRRuntimeSignatureRaw::new(
self.modify_count(),
self.epoch_time(),
self.host_uptime(),
self.host_name_length(),
self.host_name_raw(),
)
}
pub fn set_modify_count(&mut self, new: u64) {
self.modify_count = new;
}
pub fn bump_modify_count(&mut self) {
self.modify_count += 1;
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct DRRuntimeSignatureRaw {
pub(super) data: ByteStack<296>,
}
impl DRRuntimeSignatureRaw {
const DRRS_OFFSET_P0: usize = 0;
const DRRS_OFFSET_P1: usize = sizeof!(u64);
const DRRS_OFFSET_P2: usize = Self::DRRS_OFFSET_P1 + sizeof!(u128);
const DRRS_OFFSET_P3: usize = Self::DRRS_OFFSET_P2 + sizeof!(u128);
const DRRS_OFFSET_P4: usize = Self::DRRS_OFFSET_P3 + 1;
const _ENSURE: () = assert!(Self::DRRS_OFFSET_P4 == sizeof!(Self) - 255);
pub fn new_auto(modify_count: u64) -> Self {
let hostname = crate::util::os::get_hostname();
Self::new(
modify_count,
crate::util::os::get_epoch_time(),
crate::util::os::get_uptime(),
hostname.len(),
hostname.raw(),
)
}
pub fn new(
modify_count: u64,
host_epoch_time: u128,
host_uptime: u128,
host_name_length: u8,
host_name: [u8; 255],
) -> Self {
let _ = Self::_ENSURE;
let mut data = [0u8; 296];
data[Self::DRRS_OFFSET_P0..Self::DRRS_OFFSET_P1]
.copy_from_slice(&modify_count.to_le_bytes());
data[Self::DRRS_OFFSET_P1..Self::DRRS_OFFSET_P2]
.copy_from_slice(&host_epoch_time.to_le_bytes());
data[Self::DRRS_OFFSET_P2..Self::DRRS_OFFSET_P3]
.copy_from_slice(&host_uptime.to_le_bytes());
data[Self::DRRS_OFFSET_P3] = host_name_length;
data[Self::DRRS_OFFSET_P4..].copy_from_slice(&host_name);
Self {
data: ByteStack::new(data),
}
}
pub fn decoded(&self) -> DRRuntimeSignature {
DRRuntimeSignature::new(
self.read_p0_modify_count(),
self.read_p1_epoch_time(),
self.read_p2_uptime(),
self.read_p3_host_name_length() as _,
util::copy_slice_to_array(self.read_p4_host_name_raw_null()),
)
}
pub const fn read_p0_modify_count(&self) -> u64 {
self.data.read_qword(Self::DRRS_OFFSET_P0)
}
pub const fn read_p1_epoch_time(&self) -> u128 {
self.data.read_xmmword(Self::DRRS_OFFSET_P1)
}
pub const fn read_p2_uptime(&self) -> u128 {
self.data.read_xmmword(Self::DRRS_OFFSET_P2)
}
pub const fn read_p3_host_name_length(&self) -> usize {
self.data.read_byte(Self::DRRS_OFFSET_P3) as _
}
pub fn read_p4_host_name_raw_null(&self) -> &[u8] {
&self.data.slice()[Self::DRRS_OFFSET_P4..]
}
pub fn read_host_name(&self) -> &[u8] {
&self.data.slice()
[Self::DRRS_OFFSET_P4..Self::DRRS_OFFSET_P4 + self.read_p3_host_name_length()]
}
}
#[test]
fn test_dr_host_signature_encode_decode() {
const TARGET: DRHostSignature = DRHostSignature::new(
crate::engine::storage::versions::v1::V1_SERVER_VERSION,
crate::engine::storage::versions::v1::V1_DRIVER_VERSION,
FileSpecifierVersion::__new(u32::MAX - 3),
HostEndian::new(),
HostPointerWidth::new(),
HostArch::new(),
HostOS::new(),
);
let encoded = TARGET.encoded();
let decoded = encoded.decoded();
assert_eq!(decoded, TARGET);
}
#[test]
fn test_dr_runtime_signature_encoded_decode() {
const TARGET: DRRuntimeSignature = DRRuntimeSignature::new(
u64::MAX - 3,
u128::MAX - u32::MAX as u128,
u128::MAX - u32::MAX as u128,
"skycloud".len() as _,
util::copy_str_to_array("skycloud"),
);
let encoded = TARGET.encoded();
let decoded = encoded.decoded();
assert_eq!(decoded, TARGET);
assert_eq!(decoded.host_name(), b"skycloud");
}

@ -1,490 +0,0 @@
/*
* Created on Thu May 25 2023
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2023, Sayan Nandan <ohsayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
use crate::{
engine::{
mem::ByteStack,
storage::{
v1::{
header_impl::{FileScope, FileSpecifier, FileSpecifierVersion, HostRunMode},
SDSSError, SDSSResult,
},
versions::{self, DriverVersion, ServerVersion},
},
},
util,
};
/*
Genesis record (1/2)
---
Metadata record (8B x 3 + (4B x 2)):
+----------+----------+----------+---------+
| Server | Driver | File |File|Spec|
| version | Version | Scope |Spec|ID |
+----------+----------+----------+---------+
0, 63
*/
#[derive(Debug, PartialEq, Clone)]
pub struct GRMetadataRecord {
server_version: ServerVersion,
driver_version: DriverVersion,
file_scope: FileScope,
file_spec: FileSpecifier,
file_spec_id: FileSpecifierVersion,
}
impl GRMetadataRecord {
pub fn verify(
&self,
expected_file_scope: FileScope,
expected_file_specifier: FileSpecifier,
expected_file_specifier_version: FileSpecifierVersion,
) -> SDSSResult<()> {
if self.server_version() != versions::v1::V1_SERVER_VERSION {
return Err(SDSSError::HeaderDecodeServerVersionMismatch);
}
if self.driver_version() != versions::v1::V1_DRIVER_VERSION {
return Err(SDSSError::HeaderDecodeDriverVersionMismatch);
}
let okay = self.file_scope() == expected_file_scope
&& self.file_spec() == expected_file_specifier
&& self.file_spec_id() == expected_file_specifier_version;
if okay {
Ok(())
} else {
Err(SDSSError::HeaderDecodeDataMismatch)
}
}
}
impl GRMetadataRecord {
pub const fn new(
server_version: ServerVersion,
driver_version: DriverVersion,
file_scope: FileScope,
file_spec: FileSpecifier,
file_spec_id: FileSpecifierVersion,
) -> Self {
Self {
server_version,
driver_version,
file_scope,
file_spec,
file_spec_id,
}
}
pub const fn server_version(&self) -> ServerVersion {
self.server_version
}
pub const fn driver_version(&self) -> DriverVersion {
self.driver_version
}
pub const fn file_scope(&self) -> FileScope {
self.file_scope
}
pub const fn file_spec(&self) -> FileSpecifier {
self.file_spec
}
pub const fn file_spec_id(&self) -> FileSpecifierVersion {
self.file_spec_id
}
pub const fn encoded(&self) -> GRMetadataRecordRaw {
GRMetadataRecordRaw::new_full(
self.server_version(),
self.driver_version(),
self.file_scope(),
self.file_spec(),
self.file_spec_id(),
)
}
}
#[derive(Clone)]
pub struct GRMetadataRecordRaw {
pub(super) data: ByteStack<32>,
}
impl GRMetadataRecordRaw {
/// Decodes a given metadata record, validating all data for correctness.
///
/// **☢ WARNING ☢: This only decodes; it doesn't validate expected values!**
pub fn decode_noverify(data: [u8; 32]) -> Option<GRMetadataRecord> {
let data = ByteStack::new(data);
let server_version =
ServerVersion::__new(u64::from_le(data.read_qword(Self::MDR_OFFSET_P0)));
let driver_version =
DriverVersion::__new(u64::from_le(data.read_qword(Self::MDR_OFFSET_P1)));
let file_scope = FileScope::try_new(u64::from_le(data.read_qword(Self::MDR_OFFSET_P2)))?;
let file_spec = FileSpecifier::try_new(u32::from_le(data.read_dword(Self::MDR_OFFSET_P3)))?;
let file_spec_id =
FileSpecifierVersion::__new(u32::from_le(data.read_dword(Self::MDR_OFFSET_P4)));
Some(GRMetadataRecord::new(
server_version,
driver_version,
file_scope,
file_spec,
file_spec_id,
))
}
}
impl GRMetadataRecordRaw {
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 empty_buffer() -> [u8; sizeof!(Self)] {
[0u8; sizeof!(Self)]
}
pub const fn new_auto(
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,
)
}
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 = 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 = unsafe {
core::mem::transmute([
(specifier.value_u8() as u32).to_le(),
specifier_id.0.to_le(),
])
};
let file_specifier_and_id = file_specifier_and_id.to_le_bytes();
while i < sizeof!(u64) {
ret[i] = server_version[i];
ret[i + sizeof!(u64, 1)] = driver_version[i];
ret[i + sizeof!(u64, 2)] = file_scope[i];
ret[i + sizeof!(u64, 3)] = file_specifier_and_id[i];
i += 1;
}
Self {
data: ByteStack::new(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 GRMetadataRecordRaw {
pub const fn read_p0_server_version(&self) -> ServerVersion {
ServerVersion::__new(self.data.read_qword(Self::MDR_OFFSET_P0))
}
pub const fn read_p1_driver_version(&self) -> DriverVersion {
DriverVersion::__new(self.data.read_qword(Self::MDR_OFFSET_P1))
}
pub const fn read_p2_file_scope(&self) -> FileScope {
FileScope::new(self.data.read_qword(Self::MDR_OFFSET_P2))
}
pub const fn read_p3_file_spec(&self) -> FileSpecifier {
FileSpecifier::new(self.data.read_dword(Self::MDR_OFFSET_P3))
}
pub const fn read_p4_file_spec_version(&self) -> FileSpecifierVersion {
FileSpecifierVersion(self.data.read_dword(Self::MDR_OFFSET_P4))
}
}
/*
Genesis Record (2/2)
---
Host record (?B; > 56B):
- 16B: Host epoch time in nanoseconds
- 16B: Host uptime in nanoseconds
- 08B:
- 04B: Host setting version ID
- 04B: Host run mode
- 08B: Host startup counter
- 01B: Host name length
- 255B: Host name
= 304B
*/
#[derive(Debug, PartialEq, Clone)]
pub struct GRHostRecord {
epoch_time: u128,
uptime: u128,
setting_version: u32,
run_mode: HostRunMode,
startup_counter: u64,
hostname_len: u8,
hostname_raw: [u8; 255],
}
impl GRHostRecord {
/// Verified: N/A
/// To verify: N/A
pub fn verify(&self) -> SDSSResult<()> {
Ok(())
}
}
impl GRHostRecord {
pub fn decode_noverify(bytes: [u8; sizeof!(GRHostRecordRaw)]) -> Option<Self> {
let ns = ByteStack::new(bytes);
let epoch_time = u128::from_le(ns.read_xmmword(GRHostRecordRaw::GRHR_OFFSET_P0));
let uptime = u128::from_le(ns.read_xmmword(GRHostRecordRaw::GRHR_OFFSET_P1));
let setting_version = u32::from_le(ns.read_dword(GRHostRecordRaw::GRHR_OFFSET_P2));
let run_mode = HostRunMode::try_new_with_val(u32::from_le(
ns.read_dword(GRHostRecordRaw::GRHR_OFFSET_P3),
))?;
let startup_counter = u64::from_le(ns.read_qword(GRHostRecordRaw::GRHR_OFFSET_P4));
let host_name_len = ns.read_byte(GRHostRecordRaw::GRHR_OFFSET_P5);
let host_name_raw =
util::copy_slice_to_array(&ns.slice()[GRHostRecordRaw::GRHR_OFFSET_P6..]);
Some(Self::new(
epoch_time,
uptime,
setting_version,
run_mode,
startup_counter,
host_name_len,
host_name_raw,
))
}
}
impl GRHostRecord {
pub const fn new(
epoch_time: u128,
uptime: u128,
setting_version: u32,
run_mode: HostRunMode,
startup_counter: u64,
hostname_len: u8,
hostname: [u8; 255],
) -> Self {
Self {
epoch_time,
uptime,
setting_version,
run_mode,
startup_counter,
hostname_len,
hostname_raw: hostname,
}
}
pub fn epoch_time(&self) -> u128 {
self.epoch_time
}
pub fn uptime(&self) -> u128 {
self.uptime
}
pub fn setting_version(&self) -> u32 {
self.setting_version
}
pub fn run_mode(&self) -> HostRunMode {
self.run_mode
}
pub fn startup_counter(&self) -> u64 {
self.startup_counter
}
pub fn hostname_len(&self) -> u8 {
self.hostname_len
}
pub fn hostname_raw(&self) -> [u8; 255] {
self.hostname_raw
}
pub fn encoded(&self) -> GRHostRecordRaw {
GRHostRecordRaw::new(
self.epoch_time(),
self.uptime(),
self.setting_version(),
self.run_mode(),
self.startup_counter(),
self.hostname_len(),
self.hostname_raw(),
)
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct GRHostRecordRaw {
pub(super) data: ByteStack<304>,
}
impl GRHostRecordRaw {
const GRHR_OFFSET_P0: usize = 0;
const GRHR_OFFSET_P1: usize = sizeof!(u128);
const GRHR_OFFSET_P2: usize = Self::GRHR_OFFSET_P1 + sizeof!(u128);
const GRHR_OFFSET_P3: usize = Self::GRHR_OFFSET_P2 + sizeof!(u32);
const GRHR_OFFSET_P4: usize = Self::GRHR_OFFSET_P3 + sizeof!(u32);
const GRHR_OFFSET_P5: usize = Self::GRHR_OFFSET_P4 + sizeof!(u64);
const GRHR_OFFSET_P6: usize = Self::GRHR_OFFSET_P5 + 1;
const _ENSURE: () = assert!(Self::GRHR_OFFSET_P6 == sizeof!(Self) - 255);
pub fn new_auto(setting_version: u32, run_mode: HostRunMode, startup_counter: u64) -> Self {
let hostname = crate::util::os::get_hostname();
Self::new(
crate::util::os::get_epoch_time(),
crate::util::os::get_uptime(),
setting_version,
run_mode,
startup_counter,
hostname.len(),
hostname.raw(),
)
}
pub fn new(
p0_epoch_time: u128,
p1_uptime: u128,
p2_setting_version: u32,
p3_run_mode: HostRunMode,
p4_host_startup_counter: u64,
p5_host_name_length: u8,
p6_host_name_raw: [u8; 255],
) -> Self {
let _ = Self::_ENSURE;
let mut data = [0u8; sizeof!(Self)];
data[Self::GRHR_OFFSET_P0..Self::GRHR_OFFSET_P1]
.copy_from_slice(&p0_epoch_time.to_le_bytes());
data[Self::GRHR_OFFSET_P1..Self::GRHR_OFFSET_P2].copy_from_slice(&p1_uptime.to_le_bytes());
data[Self::GRHR_OFFSET_P2..Self::GRHR_OFFSET_P3]
.copy_from_slice(&p2_setting_version.to_le_bytes());
data[Self::GRHR_OFFSET_P3..Self::GRHR_OFFSET_P4]
.copy_from_slice(&(p3_run_mode.value_u8() as u32).to_le_bytes());
data[Self::GRHR_OFFSET_P4..Self::GRHR_OFFSET_P5]
.copy_from_slice(&p4_host_startup_counter.to_le_bytes());
data[Self::GRHR_OFFSET_P5] = p5_host_name_length;
data[Self::GRHR_OFFSET_P6..].copy_from_slice(&p6_host_name_raw);
Self {
data: ByteStack::new(data),
}
}
pub const fn read_p0_epoch_time(&self) -> u128 {
self.data.read_xmmword(Self::GRHR_OFFSET_P0)
}
pub const fn read_p1_uptime(&self) -> u128 {
self.data.read_xmmword(Self::GRHR_OFFSET_P1)
}
pub const fn read_p2_setting_version_id(&self) -> u32 {
self.data.read_dword(Self::GRHR_OFFSET_P2)
}
pub const fn read_p3_run_mode(&self) -> HostRunMode {
HostRunMode::new_with_val(self.data.read_dword(Self::GRHR_OFFSET_P3))
}
pub const fn read_p4_startup_counter(&self) -> u64 {
self.data.read_qword(Self::GRHR_OFFSET_P4)
}
pub const fn read_p5_host_name_length(&self) -> usize {
self.data.read_byte(Self::GRHR_OFFSET_P5) as _
}
pub fn read_p6_host_name_raw(&self) -> &[u8] {
&self.data.slice()[Self::GRHR_OFFSET_P6..]
}
pub fn read_host_name(&self) -> &[u8] {
&self.data.slice()
[Self::GRHR_OFFSET_P6..Self::GRHR_OFFSET_P6 + self.read_p5_host_name_length()]
}
pub fn decoded(&self) -> GRHostRecord {
GRHostRecord::new(
self.read_p0_epoch_time(),
self.read_p1_uptime(),
self.read_p2_setting_version_id(),
self.read_p3_run_mode(),
self.read_p4_startup_counter(),
self.read_p5_host_name_length() as _,
util::copy_slice_to_array(self.read_p6_host_name_raw()),
)
}
}
#[test]
fn test_metadata_record_encode_decode() {
let md = GRMetadataRecordRaw::new(
FileScope::Journal,
FileSpecifier::GNSTxnLog,
FileSpecifierVersion(1),
);
assert_eq!(md.read_p0_server_version(), versions::v1::V1_SERVER_VERSION);
assert_eq!(md.read_p1_driver_version(), versions::v1::V1_DRIVER_VERSION);
assert_eq!(md.read_p2_file_scope(), FileScope::Journal);
assert_eq!(md.read_p3_file_spec(), FileSpecifier::GNSTxnLog);
assert_eq!(md.read_p4_file_spec_version(), FileSpecifierVersion(1));
}
#[test]
fn test_host_record_encode_decode() {
const HOST_UPTIME: u128 = u128::MAX - 434324903;
const HOST_SETTING_VERSION_ID: u32 = 245;
const HOST_RUN_MODE: HostRunMode = HostRunMode::Prod;
const HOST_STARTUP_COUNTER: u64 = u32::MAX as _;
const HOST_NAME: &str = "skycloud";
use std::time::*;
let time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
let hr = GRHostRecordRaw::new(
time,
HOST_UPTIME,
HOST_SETTING_VERSION_ID,
HOST_RUN_MODE,
HOST_STARTUP_COUNTER,
HOST_NAME.len() as _,
crate::util::copy_str_to_array(HOST_NAME),
);
assert_eq!(hr.read_p0_epoch_time(), time);
assert_eq!(hr.read_p1_uptime(), HOST_UPTIME);
assert_eq!(hr.read_p2_setting_version_id(), HOST_SETTING_VERSION_ID);
assert_eq!(hr.read_p3_run_mode(), HOST_RUN_MODE);
assert_eq!(hr.read_p4_startup_counter(), HOST_STARTUP_COUNTER);
assert_eq!(hr.read_p5_host_name_length(), HOST_NAME.len());
assert_eq!(hr.read_host_name(), HOST_NAME.as_bytes());
}

@ -1,338 +0,0 @@
/*
* Created on Mon May 15 2023
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2023, Sayan Nandan <ohsayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
/*
* SDSS Header layout:
*
* +--------------------------------------------------------------+
* | |
* | STATIC RECORD |
* | 128B |
* +--------------------------------------------------------------+
* +--------------------------------------------------------------+
* | |
* | |
* | GENESIS RECORD |
* | (256+56+?)B |
* | +--------------------------------------------+ |
* | | | |
* | | METADATA RECORD | |
* | | 256B | |
* | +--------------------------------------------+ |
* | +--------------------------------------------+ |
* | | | |
* | | HOST RECORD | |
* | | >56B | |
* | +--------------------------------------------+ |
* | |
* +--------------------------------------------------------------+
* +--------------------------------------------------------------+
* | DYNAMIC RECORD |
* | >56B |
* +--------------------------------------------------------------+
* Note: The entire part of the header is little endian encoded
*/
use crate::util::copy_slice_to_array as cp;
use super::SDSSResult;
// (1) sr
mod sr;
// (2) gr
mod gr;
// (3) dr
mod dr;
/// The file scope
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, sky_macros::EnumMethods)]
pub enum FileScope {
Journal = 0,
DataBatch = 1,
FlatmapData = 2,
}
impl FileScope {
pub const fn try_new(id: u64) -> Option<Self> {
Some(match id {
0 => Self::Journal,
1 => Self::DataBatch,
2 => Self::FlatmapData,
_ => 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 {
GNSTxnLog = 0,
TableDataBatch = 1,
SysDB = 2,
#[cfg(test)]
TestTransactionLog = 0xFF,
}
impl FileSpecifier {
pub const fn try_new(v: u32) -> Option<Self> {
Some(match v {
0 => Self::GNSTxnLog,
1 => Self::TableDataBatch,
2 => Self::SysDB,
#[cfg(test)]
0xFF => Self::TestTransactionLog,
_ => return None,
})
}
pub const fn new(v: u32) -> Self {
match Self::try_new(v) {
Some(v) => v,
_ => panic!("unknown filespecifier"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct FileSpecifierVersion(u32);
impl FileSpecifierVersion {
pub const fn __new(v: u32) -> Self {
Self(v)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, sky_macros::EnumMethods)]
#[repr(u8)]
pub enum HostRunMode {
Dev = 0,
Prod = 1,
}
impl HostRunMode {
pub const fn try_new_with_val(v: u32) -> Option<Self> {
Some(match v {
0 => Self::Dev,
1 => Self::Prod,
_ => return None,
})
}
pub const fn new_with_val(v: u32) -> Self {
match Self::try_new_with_val(v) {
Some(v) => v,
None => panic!("unknown hostrunmode"),
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct SDSSHeader {
// static record
sr: sr::StaticRecord,
// genesis record
gr_mdr: gr::GRMetadataRecord,
gr_hr: gr::GRHostRecord,
// dynamic record
dr_hs: dr::DRHostSignature,
dr_rs: dr::DRRuntimeSignature,
}
impl SDSSHeader {
pub fn verify(
&self,
expected_file_scope: FileScope,
expected_file_specifier: FileSpecifier,
expected_file_specifier_version: FileSpecifierVersion,
) -> SDSSResult<()> {
self.sr().verify()?;
self.gr_mdr().verify(
expected_file_scope,
expected_file_specifier,
expected_file_specifier_version,
)?;
self.gr_hr().verify()?;
self.dr_hs().verify(expected_file_specifier_version)?;
self.dr_rs().verify()?;
Ok(())
}
}
impl SDSSHeader {
pub const fn new(
sr: sr::StaticRecord,
gr_mdr: gr::GRMetadataRecord,
gr_hr: gr::GRHostRecord,
dr_hs: dr::DRHostSignature,
dr_rs: dr::DRRuntimeSignature,
) -> Self {
Self {
sr,
gr_mdr,
gr_hr,
dr_hs,
dr_rs,
}
}
pub fn sr(&self) -> &sr::StaticRecord {
&self.sr
}
pub fn gr_mdr(&self) -> &gr::GRMetadataRecord {
&self.gr_mdr
}
pub fn gr_hr(&self) -> &gr::GRHostRecord {
&self.gr_hr
}
pub fn dr_hs(&self) -> &dr::DRHostSignature {
&self.dr_hs
}
pub fn dr_rs(&self) -> &dr::DRRuntimeSignature {
&self.dr_rs
}
pub fn dr_rs_mut(&mut self) -> &mut dr::DRRuntimeSignature {
&mut self.dr_rs
}
pub fn encoded(&self) -> SDSSHeaderRaw {
SDSSHeaderRaw::new_full(
self.sr.encoded(),
self.gr_mdr().encoded(),
self.gr_hr().encoded(),
self.dr_hs().encoded(),
self.dr_rs().encoded(),
)
}
}
#[derive(Clone)]
pub struct SDSSHeaderRaw {
sr: sr::StaticRecordRaw,
gr_0_mdr: gr::GRMetadataRecordRaw,
gr_1_hr: gr::GRHostRecordRaw,
dr_0_hs: dr::DRHostSignatureRaw,
dr_1_rs: dr::DRRuntimeSignatureRaw,
}
impl SDSSHeaderRaw {
const OFFSET_SR0: usize = 0;
const OFFSET_SR1: usize = sizeof!(sr::StaticRecordRaw);
const OFFSET_SR2: usize = Self::OFFSET_SR1 + sizeof!(gr::GRMetadataRecordRaw);
const OFFSET_SR3: usize = Self::OFFSET_SR2 + sizeof!(gr::GRHostRecordRaw);
const OFFSET_SR4: usize = Self::OFFSET_SR3 + sizeof!(dr::DRHostSignatureRaw);
pub fn new_auto(
gr_mdr_scope: FileScope,
gr_mdr_specifier: FileSpecifier,
gr_mdr_specifier_id: FileSpecifierVersion,
gr_hr_setting_version: u32,
gr_hr_run_mode: HostRunMode,
gr_hr_startup_counter: u64,
dr_rts_modify_count: u64,
) -> Self {
Self::new_full(
sr::StaticRecordRaw::new_auto(),
gr::GRMetadataRecordRaw::new_auto(gr_mdr_scope, gr_mdr_specifier, gr_mdr_specifier_id),
gr::GRHostRecordRaw::new_auto(
gr_hr_setting_version,
gr_hr_run_mode,
gr_hr_startup_counter,
),
dr::DRHostSignatureRaw::new_auto(gr_mdr_specifier_id),
dr::DRRuntimeSignatureRaw::new_auto(dr_rts_modify_count),
)
}
pub fn new_full(
sr: sr::StaticRecordRaw,
gr_mdr: gr::GRMetadataRecordRaw,
gr_hr: gr::GRHostRecordRaw,
dr_hs: dr::DRHostSignatureRaw,
dr_rs: dr::DRRuntimeSignatureRaw,
) -> Self {
Self {
sr,
gr_0_mdr: gr_mdr,
gr_1_hr: gr_hr,
dr_0_hs: dr_hs,
dr_1_rs: dr_rs,
}
}
pub fn new(
sr: sr::StaticRecordRaw,
gr_0_mdr: gr::GRMetadataRecordRaw,
gr_1_hr: gr::GRHostRecordRaw,
dr_hs: dr::DRHostSignatureRaw,
dr_rs: dr::DRRuntimeSignatureRaw,
) -> Self {
Self {
sr,
gr_0_mdr,
gr_1_hr,
dr_0_hs: dr_hs,
dr_1_rs: dr_rs,
}
}
pub fn get0_sr(&self) -> &[u8] {
self.sr.base.get_ref()
}
pub fn get1_dr_0_mdr(&self) -> &[u8] {
self.gr_0_mdr.data.slice()
}
pub fn get1_dr_1_hr_0(&self) -> &[u8] {
self.gr_1_hr.data.slice()
}
pub const fn header_size() -> usize {
sizeof!(sr::StaticRecordRaw)
+ sizeof!(gr::GRMetadataRecordRaw)
+ sizeof!(gr::GRHostRecordRaw)
+ sizeof!(dr::DRHostSignatureRaw)
+ sizeof!(dr::DRRuntimeSignatureRaw)
}
pub fn array(&self) -> [u8; Self::header_size()] {
let mut data = [0u8; Self::header_size()];
data[Self::OFFSET_SR0..Self::OFFSET_SR1].copy_from_slice(self.sr.base.get_ref());
data[Self::OFFSET_SR1..Self::OFFSET_SR2].copy_from_slice(self.gr_0_mdr.data.slice());
data[Self::OFFSET_SR2..Self::OFFSET_SR3].copy_from_slice(self.gr_1_hr.data.slice());
data[Self::OFFSET_SR3..Self::OFFSET_SR4].copy_from_slice(self.dr_0_hs.data.slice());
data[Self::OFFSET_SR4..].copy_from_slice(self.dr_1_rs.data.slice());
data
}
/// **☢ WARNING ☢: This only decodes; it doesn't validate expected values!**
pub fn decode_noverify(slice: [u8; Self::header_size()]) -> Option<SDSSHeader> {
let sr =
sr::StaticRecordRaw::decode_noverify(cp(&slice[Self::OFFSET_SR0..Self::OFFSET_SR1]))?;
let gr_mdr = gr::GRMetadataRecordRaw::decode_noverify(cp(
&slice[Self::OFFSET_SR1..Self::OFFSET_SR2]
))?;
let gr_hr =
gr::GRHostRecord::decode_noverify(cp(&slice[Self::OFFSET_SR2..Self::OFFSET_SR3]))?;
let dr_sig =
dr::DRHostSignature::decode_noverify(cp(&slice[Self::OFFSET_SR3..Self::OFFSET_SR4]))?;
let dr_rt = dr::DRRuntimeSignature::decode_noverify(cp(&slice[Self::OFFSET_SR4..]))?;
Some(SDSSHeader::new(sr, gr_mdr, gr_hr, dr_sig, dr_rt))
}
}

@ -1,85 +0,0 @@
/*
* Created on Thu May 25 2023
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2023, Sayan Nandan <ohsayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
use crate::engine::storage::{
header::{StaticRecordUV, StaticRecordUVRaw},
v1::{SDSSError, SDSSResult},
versions,
};
#[derive(Debug, PartialEq, Clone)]
pub struct StaticRecord {
sr: StaticRecordUV,
}
impl StaticRecord {
/// Verified:
/// - header version
///
/// Need to verify: N/A
pub fn verify(&self) -> SDSSResult<()> {
if self.sr().header_version() == versions::v1::V1_HEADER_VERSION {
Ok(())
} else {
return Err(SDSSError::HeaderDecodeHeaderVersionMismatch);
}
}
}
impl StaticRecord {
pub const fn new(sr: StaticRecordUV) -> Self {
Self { sr }
}
pub const fn encoded(&self) -> StaticRecordRaw {
StaticRecordRaw {
base: self.sr.encoded(),
}
}
pub const fn sr(&self) -> &StaticRecordUV {
&self.sr
}
}
/// Static record
#[derive(Clone)]
pub struct StaticRecordRaw {
pub(super) base: StaticRecordUVRaw,
}
impl StaticRecordRaw {
pub const fn new_auto() -> Self {
Self::new(StaticRecordUVRaw::create(versions::v1::V1_HEADER_VERSION))
}
pub const fn new(base: StaticRecordUVRaw) -> Self {
Self { base }
}
pub const fn empty_buffer() -> [u8; sizeof!(Self)] {
[0u8; sizeof!(Self)]
}
pub fn decode_noverify(buf: [u8; sizeof!(Self)]) -> Option<StaticRecord> {
StaticRecordUVRaw::decode_from_bytes(buf).map(StaticRecord::new)
}
}

@ -43,49 +43,21 @@
use {
super::{
header_impl::{FileSpecifierVersion, HostRunMode, SDSSHeaderRaw},
rw::{FileOpen, RawFSInterface, SDSSFileIO},
SDSSError, SDSSResult,
},
crate::{
engine::storage::v1::header_impl::{FileScope, FileSpecifier},
util::{compiler, copy_a_into_b, copy_slice_to_array as memcpy, Threshold},
spec, SDSSError, SDSSResult,
},
crate::util::{compiler, copy_a_into_b, copy_slice_to_array as memcpy, Threshold},
std::marker::PhantomData,
};
const CRC: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
const RECOVERY_BLOCK_AUTO_THRESHOLD: usize = 5;
pub fn open_journal<TA: JournalAdapter, Fs: RawFSInterface>(
pub fn open_journal<TA: JournalAdapter, Fs: RawFSInterface, F: spec::FileSpec>(
log_file_name: &str,
log_kind: FileSpecifier,
log_kind_version: FileSpecifierVersion,
host_setting_version: u32,
host_run_mode: HostRunMode,
host_startup_counter: u64,
gs: &TA::GlobalState,
) -> SDSSResult<FileOpen<JournalWriter<Fs, TA>>> {
macro_rules! open_file {
($modify:literal) => {
SDSSFileIO::<Fs>::open_or_create_perm_rw::<$modify>(
log_file_name,
FileScope::Journal,
log_kind,
log_kind_version,
host_setting_version,
host_run_mode,
host_startup_counter,
)
};
}
// HACK(@ohsayan): until generic const exprs are stabilized, we're in a state of hell
let f = if TA::DENY_NONAPPEND {
open_file!(false)
} else {
open_file!(true)
}?;
let file = match f {
let file = match SDSSFileIO::<Fs>::open_or_create_perm_rw::<F>(log_file_name)? {
FileOpen::Created(f) => return Ok(FileOpen::Created(JournalWriter::new(f, 0, true)?)),
FileOpen::Existing((file, _header)) => file,
};
@ -218,7 +190,7 @@ pub struct JournalReader<TA, Fs: RawFSInterface> {
impl<TA: JournalAdapter, Fs: RawFSInterface> JournalReader<TA, Fs> {
pub fn new(log_file: SDSSFileIO<Fs>) -> SDSSResult<Self> {
let log_size = log_file.file_length()? - SDSSHeaderRaw::header_size() as u64;
let log_size = log_file.file_length()? - spec::SDSSStaticHeaderV1Compact::SIZE as u64;
Ok(Self {
log_file,
log_size,

@ -29,10 +29,10 @@ use crate::engine::{
data::uuid::Uuid,
fractal::{FractalModelDriver, ModelDrivers, ModelUniqueID},
storage::v1::{
batch_jrnl, header_meta,
batch_jrnl,
journal::{self, JournalWriter},
rw::{FileOpen, RawFSInterface},
LocalFS, SDSSErrorContext, SDSSResult,
spec, LocalFS, SDSSErrorContext, SDSSResult,
},
txn::gns::{GNSAdapter, GNSTransactionDriverAnyFS},
};
@ -61,19 +61,9 @@ impl SEInitState {
gns,
}
}
pub fn try_init(
host_setting_version: u32,
host_run_mode: header_meta::HostRunMode,
host_startup_counter: u64,
) -> SDSSResult<Self> {
pub fn try_init() -> SDSSResult<Self> {
let gns = GlobalNS::empty();
let gns_txn_driver = open_gns_driver(
GNS_FILE_PATH,
host_setting_version,
host_run_mode,
host_startup_counter,
&gns,
)?;
let gns_txn_driver = open_gns_driver(GNS_FILE_PATH, &gns)?;
let new_instance = gns_txn_driver.is_created();
let mut model_drivers = ModelDrivers::new();
if !new_instance {
@ -131,18 +121,7 @@ impl SEInitState {
pub fn open_gns_driver<Fs: RawFSInterface>(
path: &str,
host_setting_version: u32,
host_run_mode: header_meta::HostRunMode,
host_startup_counter: u64,
gns: &GlobalNS,
) -> SDSSResult<FileOpen<JournalWriter<Fs, GNSAdapter>>> {
journal::open_journal::<GNSAdapter, Fs>(
path,
header_meta::FileSpecifier::GNSTxnLog,
header_meta::FileSpecifierVersion::__new(GNS_LOG_VERSION_CODE),
host_setting_version,
host_run_mode,
host_startup_counter,
gns,
)
journal::open_journal::<GNSAdapter, Fs, spec::GNSTransactionLogV1>(path, gns)
}

@ -24,13 +24,12 @@
*
*/
// raw
mod header_impl;
// impls
mod batch_jrnl;
mod journal;
pub(in crate::engine) mod loader;
mod rw;
pub mod spec;
mod sysdb;
// hl
pub mod inf;
@ -49,9 +48,6 @@ pub use {
pub mod data_batch {
pub use super::batch_jrnl::{create, reinit, DataBatchPersistDriver, DataBatchRestoreDriver};
}
pub mod header_meta {
pub use super::header_impl::{FileScope, FileSpecifier, FileSpecifierVersion, HostRunMode};
}
use crate::{engine::txn::TransactionError, util::os::SysIOError as IoError};
@ -138,6 +134,7 @@ pub enum SDSSError {
/// An error with more context
// TODO(@ohsayan): avoid the box; we'll clean this up soon
Extra(Box<Self>, String),
HeaderDecodeVersionMismatch,
}
impl From<TransactionError> for SDSSError {

@ -26,9 +26,7 @@
use {
super::{
header_impl::{
FileScope, FileSpecifier, FileSpecifierVersion, HostRunMode, SDSSHeader, SDSSHeaderRaw,
},
spec::{FileSpec, Header},
SDSSResult,
},
crate::{
@ -395,134 +393,36 @@ pub struct SDSSFileIO<Fs: RawFSInterface> {
}
impl<Fs: RawFSInterface> SDSSFileIO<Fs> {
/// Open an existing SDSS file
///
/// **IMPORTANT: File position: end-of-header-section**
pub fn open<const REWRITE_MODIFY_COUNTER: bool>(
file_path: &str,
file_scope: FileScope,
file_specifier: FileSpecifier,
file_specifier_version: FileSpecifierVersion,
) -> SDSSResult<(SDSSHeader, Self)> {
let f = Fs::fs_fopen_rw(file_path)?;
Self::_sdss_fopen::<REWRITE_MODIFY_COUNTER>(
f,
file_scope,
file_specifier,
file_specifier_version,
)
}
/// internal SDSS fopen routine
fn _sdss_fopen<const REWRITE_MODIFY_COUNTER: bool>(
mut f: <Fs as RawFSInterface>::File,
file_scope: FileScope,
file_specifier: FileSpecifier,
file_specifier_version: FileSpecifierVersion,
) -> Result<(SDSSHeader, SDSSFileIO<Fs>), SDSSError> {
let mut header_raw = [0u8; SDSSHeaderRaw::header_size()];
f.fr_read_exact(&mut header_raw)?;
let header = SDSSHeaderRaw::decode_noverify(header_raw)
.ok_or(SDSSError::HeaderDecodeCorruptedHeader)?;
header.verify(file_scope, file_specifier, file_specifier_version)?;
let mut f = Self::_new(f);
if REWRITE_MODIFY_COUNTER {
// since we updated this file, let us update the header
let mut new_header = header.clone();
new_header.dr_rs_mut().bump_modify_count();
f.seek_from_start(0)?;
f.fsynced_write(new_header.encoded().array().as_ref())?;
f.seek_from_start(SDSSHeaderRaw::header_size() as _)?;
}
Ok((header, f))
}
/// Create a new SDSS file
///
/// **IMPORTANT: File position: end-of-header-section**
pub fn create(
file_path: &str,
file_scope: FileScope,
file_specifier: FileSpecifier,
file_specifier_version: FileSpecifierVersion,
host_setting_version: u32,
host_run_mode: HostRunMode,
host_startup_counter: u64,
) -> SDSSResult<Self> {
let f = Fs::fs_fcreate_rw(file_path)?;
Self::_sdss_fcreate(
file_scope,
file_specifier,
file_specifier_version,
host_setting_version,
host_run_mode,
host_startup_counter,
f,
)
}
/// Internal SDSS fcreate routine
fn _sdss_fcreate(
file_scope: FileScope,
file_specifier: FileSpecifier,
file_specifier_version: FileSpecifierVersion,
host_setting_version: u32,
host_run_mode: HostRunMode,
host_startup_counter: u64,
f: <Fs as RawFSInterface>::File,
) -> Result<SDSSFileIO<Fs>, SDSSError> {
let data = SDSSHeaderRaw::new_auto(
file_scope,
file_specifier,
file_specifier_version,
host_setting_version,
host_run_mode,
host_startup_counter,
0,
)
.array();
let mut f = Self::_new(f);
f.fsynced_write(&data)?;
pub fn open<F: FileSpec>(fpath: &str) -> SDSSResult<(Self, F::Header)> {
let mut f = Self::_new(Fs::fs_fopen_rw(fpath)?);
let header = F::Header::decode_verify(&mut f, F::DECODE_DATA, F::VERIFY_DATA)?;
Ok((f, header))
}
pub fn create<F: FileSpec>(fpath: &str) -> SDSSResult<Self> {
let mut f = Self::_new(Fs::fs_fcreate_rw(fpath)?);
F::Header::encode(&mut f, F::ENCODE_DATA)?;
Ok(f)
}
/// Create a new SDSS file or re-open an existing file and verify
///
/// **IMPORTANT: File position: end-of-header-section**
pub fn open_or_create_perm_rw<const REWRITE_MODIFY_COUNTER: bool>(
file_path: &str,
file_scope: FileScope,
file_specifier: FileSpecifier,
file_specifier_version: FileSpecifierVersion,
host_setting_version: u32,
host_run_mode: HostRunMode,
host_startup_counter: u64,
) -> SDSSResult<FileOpen<Self, (Self, SDSSHeader)>> {
let f = Fs::fs_fopen_or_create_rw(file_path)?;
match f {
FileOpen::Created(f) => {
let f = Self::_sdss_fcreate(
file_scope,
file_specifier,
file_specifier_version,
host_setting_version,
host_run_mode,
host_startup_counter,
f,
)?;
pub fn open_or_create_perm_rw<F: FileSpec>(
fpath: &str,
) -> SDSSResult<FileOpen<Self, (Self, F::Header)>> {
match Fs::fs_fopen_or_create_rw(fpath)? {
FileOpen::Created(c) => {
let mut f = Self::_new(c);
F::Header::encode(&mut f, F::ENCODE_DATA)?;
Ok(FileOpen::Created(f))
}
FileOpen::Existing(f) => {
let (f, header) = Self::_sdss_fopen::<REWRITE_MODIFY_COUNTER>(
f,
file_scope,
file_specifier,
file_specifier_version,
)?;
Ok(FileOpen::Existing((header, f)))
FileOpen::Existing(e) => {
let mut f = Self::_new(e);
let header = F::Header::decode_verify(&mut f, F::DECODE_DATA, F::VERIFY_DATA)?;
Ok(FileOpen::Existing((f, header)))
}
}
}
}
impl<Fs: RawFSInterface> SDSSFileIO<Fs> {
fn _new(f: Fs::File) -> Self {
pub fn _new(f: Fs::File) -> Self {
Self {
f,
_fs: PhantomData,

@ -0,0 +1,517 @@
/*
* Created on Mon Sep 25 2023
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2023, Sayan Nandan <ohsayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
/*
Header specification
---
We utilize two different kinds of headers:
- Static header - Mostly to avoid data corruption
- Variable header - For preserving dynamic information
*/
use {
super::{
rw::{RawFSInterface, SDSSFileIO},
SDSSResult,
},
crate::{
engine::storage::{
header::{HostArch, HostEndian, HostOS, HostPointerWidth},
v1::SDSSError,
versions::{self, DriverVersion, HeaderVersion, ServerVersion},
},
util::os,
},
std::{
mem::{transmute, ManuallyDrop},
ops::Range,
},
};
/*
meta
*/
/// The file scope
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, sky_macros::EnumMethods)]
pub enum FileScope {
Journal = 0,
DataBatch = 1,
FlatmapData = 2,
}
impl FileScope {
pub const fn try_new(id: u64) -> Option<Self> {
Some(match id {
0 => Self::Journal,
1 => Self::DataBatch,
2 => Self::FlatmapData,
_ => 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 {
GNSTxnLog = 0,
TableDataBatch = 1,
SysDB = 2,
#[cfg(test)]
TestTransactionLog = 0xFF,
}
impl FileSpecifier {
pub const fn try_new(v: u32) -> Option<Self> {
Some(match v {
0 => Self::GNSTxnLog,
1 => Self::TableDataBatch,
2 => Self::SysDB,
#[cfg(test)]
0xFF => Self::TestTransactionLog,
_ => return None,
})
}
pub const fn new(v: u32) -> Self {
match Self::try_new(v) {
Some(v) => v,
_ => panic!("unknown filespecifier"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct FileSpecifierVersion(u16);
impl FileSpecifierVersion {
pub const fn __new(v: u16) -> Self {
Self(v)
}
}
const SDSS_MAGIC: u64 = 0x4F48534159414E21;
/// Specification for a SDSS file
pub trait FileSpec {
/// The header spec for the file
type Header: Header;
/// Encode data
const ENCODE_DATA: <Self::Header as Header>::EncodeArgs;
/// Decode data
const DECODE_DATA: <Self::Header as Header>::DecodeArgs;
/// Verify data
const VERIFY_DATA: <Self::Header as Header>::DecodeVerifyArgs;
}
/*
file spec impls
*/
#[cfg(test)]
pub struct TestFile;
#[cfg(test)]
impl FileSpec for TestFile {
type Header = SDSSStaticHeaderV1Compact;
const ENCODE_DATA: <Self::Header as Header>::EncodeArgs = (
FileScope::FlatmapData,
FileSpecifier::TestTransactionLog,
FileSpecifierVersion::__new(0),
);
const DECODE_DATA: <Self::Header as Header>::DecodeArgs = ();
const VERIFY_DATA: <Self::Header as Header>::DecodeVerifyArgs = Self::ENCODE_DATA;
}
/// The file specification for the GNS transaction log (impl v1)
pub struct GNSTransactionLogV1;
impl FileSpec for GNSTransactionLogV1 {
type Header = SDSSStaticHeaderV1Compact;
const ENCODE_DATA: <Self::Header as Header>::EncodeArgs = (
FileScope::Journal,
FileSpecifier::GNSTxnLog,
FileSpecifierVersion::__new(0),
);
const DECODE_DATA: <Self::Header as Header>::DecodeArgs = ();
const VERIFY_DATA: <Self::Header as Header>::DecodeVerifyArgs = Self::ENCODE_DATA;
}
/// The file specification for a journal batch
pub struct DataBatchJournalV1;
impl FileSpec for DataBatchJournalV1 {
type Header = SDSSStaticHeaderV1Compact;
const ENCODE_DATA: <Self::Header as Header>::EncodeArgs = (
FileScope::DataBatch,
FileSpecifier::TableDataBatch,
FileSpecifierVersion::__new(0),
);
const DECODE_DATA: <Self::Header as Header>::DecodeArgs = ();
const VERIFY_DATA: <Self::Header as Header>::DecodeVerifyArgs = Self::ENCODE_DATA;
}
/// The file specification for the system db
pub struct SysDBV1;
impl FileSpec for SysDBV1 {
type Header = SDSSStaticHeaderV1Compact;
const ENCODE_DATA: <Self::Header as Header>::EncodeArgs = (
FileScope::FlatmapData,
FileSpecifier::SysDB,
FileSpecifierVersion::__new(0),
);
const DECODE_DATA: <Self::Header as Header>::DecodeArgs = ();
const VERIFY_DATA: <Self::Header as Header>::DecodeVerifyArgs = Self::ENCODE_DATA;
}
/*
header spec
*/
/// SDSS Header specification
pub trait Header: Sized {
/// Encode arguments
type EncodeArgs;
/// Decode arguments
type DecodeArgs;
/// Decode verify arguments
type DecodeVerifyArgs;
/// Encode the header
fn encode<Fs: RawFSInterface>(f: &mut SDSSFileIO<Fs>, args: Self::EncodeArgs)
-> SDSSResult<()>;
/// Decode the header
fn decode<Fs: RawFSInterface>(
f: &mut SDSSFileIO<Fs>,
args: Self::DecodeArgs,
) -> SDSSResult<Self>;
/// Verify the header
fn verify(&self, args: Self::DecodeVerifyArgs) -> SDSSResult<()>;
/// Decode and verify the header
fn decode_verify<Fs: RawFSInterface>(
f: &mut SDSSFileIO<Fs>,
d_args: Self::DecodeArgs,
v_args: Self::DecodeVerifyArgs,
) -> SDSSResult<Self> {
let h = Self::decode(f, d_args)?;
h.verify(v_args)?;
Ok(h)
}
}
/*
header impls
*/
unsafe fn memcpy<const N: usize>(src: &[u8]) -> [u8; N] {
let mut dst = [0u8; N];
src.as_ptr().copy_to_nonoverlapping(dst.as_mut_ptr(), N);
dst
}
macro_rules! var {
(let $($name:ident),* $(,)?) => {
$(let $name;)*
}
}
/*
Compact SDSS Header v1
---
- 1: Magic block (16B): magic + header version
- 2: Static block (40B):
- 2.1: Genesis static record (24B)
- 2.1.1: Software information (16B)
- Server version (8B)
- Driver version (8B)
- 2.1.2: Host information (4B):
- OS (1B)
- Arch (1B)
- Pointer width (1B)
- Endian (1B)
- 2.1.3: File information (4B):
- File class (1B)
- File specifier (1B)
- File specifier version (2B)
- 2.2: Genesis runtime record (16B)
- Host epoch (16B)
- 3: Padding block (8B)
*/
#[repr(align(8))]
#[derive(Debug, PartialEq)]
pub struct SDSSStaticHeaderV1Compact {
// 1 magic block
magic_header_version: HeaderVersion,
// 2.1.1
genesis_static_sw_server_version: ServerVersion,
genesis_static_sw_driver_version: DriverVersion,
// 2.1.2
genesis_static_host_os: HostOS,
genesis_static_host_arch: HostArch,
genesis_static_host_ptr_width: HostPointerWidth,
genesis_static_host_endian: HostEndian,
// 2.1.3
genesis_static_file_class: FileScope,
genesis_static_file_specifier: FileSpecifier,
genesis_static_file_specifier_version: FileSpecifierVersion,
// 2.2
genesis_runtime_epoch_time: u128,
// 3
genesis_padding_block: [u8; 8],
}
impl SDSSStaticHeaderV1Compact {
pub const SIZE: usize = 64;
/// Decode and validate the full header block (validate ONLY; you must verify yourself)
///
/// Notes:
/// - Time might be inconsistent; verify
/// - Compatibility requires additional intervention
/// - If padding block was not zeroed, handle
/// - No file metadata and is verified. Check!
///
fn _decode(block: [u8; 64]) -> SDSSResult<Self> {
var!(let raw_magic, raw_header_version, raw_server_version, raw_driver_version, raw_host_os, raw_host_arch,
raw_host_ptr_width, raw_host_endian, raw_file_class, raw_file_specifier, raw_file_specifier_version,
raw_runtime_epoch_time, raw_paddding_block,
);
macro_rules! u64 {
($pos:expr) => {
u64::from_le_bytes(memcpy(&block[$pos]))
};
}
unsafe {
// UNSAFE(@ohsayan): all segments are correctly accessed (aligned to u8)
raw_magic = u64!(Self::SEG1_MAGIC);
raw_header_version = HeaderVersion::__new(u64!(Self::SEG1_HEADER_VERSION));
raw_server_version = ServerVersion::__new(u64!(Self::SEG2_REC1_SERVER_VERSION));
raw_driver_version = DriverVersion::__new(u64!(Self::SEG2_REC1_DRIVER_VERSION));
raw_host_os = block[Self::SEG2_REC1_HOST_OS];
raw_host_arch = block[Self::SEG2_REC1_HOST_ARCH];
raw_host_ptr_width = block[Self::SEG2_REC1_HOST_PTR_WIDTH];
raw_host_endian = block[Self::SEG2_REC1_HOST_ENDIAN];
raw_file_class = block[Self::SEG2_REC1_FILE_CLASS];
raw_file_specifier = block[Self::SEG2_REC1_FILE_SPECIFIER];
raw_file_specifier_version = FileSpecifierVersion::__new(u16::from_le_bytes(memcpy(
&block[Self::SEG2_REC1_FILE_SPECIFIER_VERSION],
)));
raw_runtime_epoch_time =
u128::from_le_bytes(memcpy(&block[Self::SEG2_REC2_RUNTIME_EPOCH_TIME]));
raw_paddding_block = memcpy::<8>(&block[Self::SEG3_PADDING_BLK]);
}
macro_rules! okay {
($($expr:expr),* $(,)?) => {
$(($expr) &)*true
}
}
let okay_header_version = raw_header_version == versions::CURRENT_HEADER_VERSION;
let okay_server_version = raw_server_version == versions::CURRENT_SERVER_VERSION;
let okay_driver_version = raw_driver_version == versions::CURRENT_DRIVER_VERSION;
let okay = okay!(
// 1.1 mgblk
raw_magic == SDSS_MAGIC,
okay_header_version,
// 2.1.1
okay_server_version,
okay_driver_version,
// 2.1.2
raw_host_os <= HostOS::MAX,
raw_host_arch <= HostArch::MAX,
raw_host_ptr_width <= HostPointerWidth::MAX,
raw_host_endian <= HostEndian::MAX,
// 2.1.3
raw_file_class <= FileScope::MAX,
raw_file_specifier <= FileSpecifier::MAX,
);
if okay {
Ok(unsafe {
// UNSAFE(@ohsayan): the block ranges are very well defined
Self {
// 1.1
magic_header_version: raw_header_version,
// 2.1.1
genesis_static_sw_server_version: raw_server_version,
genesis_static_sw_driver_version: raw_driver_version,
// 2.1.2
genesis_static_host_os: transmute(raw_host_os),
genesis_static_host_arch: transmute(raw_host_arch),
genesis_static_host_ptr_width: transmute(raw_host_ptr_width),
genesis_static_host_endian: transmute(raw_host_endian),
// 2.1.3
genesis_static_file_class: transmute(raw_file_class),
genesis_static_file_specifier: transmute(raw_file_specifier),
genesis_static_file_specifier_version: raw_file_specifier_version,
// 2.2
genesis_runtime_epoch_time: raw_runtime_epoch_time,
// 3
genesis_padding_block: raw_paddding_block,
}
})
} else {
let version_okay = okay_header_version & okay_server_version & okay_driver_version;
let md = ManuallyDrop::new([
SDSSError::HeaderDecodeCorruptedHeader,
SDSSError::HeaderDecodeVersionMismatch,
]);
Err(unsafe {
// UNSAFE(@ohsayan): while not needed, md for drop safety + correct index
md.as_ptr().add(!version_okay as usize).read()
})
}
}
}
impl SDSSStaticHeaderV1Compact {
const SEG1_MAGIC: Range<usize> = 0..8;
const SEG1_HEADER_VERSION: Range<usize> = 8..16;
const SEG2_REC1_SERVER_VERSION: Range<usize> = 16..24;
const SEG2_REC1_DRIVER_VERSION: Range<usize> = 24..32;
const SEG2_REC1_HOST_OS: usize = 32;
const SEG2_REC1_HOST_ARCH: usize = 33;
const SEG2_REC1_HOST_PTR_WIDTH: usize = 34;
const SEG2_REC1_HOST_ENDIAN: usize = 35;
const SEG2_REC1_FILE_CLASS: usize = 36;
const SEG2_REC1_FILE_SPECIFIER: usize = 37;
const SEG2_REC1_FILE_SPECIFIER_VERSION: Range<usize> = 38..40;
const SEG2_REC2_RUNTIME_EPOCH_TIME: Range<usize> = 40..56;
const SEG3_PADDING_BLK: Range<usize> = 56..64;
fn _encode(
file_class: FileScope,
file_specifier: FileSpecifier,
file_specifier_version: FileSpecifierVersion,
epoch_time: u128,
padding_block: [u8; 8],
) -> [u8; 64] {
let mut ret = [0; 64];
// 1. mgblk
ret[Self::SEG1_MAGIC].copy_from_slice(&SDSS_MAGIC.to_le_bytes());
ret[Self::SEG1_HEADER_VERSION]
.copy_from_slice(&versions::CURRENT_HEADER_VERSION.little_endian_u64());
// 2.1.1
ret[Self::SEG2_REC1_SERVER_VERSION]
.copy_from_slice(&versions::CURRENT_SERVER_VERSION.little_endian());
ret[Self::SEG2_REC1_DRIVER_VERSION]
.copy_from_slice(&versions::CURRENT_DRIVER_VERSION.little_endian());
// 2.1.2
ret[Self::SEG2_REC1_HOST_OS] = HostOS::new().value_u8();
ret[Self::SEG2_REC1_HOST_ARCH] = HostArch::new().value_u8();
ret[Self::SEG2_REC1_HOST_PTR_WIDTH] = HostPointerWidth::new().value_u8();
ret[Self::SEG2_REC1_HOST_ENDIAN] = HostEndian::new().value_u8();
// 2.1.3
ret[Self::SEG2_REC1_FILE_CLASS] = file_class.value_u8();
ret[Self::SEG2_REC1_FILE_SPECIFIER] = file_specifier.value_u8();
ret[Self::SEG2_REC1_FILE_SPECIFIER_VERSION]
.copy_from_slice(&file_specifier_version.0.to_le_bytes());
// 2.2
ret[Self::SEG2_REC2_RUNTIME_EPOCH_TIME].copy_from_slice(&epoch_time.to_le_bytes());
// 3
ret[Self::SEG3_PADDING_BLK].copy_from_slice(&padding_block);
ret
}
pub fn _encode_auto(
file_class: FileScope,
file_specifier: FileSpecifier,
file_specifier_version: FileSpecifierVersion,
) -> [u8; 64] {
let epoch_time = os::get_epoch_time();
Self::_encode(
file_class,
file_specifier,
file_specifier_version,
epoch_time,
[0; 8],
)
}
}
impl SDSSStaticHeaderV1Compact {
pub fn header_version(&self) -> HeaderVersion {
self.magic_header_version
}
pub fn server_version(&self) -> ServerVersion {
self.genesis_static_sw_server_version
}
pub fn driver_version(&self) -> DriverVersion {
self.genesis_static_sw_driver_version
}
pub fn host_os(&self) -> HostOS {
self.genesis_static_host_os
}
pub fn host_arch(&self) -> HostArch {
self.genesis_static_host_arch
}
pub fn host_ptr_width(&self) -> HostPointerWidth {
self.genesis_static_host_ptr_width
}
pub fn host_endian(&self) -> HostEndian {
self.genesis_static_host_endian
}
pub fn file_class(&self) -> FileScope {
self.genesis_static_file_class
}
pub fn file_specifier(&self) -> FileSpecifier {
self.genesis_static_file_specifier
}
pub fn file_specifier_version(&self) -> FileSpecifierVersion {
self.genesis_static_file_specifier_version
}
pub fn epoch_time(&self) -> u128 {
self.genesis_runtime_epoch_time
}
pub fn padding_block(&self) -> [u8; 8] {
self.genesis_padding_block
}
}
impl Header for SDSSStaticHeaderV1Compact {
type EncodeArgs = (FileScope, FileSpecifier, FileSpecifierVersion);
type DecodeArgs = ();
type DecodeVerifyArgs = Self::EncodeArgs;
fn encode<Fs: RawFSInterface>(
f: &mut SDSSFileIO<Fs>,
(scope, spec, spec_v): Self::EncodeArgs,
) -> SDSSResult<()> {
let b = Self::_encode_auto(scope, spec, spec_v);
f.fsynced_write(&b)
}
fn decode<Fs: RawFSInterface>(f: &mut SDSSFileIO<Fs>, _: Self::DecodeArgs) -> SDSSResult<Self> {
let mut buf = [0u8; 64];
f.read_to_buffer(&mut buf)?;
Self::_decode(buf)
}
fn verify(&self, (scope, spec, spec_v): Self::DecodeVerifyArgs) -> SDSSResult<()> {
if (self.file_class() == scope)
& (self.file_specifier() == spec)
& (self.file_specifier_version() == spec_v)
{
Ok(())
} else {
Err(SDSSError::HeaderDecodeDataMismatch)
}
}
}

@ -27,10 +27,7 @@
use crate::engine::{
data::{cell::Datacell, DictEntryGeneric, DictGeneric},
fractal::SysConfig,
storage::v1::{
header_meta::{FileScope, FileSpecifier, FileSpecifierVersion},
RawFSInterface, SDSSError, SDSSFileIO, SDSSResult,
},
storage::v1::{spec, RawFSInterface, SDSSError, SDSSFileIO, SDSSResult},
};
const SYSDB_PATH: &str = "sys.db";
@ -75,19 +72,11 @@ pub fn sync_system_database<Fs: RawFSInterface>(cfg: &SysConfig) -> SDSSResult<(
}
}
// open file
let mut file = SDSSFileIO::<Fs>::open_or_create_perm_rw::<false>(
SYSDB_COW_PATH,
FileScope::FlatmapData,
FileSpecifier::SysDB,
FileSpecifierVersion::__new(0),
cfg.host_data().settings_version(),
cfg.host_data().run_mode(),
cfg.host_data().startup_counter(),
)?
.into_created()
.ok_or(SDSSError::OtherError(
"sys.db.cow already exists. please remove this file.",
))?;
let mut file = SDSSFileIO::<Fs>::open_or_create_perm_rw::<spec::SysDBV1>(SYSDB_COW_PATH)?
.into_created()
.ok_or(SDSSError::OtherError(
"sys.db.cow already exists. please remove this file.",
))?;
// write
let buf = super::inf::enc::enc_dict_full::<super::inf::map::GenericDictSpec>(&map);
file.fsynced_write(&buf)?;

@ -41,9 +41,9 @@ use {
DataBatchPersistDriver, DataBatchRestoreDriver, DecodedBatchEvent,
DecodedBatchEventKind, NormalBatch,
},
header_meta::{FileScope, FileSpecifier, FileSpecifierVersion, HostRunMode},
memfs::VirtualFS,
rw::{FileOpen, SDSSFileIO},
spec,
},
},
util::test_utils,
@ -57,18 +57,8 @@ fn pkey(v: impl Into<Datacell>) -> PrimaryIndexKey {
fn open_file(
fpath: &str,
) -> FileOpen<SDSSFileIO<VirtualFS>, (SDSSFileIO<VirtualFS>, super::super::header_impl::SDSSHeader)>
{
SDSSFileIO::open_or_create_perm_rw::<false>(
fpath,
FileScope::DataBatch,
FileSpecifier::TableDataBatch,
FileSpecifierVersion::__new(0),
0,
HostRunMode::Dev,
1,
)
.unwrap()
) -> FileOpen<SDSSFileIO<VirtualFS>, (SDSSFileIO<VirtualFS>, spec::SDSSStaticHeaderV1Compact)> {
SDSSFileIO::open_or_create_perm_rw::<spec::DataBatchJournalV1>(fpath).unwrap()
}
fn open_batch_data(fpath: &str, mdl: &Model) -> DataBatchPersistDriver<VirtualFS> {

@ -25,21 +25,15 @@
*/
use crate::engine::storage::v1::{
header_impl::{FileScope, FileSpecifier, FileSpecifierVersion, HostRunMode},
rw::{FileOpen, SDSSFileIO},
spec,
};
#[test]
fn create_delete() {
{
let f = SDSSFileIO::<super::VirtualFS>::open_or_create_perm_rw::<false>(
let f = SDSSFileIO::<super::VirtualFS>::open_or_create_perm_rw::<spec::TestFile>(
"hello_world.db-tlog",
FileScope::Journal,
FileSpecifier::GNSTxnLog,
FileSpecifierVersion::__new(0),
0,
HostRunMode::Prod,
0,
)
.unwrap();
match f {
@ -47,24 +41,12 @@ fn create_delete() {
FileOpen::Created(_) => {}
};
}
let open = SDSSFileIO::<super::VirtualFS>::open_or_create_perm_rw::<false>(
let open = SDSSFileIO::<super::VirtualFS>::open_or_create_perm_rw::<spec::TestFile>(
"hello_world.db-tlog",
FileScope::Journal,
FileSpecifier::GNSTxnLog,
FileSpecifierVersion::__new(0),
0,
HostRunMode::Prod,
0,
)
.unwrap();
let h = match open {
FileOpen::Existing((_, header)) => header,
let _ = match open {
FileOpen::Existing(_) => {}
_ => panic!(),
};
assert_eq!(h.gr_mdr().file_scope(), FileScope::Journal);
assert_eq!(h.gr_mdr().file_spec(), FileSpecifier::GNSTxnLog);
assert_eq!(h.gr_mdr().file_spec_id(), FileSpecifierVersion::__new(0));
assert_eq!(h.gr_hr().run_mode(), HostRunMode::Prod);
assert_eq!(h.gr_hr().setting_version(), 0);
assert_eq!(h.gr_hr().startup_counter(), 0);
}

@ -27,9 +27,8 @@
use {
crate::{
engine::storage::v1::{
header_impl::{FileSpecifier, FileSpecifierVersion, HostRunMode},
journal::{self, JournalAdapter, JournalWriter},
SDSSError, SDSSResult,
spec, SDSSError, SDSSResult,
},
util,
},
@ -134,16 +133,8 @@ fn open_log(
log_name: &str,
db: &Database,
) -> SDSSResult<JournalWriter<super::VirtualFS, DatabaseTxnAdapter>> {
journal::open_journal::<DatabaseTxnAdapter, super::VirtualFS>(
log_name,
FileSpecifier::TestTransactionLog,
FileSpecifierVersion::__new(0),
0,
HostRunMode::Prod,
1,
&db,
)
.map(|v| v.into_inner())
journal::open_journal::<DatabaseTxnAdapter, super::VirtualFS, spec::TestFile>(log_name, db)
.map(|v| v.into_inner())
}
#[test]

@ -28,18 +28,22 @@
pub mod server_version;
pub const CURRENT_SERVER_VERSION: ServerVersion = v1::V1_SERVER_VERSION;
pub const CURRENT_DRIVER_VERSION: DriverVersion = v1::V1_DRIVER_VERSION;
pub const CURRENT_HEADER_VERSION: HeaderVersion = v1::V1_HEADER_VERSION;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
/// The header version
///
/// The header version is part of the static record and *barely* changes (almost like once in a light year)
pub struct HeaderVersion(u32);
pub struct HeaderVersion(u64);
impl HeaderVersion {
pub const fn __new(v: u32) -> Self {
pub const fn __new(v: u64) -> Self {
Self(v)
}
pub const fn little_endian_u64(&self) -> [u8; 8] {
(self.0 as u64).to_le_bytes()
self.0.to_le_bytes()
}
}

Loading…
Cancel
Save