#![allow(clippy::missing_safety_doc)]
use super::jit_debug;
use crate::DEFAULT_MAX_COLLECTION_ELEMENTS;
#[repr(C)]
pub struct PostcardJitPosError {
pub new_pos: usize,
pub error: i32,
}
#[repr(C)]
pub struct PostcardJitPosEndError {
pub packed_pos_end: usize,
pub error: i32,
}
impl PostcardJitPosEndError {
pub fn new(pos: usize, is_end: bool, error: i32) -> Self {
let packed_pos_end = if is_end { pos | (1usize << 63) } else { pos };
Self {
packed_pos_end,
error,
}
}
#[allow(dead_code)]
pub fn pos(&self) -> usize {
self.packed_pos_end & 0x7FFFFFFFFFFFFFFF
}
#[allow(dead_code)]
pub fn is_end(&self) -> bool {
(self.packed_pos_end >> 63) != 0
}
}
#[repr(C)]
pub struct PostcardJitPosValueError {
pub packed_pos_value: usize,
pub error: i32,
}
impl PostcardJitPosValueError {
pub fn new(new_pos: usize, value: bool, error: i32) -> Self {
let packed_pos_value = if value {
new_pos | (1usize << 63)
} else {
new_pos
};
Self {
packed_pos_value,
error,
}
}
#[allow(dead_code)]
pub fn new_pos(&self) -> usize {
self.packed_pos_value & 0x7FFFFFFFFFFFFFFF
}
#[allow(dead_code)]
pub fn value(&self) -> bool {
(self.packed_pos_value >> 63) != 0
}
}
#[repr(C)]
pub struct PostcardJitVarintResult {
pub new_pos: usize,
pub value: u64,
pub error: i32,
}
pub mod error {
pub const UNEXPECTED_EOF: i32 = -100;
pub const INVALID_BOOL: i32 = -101;
pub const VARINT_OVERFLOW: i32 = -102;
pub const SEQ_UNDERFLOW: i32 = -103;
pub const COLLECTION_TOO_LARGE: i32 = -109;
pub const UNSUPPORTED: i32 = -1;
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn postcard_jit_read_varint(
input: *const u8,
len: usize,
pos: usize,
) -> PostcardJitVarintResult {
jit_debug!("[postcard_jit_read_varint] pos={}, len={}", pos, len);
let mut result: u64 = 0;
let mut shift: u32 = 0;
let mut p = pos;
loop {
if p >= len {
jit_debug!("[postcard_jit_read_varint] EOF at pos={}", p);
return PostcardJitVarintResult {
new_pos: p,
value: 0,
error: error::UNEXPECTED_EOF,
};
}
let byte = unsafe { *input.add(p) };
p += 1;
let data = (byte & 0x7F) as u64;
if shift >= 64 {
jit_debug!("[postcard_jit_read_varint] overflow at shift={}", shift);
return PostcardJitVarintResult {
new_pos: p,
value: 0,
error: error::VARINT_OVERFLOW,
};
}
result |= data << shift;
shift += 7;
if (byte & 0x80) == 0 {
jit_debug!(
"[postcard_jit_read_varint] done: value={}, new_pos={}",
result,
p
);
return PostcardJitVarintResult {
new_pos: p,
value: result,
error: 0,
};
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn postcard_jit_seq_begin(
input: *const u8,
len: usize,
pos: usize,
state_ptr: *mut u64,
) -> PostcardJitPosError {
jit_debug!(
"[postcard_jit_seq_begin] pos={}, len={}, state_ptr={:p}",
pos,
len,
state_ptr
);
let result = unsafe { postcard_jit_read_varint(input, len, pos) };
if result.error != 0 {
return PostcardJitPosError {
new_pos: result.new_pos,
error: result.error,
};
}
if result.value > DEFAULT_MAX_COLLECTION_ELEMENTS {
return PostcardJitPosError {
new_pos: result.new_pos,
error: error::COLLECTION_TOO_LARGE,
};
}
unsafe {
*state_ptr = result.value;
}
jit_debug!(
"[postcard_jit_seq_begin] remaining={}, new_pos={}",
result.value,
result.new_pos
);
PostcardJitPosError {
new_pos: result.new_pos,
error: 0,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn postcard_jit_seq_is_end(
pos: usize,
state_ptr: *const u64,
) -> PostcardJitPosEndError {
let remaining = unsafe { *state_ptr };
jit_debug!(
"[postcard_jit_seq_is_end] pos={}, remaining={}",
pos,
remaining
);
let is_end = remaining == 0;
jit_debug!("[postcard_jit_seq_is_end] -> is_end={}", is_end);
PostcardJitPosEndError::new(pos, is_end, 0)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn postcard_jit_seq_next(
pos: usize,
state_ptr: *mut u64,
) -> PostcardJitPosError {
let remaining = unsafe { *state_ptr };
jit_debug!(
"[postcard_jit_seq_next] pos={}, remaining={}",
pos,
remaining
);
if remaining == 0 {
return PostcardJitPosError {
new_pos: pos,
error: error::SEQ_UNDERFLOW,
};
}
unsafe {
*state_ptr = remaining - 1;
}
jit_debug!("[postcard_jit_seq_next] -> new_remaining={}", remaining - 1);
PostcardJitPosError {
new_pos: pos,
error: 0,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn postcard_jit_parse_bool(
input: *const u8,
len: usize,
pos: usize,
) -> PostcardJitPosValueError {
jit_debug!("[postcard_jit_parse_bool] pos={}, len={}", pos, len);
if pos >= len {
jit_debug!("[postcard_jit_parse_bool] EOF!");
return PostcardJitPosValueError::new(pos, false, error::UNEXPECTED_EOF);
}
let byte = unsafe { *input.add(pos) };
jit_debug!("[postcard_jit_parse_bool] byte={}", byte);
match byte {
0 => {
jit_debug!("[postcard_jit_parse_bool] -> false");
PostcardJitPosValueError::new(pos + 1, false, 0)
}
1 => {
jit_debug!("[postcard_jit_parse_bool] -> true");
PostcardJitPosValueError::new(pos + 1, true, 0)
}
_ => {
jit_debug!("[postcard_jit_parse_bool] -> invalid!");
PostcardJitPosValueError::new(pos, false, error::INVALID_BOOL)
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn postcard_jit_bulk_copy_u8(dest: *mut u8, src: *const u8, count: usize) {
jit_debug!(
"[postcard_jit_bulk_copy_u8] dest={:p}, src={:p}, count={}",
dest,
src,
count
);
unsafe {
core::ptr::copy_nonoverlapping(src, dest, count);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_varint_single_byte() {
let input = [0u8];
let result = unsafe { postcard_jit_read_varint(input.as_ptr(), input.len(), 0) };
assert_eq!(result.error, 0);
assert_eq!(result.value, 0);
assert_eq!(result.new_pos, 1);
let input = [127u8];
let result = unsafe { postcard_jit_read_varint(input.as_ptr(), input.len(), 0) };
assert_eq!(result.error, 0);
assert_eq!(result.value, 127);
assert_eq!(result.new_pos, 1);
}
#[test]
fn test_varint_multi_byte() {
let input = [0x80, 0x01];
let result = unsafe { postcard_jit_read_varint(input.as_ptr(), input.len(), 0) };
assert_eq!(result.error, 0);
assert_eq!(result.value, 128);
assert_eq!(result.new_pos, 2);
let input = [0xAC, 0x02];
let result = unsafe { postcard_jit_read_varint(input.as_ptr(), input.len(), 0) };
assert_eq!(result.error, 0);
assert_eq!(result.value, 300);
assert_eq!(result.new_pos, 2);
}
#[test]
fn test_parse_bool() {
let input = [0u8];
let result = unsafe { postcard_jit_parse_bool(input.as_ptr(), input.len(), 0) };
assert_eq!(result.error, 0);
assert!(!result.value());
assert_eq!(result.new_pos(), 1);
let input = [1u8];
let result = unsafe { postcard_jit_parse_bool(input.as_ptr(), input.len(), 0) };
assert_eq!(result.error, 0);
assert!(result.value());
assert_eq!(result.new_pos(), 1);
let input = [2u8];
let result = unsafe { postcard_jit_parse_bool(input.as_ptr(), input.len(), 0) };
assert_eq!(result.error, error::INVALID_BOOL);
}
#[test]
fn test_seq_operations() {
let input = [0x03, 0x01, 0x00, 0x01];
let mut state: u64 = 0;
let result = unsafe { postcard_jit_seq_begin(input.as_ptr(), input.len(), 0, &mut state) };
assert_eq!(result.error, 0);
assert_eq!(result.new_pos, 1); assert_eq!(state, 3);
let result = unsafe { postcard_jit_seq_is_end(result.new_pos, &state) };
assert_eq!(result.error, 0);
assert!(!result.is_end());
let result = unsafe { postcard_jit_seq_next(result.pos(), &mut state) };
assert_eq!(result.error, 0);
assert_eq!(state, 2);
let result = unsafe { postcard_jit_seq_next(result.new_pos, &mut state) };
assert_eq!(result.error, 0);
assert_eq!(state, 1);
let result = unsafe { postcard_jit_seq_next(result.new_pos, &mut state) };
assert_eq!(result.error, 0);
assert_eq!(state, 0);
let result = unsafe { postcard_jit_seq_is_end(result.new_pos, &state) };
assert_eq!(result.error, 0);
assert!(result.is_end());
}
#[test]
fn test_seq_begin_rejects_oversized_length() {
let mut encoded = Vec::new();
let mut value = DEFAULT_MAX_COLLECTION_ELEMENTS + 1;
loop {
let mut byte = (value & 0x7F) as u8;
value >>= 7;
if value != 0 {
byte |= 0x80;
}
encoded.push(byte);
if value == 0 {
break;
}
}
let mut state: u64 = 0;
let result =
unsafe { postcard_jit_seq_begin(encoded.as_ptr(), encoded.len(), 0, &mut state) };
assert_eq!(result.error, error::COLLECTION_TOO_LARGE);
}
}