initial commit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/target
|
||||
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "rply-codec"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
rmp = "0.8.14"
|
||||
thiserror = "2.0.17"
|
||||
Binary file not shown.
+26
@@ -0,0 +1,26 @@
|
||||
mod rply;
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn v2_header() {
|
||||
let mut file = std::fs::File::open("examples/bobl.replay").unwrap();
|
||||
let header = match rply::read_header(&mut file).unwrap() {
|
||||
rply::Header::V0V1(_) => panic!("Version too low"),
|
||||
rply::Header::V2(h) => h,
|
||||
};
|
||||
// content_crc: 2199475946,
|
||||
// initial_state_size: 2531,
|
||||
// identifier: 1761326589,
|
||||
assert_eq!(header.base.version, 2);
|
||||
assert_eq!(header.base.content_crc, 2199475946);
|
||||
assert_eq!(header.base.initial_state_size, 2531);
|
||||
assert_eq!(header.base.identifier, 1761326589);
|
||||
}
|
||||
}
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
#[repr(usize)]
|
||||
pub enum HeaderV0V1Part {
|
||||
Magic = 0,
|
||||
Version = 4,
|
||||
CRC = 8,
|
||||
StateSize = 12,
|
||||
Identifier = 16,
|
||||
HeaderLen = 24,
|
||||
}
|
||||
#[repr(usize)]
|
||||
enum HeaderV2Part {
|
||||
Magic = 0,
|
||||
Version = 4,
|
||||
CRC = 8,
|
||||
StateSize = 12,
|
||||
Identifier = 16,
|
||||
FrameCount = 24,
|
||||
BlockSize = 28,
|
||||
SuperblockSize = 32,
|
||||
CheckpointConfig = 36,
|
||||
HeaderLen = 40,
|
||||
}
|
||||
const HEADER_V0V1_LEN_BYTES: usize = HeaderV0V1Part::HeaderLen as usize;
|
||||
const HEADER_LEN_BYTES: usize = HeaderV2Part::HeaderLen as usize;
|
||||
|
||||
const VERSION: u32 = 2;
|
||||
const MAGIC: u32 = 0x42535632;
|
||||
|
||||
#[repr(u8)]
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug)]
|
||||
pub enum FrameToken {
|
||||
Invalid = 0,
|
||||
Regular = b'f',
|
||||
Checkpoint = b'c',
|
||||
Checkpoint2 = b'C',
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug)]
|
||||
pub enum SSToken {
|
||||
Start = 0,
|
||||
NewBlock = 1,
|
||||
NewSuperblock = 2,
|
||||
SuperblockSeq = 3,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug)]
|
||||
pub enum Compression {
|
||||
None = 0,
|
||||
Zlib = 1,
|
||||
Zstd = 2,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for Compression {}
|
||||
|
||||
#[repr(u8)]
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug)]
|
||||
pub enum Encoding {
|
||||
Raw = 0,
|
||||
Statestream = 1,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for Encoding {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HeaderBase {
|
||||
pub version: u32,
|
||||
pub content_crc: u32,
|
||||
pub initial_state_size: u32,
|
||||
pub identifier: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HeaderV2 {
|
||||
pub base: HeaderBase,
|
||||
pub frame_count: u32,
|
||||
pub block_size: u32,
|
||||
pub superblock_size: u32,
|
||||
pub checkpoint_commit_interval: u8,
|
||||
pub checkpoint_commit_threshold: u8,
|
||||
pub checkpoint_compression: Compression,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Header {
|
||||
V0V1(HeaderBase),
|
||||
V2(HeaderV2),
|
||||
}
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ReplayError {
|
||||
#[error("Invalid replay magic {0}")]
|
||||
Magic(u32),
|
||||
#[error("Unsupported version {0}")]
|
||||
Version(u32),
|
||||
#[error("Unsupported compression scheme {0}")]
|
||||
Compression(u8),
|
||||
#[error("I/O Error")]
|
||||
IO(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, ReplayError>;
|
||||
|
||||
pub fn read_header(rply: &mut impl std::io::Read) -> Result<Header> {
|
||||
let mut bytes = vec![0; HEADER_LEN_BYTES];
|
||||
rply.read_exact(&mut bytes[0..HEADER_V0V1_LEN_BYTES])?;
|
||||
// These unwraps are safe because if I can take e.g. a slice of length 4, I already have a 4-byte value.
|
||||
// And I know I can take those slices because read_exact read exactly 24 bytes.
|
||||
let magic = u32::from_le_bytes(
|
||||
<[u8; 4]>::try_from(
|
||||
&bytes[(HeaderV0V1Part::Magic as usize)..(HeaderV0V1Part::Magic as usize + 4)],
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
if magic != MAGIC {
|
||||
return Err(ReplayError::Magic(magic));
|
||||
}
|
||||
let version = u32::from_le_bytes(
|
||||
<[u8; 4]>::try_from(
|
||||
&bytes[(HeaderV0V1Part::Version as usize)..(HeaderV0V1Part::Version as usize + 4)],
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
if version > 2 {
|
||||
return Err(ReplayError::Version(version));
|
||||
}
|
||||
let content_crc = u32::from_le_bytes(
|
||||
<[u8; 4]>::try_from(
|
||||
&bytes[(HeaderV0V1Part::CRC as usize)..(HeaderV0V1Part::CRC as usize + 4)],
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
let initial_state_size = u32::from_le_bytes(
|
||||
<[u8; 4]>::try_from(
|
||||
&bytes[(HeaderV0V1Part::StateSize as usize)..(HeaderV0V1Part::StateSize as usize + 4)],
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
let identifier = u64::from_le_bytes(
|
||||
<[u8; 8]>::try_from(
|
||||
&bytes
|
||||
[(HeaderV0V1Part::Identifier as usize)..(HeaderV0V1Part::Identifier as usize + 8)],
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
let base = HeaderBase {
|
||||
version,
|
||||
content_crc,
|
||||
initial_state_size,
|
||||
identifier,
|
||||
};
|
||||
if version < 2 {
|
||||
return Ok(Header::V0V1(base));
|
||||
}
|
||||
rply.read_exact(&mut bytes[HEADER_V0V1_LEN_BYTES..HEADER_LEN_BYTES]);
|
||||
let frame_count = u32::from_le_bytes(
|
||||
<[u8; 4]>::try_from(
|
||||
&bytes[(HeaderV2Part::FrameCount as usize)..(HeaderV2Part::FrameCount as usize + 4)],
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
let block_size = u32::from_le_bytes(
|
||||
<[u8; 4]>::try_from(
|
||||
&bytes[(HeaderV2Part::BlockSize as usize)..(HeaderV2Part::BlockSize as usize + 4)],
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
let superblock_size = u32::from_le_bytes(
|
||||
<[u8; 4]>::try_from(
|
||||
&bytes[(HeaderV2Part::SuperblockSize as usize)
|
||||
..(HeaderV2Part::SuperblockSize as usize + 4)],
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
let cp_config = u32::from_le_bytes(
|
||||
<[u8; 4]>::try_from(
|
||||
&bytes[(HeaderV2Part::CheckpointConfig as usize)
|
||||
..(HeaderV2Part::CheckpointConfig as usize + 4)],
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
let checkpoint_commit_interval = (cp_config >> 24) as u8;
|
||||
let checkpoint_commit_threshold = ((cp_config >> 16) & 0xFF) as u8;
|
||||
let checkpoint_compression = Compression::try_from(((cp_config >> 8) & 0xFF) as u8)?;
|
||||
Ok(Header::V2(HeaderV2 {
|
||||
base,
|
||||
frame_count,
|
||||
block_size,
|
||||
superblock_size,
|
||||
checkpoint_commit_interval,
|
||||
checkpoint_commit_threshold,
|
||||
checkpoint_compression,
|
||||
}))
|
||||
}
|
||||
Reference in New Issue
Block a user