use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use std::fmt;
pub type RecordKind = u8;
pub type SysId = u64;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RecordKey {
pub kind: RecordKind,
pub sys_id: SysId,
}
pub type TxSeq = u64;
pub type PkBytes = SmallVec<[u8; 64]>;
pub type PkEncodeFn = fn(&[u8]) -> PkBytes;
pub const RECORD_HEADER_SIZE: usize = 16;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FieldType {
Bool,
U8,
U16,
U32,
U64,
I32,
I64,
U128,
FixedBytes,
VarBytes,
EnumU8,
}
#[repr(C)]
#[derive(
Debug, Clone, Copy, Default, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize,
)]
pub struct Version {
main: u8,
minor: u8,
}
const _: () = assert!(std::mem::size_of::<Version>() == 2);
impl Version {
#[inline(always)]
pub const fn new(main: u8, minor: u8) -> Self {
Self { main, minor }
}
#[inline(always)]
pub const fn main(self) -> u8 {
self.main
}
#[inline(always)]
pub const fn minor(self) -> u8 {
self.minor
}
#[inline(always)]
pub const fn to_bytes(self) -> [u8; 2] {
[self.main, self.minor]
}
#[inline(always)]
pub const fn from_bytes(bytes: [u8; 2]) -> Self {
Self::new(bytes[0], bytes[1])
}
}
impl From<u16> for Version {
#[inline(always)]
fn from(value: u16) -> Self {
Self::from_bytes(value.to_le_bytes())
}
}
impl From<Version> for u16 {
#[inline(always)]
fn from(value: Version) -> Self {
u16::from_le_bytes(value.to_bytes())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FieldDefinition {
pub name: &'static str,
pub field_index: u32,
pub offset: u32,
pub ty: FieldType,
pub len: u32,
pub rust_type_name: &'static str,
pub enum_type_name: Option<&'static str>,
pub immutable: bool,
}
#[derive(Debug, Clone, Copy)]
pub struct RecordDefinition {
pub kind: RecordKind,
pub name: &'static str,
pub is_pk_idx: bool,
pub support_range_scan: bool,
pub data_size: u32,
pub version: u16,
pub pk_encode: Option<PkEncodeFn>,
pub fields: &'static [FieldDefinition],
pub reserved_fields: &'static [FieldDefinition],
pub pk_fields: &'static [&'static str],
}
impl RecordDefinition {
#[inline]
pub fn field_by_name(&self, name: &str) -> Option<&FieldDefinition> {
self.fields.iter().find(|f| f.name == name)
}
}
pub trait RecordSchema {
const KIND: RecordKind;
const RECORD_LEN: usize;
const FIELD_COUNT: usize;
fn definition() -> &'static RecordDefinition;
}
pub trait PkCodec {
fn encode_pk_from_bytes(data: &[u8]) -> PkBytes;
}
pub trait GeneratedRecordAccess: RecordSchema {
const DATA_LEN: usize;
type Access<'a>;
type NewBuilder<'a>;
type UpdateBuilder<'a>;
fn wrap<'a>(buf: &'a [u8]) -> Self::Access<'a>;
fn wrap_new<'a>(buf: &'a mut [u8]) -> Self::NewBuilder<'a>;
fn wrap_update<'a>(buf: &'a mut [u8]) -> Self::UpdateBuilder<'a>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EnumDecodeError {
pub type_name: &'static str,
pub raw: u8,
}
impl fmt::Display for EnumDecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "invalid {} discriminant: {}", self.type_name, self.raw)
}
}
impl std::error::Error for EnumDecodeError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AccessError {
pub required: usize,
pub actual: usize,
}
impl fmt::Display for AccessError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"buffer too small: required {} bytes, got {}",
self.required, self.actual
)
}
}
impl std::error::Error for AccessError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EnumVariantDefinition {
pub name: &'static str,
pub discriminant: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EnumDefinition {
pub name: &'static str,
pub variants: &'static [EnumVariantDefinition],
}
pub trait EnumU8: Copy + Eq + 'static {
fn to_u8(self) -> u8;
fn try_from_u8(v: u8) -> Result<Self, EnumDecodeError>;
fn type_name() -> &'static str;
fn definition() -> &'static EnumDefinition;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FixedBytes<const N: usize> {
len: u16,
buf: [u8; N],
}
impl<const N: usize> FixedBytes<N> {
pub fn new(bytes: &[u8]) -> Result<Self, AccessError> {
if bytes.len() > N {
return Err(AccessError {
required: N,
actual: bytes.len(),
});
}
let mut buf = [0u8; N];
buf[..bytes.len()].copy_from_slice(bytes);
Ok(Self {
len: bytes.len() as u16,
buf,
})
}
#[inline(always)]
pub fn len(&self) -> usize {
self.len as usize
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline(always)]
pub fn as_slice(&self) -> &[u8] {
&self.buf[..self.len()]
}
#[inline(always)]
pub fn padded_slice(&self) -> &[u8; N] {
&self.buf
}
#[inline]
pub fn pk_bytes(&self) -> PkBytes {
let mut pk = PkBytes::new();
pk.extend_from_slice(&self.buf);
pk
}
}
impl<const N: usize, const M: usize> From<&[u8; M]> for FixedBytes<N> {
#[inline]
fn from(bytes: &[u8; M]) -> Self {
const { assert!(M <= N, "FixedBytes: source length exceeds capacity") };
let mut buf = [0u8; N];
buf[..M].copy_from_slice(bytes);
Self { len: M as u16, buf }
}
}
pub struct PkBuilder {
buf: PkBytes,
}
impl PkBuilder {
#[inline]
pub fn new() -> Self {
Self {
buf: SmallVec::new(),
}
}
#[inline]
pub fn push_u8(&mut self, v: u8) {
self.buf.push(v);
}
#[inline]
pub fn push_u16(&mut self, v: u16) {
self.buf.extend_from_slice(&v.to_be_bytes());
}
#[inline]
pub fn push_u32(&mut self, v: u32) {
self.buf.extend_from_slice(&v.to_be_bytes());
}
#[inline]
pub fn push_u64(&mut self, v: u64) {
self.buf.extend_from_slice(&v.to_be_bytes());
}
#[inline]
pub fn push_i32(&mut self, v: i32) {
let encoded = ((v as u32) ^ 0x8000_0000).to_be_bytes();
self.buf.extend_from_slice(&encoded);
}
#[inline]
pub fn push_i64(&mut self, v: i64) {
let encoded = ((v as u64) ^ 0x8000_0000_0000_0000).to_be_bytes();
self.buf.extend_from_slice(&encoded);
}
#[inline]
pub fn push_bytes(&mut self, bytes: &[u8]) {
self.buf.extend_from_slice(bytes);
}
#[inline]
pub fn finish(self) -> PkBytes {
self.buf
}
}
impl Default for PkBuilder {
fn default() -> Self {
Self::new()
}
}
#[inline(always)]
fn check_len(buf: &[u8], need: usize) -> Result<(), AccessError> {
if buf.len() >= need {
Ok(())
} else {
Err(AccessError {
required: need,
actual: buf.len(),
})
}
}
#[inline(always)]
fn checked_need(offset: usize, len: usize, actual: usize) -> Result<usize, AccessError> {
offset.checked_add(len).ok_or(AccessError {
required: usize::MAX,
actual,
})
}
#[inline(always)]
fn assert_len(buf: &[u8], need: usize) {
assert!(
buf.len() >= need,
"write buffer too small: {} < {}",
buf.len(),
need
);
}
#[inline(always)]
pub fn read_u8(buf: &[u8], offset: usize) -> Result<u8, AccessError> {
check_len(buf, checked_need(offset, 1, buf.len())?)?;
Ok(buf[offset])
}
#[inline(always)]
pub fn write_u8(buf: &mut [u8], offset: usize, v: u8) {
assert_len(buf, offset + 1);
buf[offset] = v;
}
#[inline(always)]
pub fn read_bool(buf: &[u8], offset: usize) -> Result<bool, AccessError> {
Ok(read_u8(buf, offset)? != 0)
}
#[inline(always)]
pub fn write_bool(buf: &mut [u8], offset: usize, v: bool) {
write_u8(buf, offset, if v { 1 } else { 0 });
}
#[inline(always)]
pub fn read_u16_le(buf: &[u8], offset: usize) -> Result<u16, AccessError> {
let end = checked_need(offset, 2, buf.len())?;
check_len(buf, end)?;
let mut tmp = [0u8; 2];
tmp.copy_from_slice(&buf[offset..end]);
Ok(u16::from_le_bytes(tmp))
}
#[inline(always)]
pub fn write_u16_le(buf: &mut [u8], offset: usize, v: u16) {
assert_len(buf, offset + 2);
buf[offset..offset + 2].copy_from_slice(&v.to_le_bytes());
}
#[inline(always)]
pub fn read_u32_le(buf: &[u8], offset: usize) -> Result<u32, AccessError> {
let end = checked_need(offset, 4, buf.len())?;
check_len(buf, end)?;
let mut tmp = [0u8; 4];
tmp.copy_from_slice(&buf[offset..end]);
Ok(u32::from_le_bytes(tmp))
}
#[inline(always)]
pub fn write_u32_le(buf: &mut [u8], offset: usize, v: u32) {
assert_len(buf, offset + 4);
buf[offset..offset + 4].copy_from_slice(&v.to_le_bytes());
}
#[inline(always)]
pub fn read_u64_le(buf: &[u8], offset: usize) -> Result<u64, AccessError> {
let end = checked_need(offset, 8, buf.len())?;
check_len(buf, end)?;
let mut tmp = [0u8; 8];
tmp.copy_from_slice(&buf[offset..end]);
Ok(u64::from_le_bytes(tmp))
}
#[inline(always)]
pub fn write_u64_le(buf: &mut [u8], offset: usize, v: u64) {
assert_len(buf, offset + 8);
buf[offset..offset + 8].copy_from_slice(&v.to_le_bytes());
}
#[inline(always)]
pub fn read_i32_le(buf: &[u8], offset: usize) -> Result<i32, AccessError> {
Ok(read_u32_le(buf, offset)? as i32)
}
#[inline(always)]
pub fn write_i32_le(buf: &mut [u8], offset: usize, v: i32) {
write_u32_le(buf, offset, v as u32);
}
#[inline(always)]
pub fn read_i64_le(buf: &[u8], offset: usize) -> Result<i64, AccessError> {
Ok(read_u64_le(buf, offset)? as i64)
}
#[inline(always)]
pub fn write_i64_le(buf: &mut [u8], offset: usize, v: i64) {
write_u64_le(buf, offset, v as u64);
}
#[inline(always)]
pub fn read_u128_le(buf: &[u8], offset: usize) -> Result<u128, AccessError> {
let end = checked_need(offset, 16, buf.len())?;
check_len(buf, end)?;
let mut tmp = [0u8; 16];
tmp.copy_from_slice(&buf[offset..end]);
Ok(u128::from_le_bytes(tmp))
}
#[inline(always)]
pub fn write_u128_le(buf: &mut [u8], offset: usize, v: u128) {
assert_len(buf, offset + 16);
buf[offset..offset + 16].copy_from_slice(&v.to_le_bytes());
}
#[inline(always)]
pub fn read_fixed_bytes<const N: usize>(
buf: &[u8],
offset: usize,
) -> Result<FixedBytes<N>, AccessError> {
let start = checked_need(offset, 2, buf.len())?;
let end = checked_need(start, N, buf.len())?;
check_len(buf, end)?;
let len = read_u16_le(buf, offset)? as usize;
if len > N {
return Err(AccessError {
required: len,
actual: N,
});
}
let mut tmp = [0u8; N];
tmp.copy_from_slice(&buf[start..end]);
Ok(FixedBytes {
len: len as u16,
buf: tmp,
})
}
#[inline(always)]
pub fn write_fixed_bytes<const N: usize>(buf: &mut [u8], offset: usize, v: &FixedBytes<N>) {
assert_len(buf, offset + 2 + N);
write_u16_le(buf, offset, v.len);
buf[offset + 2..offset + 2 + N].fill(0);
buf[offset + 2..offset + 2 + v.len()].copy_from_slice(v.as_slice());
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Command {
command_kind: u8,
ext_seq: u64,
ref_ext_time_us: u64,
payload: Vec<u8>,
}
impl Command {
#[inline]
pub fn new(command_kind: u8, ext_seq: u64, ref_ext_time_us: u64, payload: Vec<u8>) -> Self {
Self {
command_kind,
ext_seq,
ref_ext_time_us,
payload,
}
}
#[inline(always)]
pub fn command_kind(&self) -> u8 {
self.command_kind
}
#[inline(always)]
pub fn ext_seq(&self) -> u64 {
self.ext_seq
}
#[inline(always)]
pub fn ingress_dedupe_key(&self) -> u64 {
self.ext_seq
}
#[inline(always)]
pub fn ref_ext_time_us(&self) -> u64 {
self.ref_ext_time_us
}
#[inline(always)]
pub fn payload_len(&self) -> usize {
self.payload.len()
}
#[inline(always)]
pub fn payload(&self) -> &[u8] {
&self.payload
}
#[inline(always)]
pub fn into_payload(self) -> Vec<u8> {
self.payload
}
}
#[inline(always)]
pub fn read_var_bytes(data: &[u8], offset: usize) -> Result<(&[u8], usize), AccessError> {
let len = read_u16_le(data, offset)? as usize;
let end = checked_need(checked_need(offset, 2, data.len())?, len, data.len())?;
if end > data.len() {
return Err(AccessError {
required: end,
actual: data.len(),
});
}
Ok((&data[offset + 2..end], end))
}
#[inline]
pub fn write_var_bytes(buf: &mut Vec<u8>, content: &[u8]) -> Result<(), AccessError> {
let len = u16::try_from(content.len()).map_err(|_| AccessError {
required: content.len(),
actual: u16::MAX as usize,
})?;
buf.extend_from_slice(&len.to_le_bytes());
buf.extend_from_slice(content);
Ok(())
}
#[derive(Debug, Clone, Copy)]
pub struct PayloadFieldDefinition {
pub name: &'static str,
pub field_index: u32,
pub ty: FieldType,
pub rust_type_name: &'static str,
pub enum_type_name: Option<&'static str>,
pub fixed_size: Option<u32>,
}
#[derive(Debug, Clone, Copy)]
pub struct CommandDefinition {
pub kind: u8,
pub name: &'static str,
pub version: u16,
pub fields: &'static [PayloadFieldDefinition],
}
pub trait CommandSchema {
const KIND: u8;
fn definition() -> &'static CommandDefinition;
}
pub trait GeneratedCommandAccess: CommandSchema {
type Access<'a>;
type Builder;
fn wrap(data: &[u8]) -> Self::Access<'_>;
fn builder() -> Self::Builder;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Event {
event_kind: u8,
event_seq: u32,
payload: Vec<u8>,
}
impl Event {
#[inline]
pub fn new(event_kind: u8, event_seq: u32, payload: Vec<u8>) -> Self {
Self {
event_kind,
event_seq,
payload,
}
}
#[inline(always)]
pub fn event_kind(&self) -> u8 {
self.event_kind
}
#[inline(always)]
pub fn event_seq(&self) -> u32 {
self.event_seq
}
#[inline(always)]
pub fn payload(&self) -> &[u8] {
&self.payload
}
#[inline(always)]
pub fn into_payload(self) -> Vec<u8> {
self.payload
}
#[inline]
pub fn into_frame(self, tx_seq: TxSeq) -> EventFrame {
EventFrame::new(tx_seq, self.event_seq, self.event_kind, self.payload)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EventFrame {
tx_seq: TxSeq,
event_seq: u32,
event_kind: u8,
payload: Vec<u8>,
}
impl EventFrame {
#[inline]
pub fn new(tx_seq: u64, event_seq: u32, event_kind: u8, payload: Vec<u8>) -> Self {
Self {
tx_seq,
event_seq,
event_kind,
payload,
}
}
#[inline(always)]
pub fn tx_seq(&self) -> TxSeq {
self.tx_seq
}
#[inline(always)]
pub fn event_seq(&self) -> u32 {
self.event_seq
}
#[inline(always)]
pub fn event_kind(&self) -> u8 {
self.event_kind
}
#[inline(always)]
pub fn frame_len(&self) -> usize {
24 + self.payload.len()
}
#[inline(always)]
pub fn payload(&self) -> &[u8] {
&self.payload
}
#[inline(always)]
pub fn into_payload(self) -> Vec<u8> {
self.payload
}
#[inline]
pub fn into_parts(self) -> (TxSeq, Event) {
(
self.tx_seq,
Event {
event_kind: self.event_kind,
event_seq: self.event_seq,
payload: self.payload,
},
)
}
}
#[derive(Debug, Clone, Copy)]
pub struct EventDefinition {
pub kind: u8,
pub name: &'static str,
pub version: u16,
pub fields: &'static [PayloadFieldDefinition],
}
pub trait EventSchema {
const KIND: u8;
fn definition() -> &'static EventDefinition;
}
pub trait GeneratedEventAccess: EventSchema {
type Access<'a>;
type Builder;
fn wrap(data: &[u8]) -> Self::Access<'_>;
fn builder() -> Self::Builder;
}