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
parent
c4d51ac8e7
commit
ef3d71f593
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue