use crate::error::Result;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Write};
pub const SHORT_BYTES: usize = 2;
pub const INT_BYTES: usize = 4;
pub const LONG_BYTES: usize = 8;
pub const UNSIGNED_INT_BYTES: usize = 4;
pub const ZERO_LENGTH_BYTE_ARRAY: &[u8] = &[];
pub fn write_i16(w: &mut impl Write, val: i16) -> Result<()> {
Ok(w.write_i16::<LittleEndian>(val)?)
}
pub fn read_i16(r: &mut impl Read) -> Result<i16> {
Ok(r.read_i16::<LittleEndian>()?)
}
pub fn write_i32(w: &mut impl Write, val: i32) -> Result<()> {
Ok(w.write_i32::<LittleEndian>(val)?)
}
pub fn read_i32(r: &mut impl Read) -> Result<i32> {
Ok(r.read_i32::<LittleEndian>()?)
}
pub fn write_i64(w: &mut impl Write, val: i64) -> Result<()> {
Ok(w.write_i64::<LittleEndian>(val)?)
}
pub fn read_i64(r: &mut impl Read) -> Result<i64> {
Ok(r.read_i64::<LittleEndian>()?)
}
pub fn write_u32(w: &mut impl Write, val: u32) -> Result<()> {
Ok(w.write_u32::<LittleEndian>(val)?)
}
pub fn read_u32(r: &mut impl Read) -> Result<u32> {
Ok(r.read_u32::<LittleEndian>()?)
}
pub fn write_packed_i32(w: &mut impl Write, val: i32) -> Result<usize> {
Ok(noxu_util::packed::write_packed_i32(w, val)?)
}
pub fn read_packed_i32(r: &mut impl Read) -> Result<i32> {
Ok(noxu_util::packed::read_packed_i32(r)?)
}
pub fn packed_i32_size(val: i32) -> usize {
noxu_util::packed::packed_i32_size(val)
}
pub fn write_packed_i64(w: &mut impl Write, val: i64) -> Result<usize> {
Ok(noxu_util::packed::write_packed_i64(w, val)?)
}
pub fn read_packed_i64(r: &mut impl Read) -> Result<i64> {
Ok(noxu_util::packed::read_packed_i64(r)?)
}
pub fn packed_i64_size(val: i64) -> usize {
noxu_util::packed::packed_i64_size(val)
}
pub fn write_byte_array(
w: &mut impl Write,
data: Option<&[u8]>,
) -> Result<usize> {
match data {
None => write_packed_i32(w, -1),
Some(bytes) => {
let len = bytes.len() as i32;
let size1 = write_packed_i32(w, len)?;
w.write_all(bytes)?;
Ok(size1 + bytes.len())
}
}
}
pub fn read_byte_array(r: &mut impl Read) -> Result<Option<Vec<u8>>> {
let len = read_packed_i32(r)?;
if len < 0 {
return Ok(None);
}
if len == 0 {
return Ok(Some(Vec::new()));
}
let mut buf = vec![0u8; len as usize];
r.read_exact(&mut buf)?;
Ok(Some(buf))
}
pub fn byte_array_size(data: Option<&[u8]>) -> usize {
match data {
None => packed_i32_size(-1),
Some(bytes) => packed_i32_size(bytes.len() as i32) + bytes.len(),
}
}
pub fn write_bytes_no_length(w: &mut impl Write, data: &[u8]) -> Result<()> {
Ok(w.write_all(data)?)
}
pub fn read_bytes_no_length(r: &mut impl Read, len: usize) -> Result<Vec<u8>> {
let mut buf = vec![0u8; len];
r.read_exact(&mut buf)?;
Ok(buf)
}
pub fn write_string(w: &mut impl Write, s: Option<&str>) -> Result<usize> {
match s {
None => write_byte_array(w, None),
Some(text) => write_byte_array(w, Some(text.as_bytes())),
}
}
pub fn read_string(r: &mut impl Read) -> Result<Option<String>> {
match read_byte_array(r)? {
None => Ok(None),
Some(bytes) => {
let s = String::from_utf8(bytes).map_err(|e| {
std::io::Error::new(std::io::ErrorKind::InvalidData, e)
})?;
Ok(Some(s))
}
}
}
pub fn string_size(s: Option<&str>) -> usize {
match s {
None => packed_i32_size(-1),
Some(text) => byte_array_size(Some(text.as_bytes())),
}
}
pub fn write_bool(w: &mut impl Write, val: bool) -> Result<()> {
Ok(w.write_u8(if val { 1 } else { 0 })?)
}
pub fn read_bool(r: &mut impl Read) -> Result<bool> {
Ok(r.read_u8()? != 0)
}
pub const fn bool_size() -> usize {
1
}
pub fn write_timestamp(w: &mut impl Write, millis: i64) -> Result<usize> {
write_packed_i64(w, millis)
}
pub fn read_timestamp(r: &mut impl Read) -> Result<i64> {
read_packed_i64(r)
}
pub fn timestamp_size(millis: i64) -> usize {
packed_i64_size(millis)
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
#[test]
fn test_i32_roundtrip() {
let values = [0, 1, -1, i32::MAX, i32::MIN, 12345, -67890];
for &val in &values {
let mut buf = Vec::new();
write_i32(&mut buf, val).unwrap();
assert_eq!(buf.len(), INT_BYTES);
let result = read_i32(&mut Cursor::new(&buf)).unwrap();
assert_eq!(val, result);
}
}
#[test]
fn test_packed_i32_roundtrip() {
let values = [0, 1, -1, 100, -100, 1000, -1000, i32::MAX, i32::MIN];
for &val in &values {
let mut buf = Vec::new();
let size = write_packed_i32(&mut buf, val).unwrap();
assert_eq!(size, buf.len());
assert_eq!(size, packed_i32_size(val));
let result = read_packed_i32(&mut Cursor::new(&buf)).unwrap();
assert_eq!(val, result);
}
}
#[test]
fn test_byte_array_roundtrip() {
let data = b"Hello, Noxu!";
let mut buf = Vec::new();
write_byte_array(&mut buf, Some(data)).unwrap();
let result = read_byte_array(&mut Cursor::new(&buf)).unwrap();
assert_eq!(result, Some(data.to_vec()));
let mut buf = Vec::new();
write_byte_array(&mut buf, Some(&[])).unwrap();
let result = read_byte_array(&mut Cursor::new(&buf)).unwrap();
assert_eq!(result, Some(Vec::new()));
let mut buf = Vec::new();
write_byte_array(&mut buf, None).unwrap();
let result = read_byte_array(&mut Cursor::new(&buf)).unwrap();
assert_eq!(result, None);
}
#[test]
fn test_string_roundtrip() {
let test_cases =
[Some("Hello, World!"), Some(""), Some("UTF-8: (ok) (crab)"), None];
for &text in &test_cases {
let mut buf = Vec::new();
write_string(&mut buf, text).unwrap();
let result = read_string(&mut Cursor::new(&buf)).unwrap();
assert_eq!(result.as_deref(), text);
}
}
#[test]
fn test_bool_roundtrip() {
for &val in &[true, false] {
let mut buf = Vec::new();
write_bool(&mut buf, val).unwrap();
assert_eq!(buf.len(), 1);
let result = read_bool(&mut Cursor::new(&buf)).unwrap();
assert_eq!(val, result);
}
}
#[test]
fn test_timestamp() {
let now = 1234567890123i64;
let mut buf = Vec::new();
write_timestamp(&mut buf, now).unwrap();
let result = read_timestamp(&mut Cursor::new(&buf)).unwrap();
assert_eq!(now, result);
}
#[test]
fn test_i16_roundtrip() {
let values: &[i16] = &[0, 1, -1, i16::MAX, i16::MIN, 1000, -1000];
for &val in values {
let mut buf = Vec::new();
write_i16(&mut buf, val).unwrap();
assert_eq!(buf.len(), SHORT_BYTES);
let result = read_i16(&mut Cursor::new(&buf)).unwrap();
assert_eq!(val, result);
}
}
#[test]
fn test_i64_roundtrip() {
let values: &[i64] =
&[0, 1, -1, i64::MAX, i64::MIN, 1_234_567_890_123, -9_876_543_210];
for &val in values {
let mut buf = Vec::new();
write_i64(&mut buf, val).unwrap();
assert_eq!(buf.len(), LONG_BYTES);
let result = read_i64(&mut Cursor::new(&buf)).unwrap();
assert_eq!(val, result);
}
}
#[test]
fn test_u32_roundtrip() {
let values: &[u32] = &[0, 1, u32::MAX, 0x1234_5678, 999_999];
for &val in values {
let mut buf = Vec::new();
write_u32(&mut buf, val).unwrap();
assert_eq!(buf.len(), UNSIGNED_INT_BYTES);
let result = read_u32(&mut Cursor::new(&buf)).unwrap();
assert_eq!(val, result);
}
}
#[test]
fn test_packed_i32_size_tiers() {
let cases: &[(i32, usize)] = &[
(0, 1),
(119, 1),
(-119, 1),
(120, 2),
(-120, 2),
(i32::MAX, 5),
(i32::MIN, 5),
];
for &(val, expected_size) in cases {
assert_eq!(
packed_i32_size(val),
expected_size,
"packed_i32_size({}) expected {}",
val,
expected_size
);
let mut buf = Vec::new();
let written = write_packed_i32(&mut buf, val).unwrap();
assert_eq!(written, expected_size);
assert_eq!(buf.len(), expected_size);
let decoded = read_packed_i32(&mut Cursor::new(&buf)).unwrap();
assert_eq!(decoded, val);
}
}
#[test]
fn test_packed_i64_roundtrip() {
let values: &[i64] = &[
0,
1,
-1,
119,
-119,
120,
-120,
i64::MAX,
i64::MIN,
1_000_000_000_000,
];
for &val in values {
let mut buf = Vec::new();
let written = write_packed_i64(&mut buf, val).unwrap();
assert_eq!(written, packed_i64_size(val));
assert_eq!(buf.len(), packed_i64_size(val));
let decoded = read_packed_i64(&mut Cursor::new(&buf)).unwrap();
assert_eq!(decoded, val, "packed i64 roundtrip failed for {}", val);
}
}
#[test]
fn test_byte_array_large() {
let data: Vec<u8> = (0u8..=255).cycle().take(1024).collect();
let mut buf = Vec::new();
write_byte_array(&mut buf, Some(&data)).unwrap();
let result = read_byte_array(&mut Cursor::new(&buf)).unwrap();
assert_eq!(result.unwrap(), data);
}
#[test]
fn test_string_empty_and_null() {
let mut buf_empty = Vec::new();
write_string(&mut buf_empty, Some("")).unwrap();
let result_empty = read_string(&mut Cursor::new(&buf_empty)).unwrap();
assert_eq!(result_empty, Some(String::new()));
let mut buf_null = Vec::new();
write_string(&mut buf_null, None).unwrap();
let result_null = read_string(&mut Cursor::new(&buf_null)).unwrap();
assert_eq!(result_null, None);
assert_ne!(buf_empty, buf_null);
}
#[test]
fn test_write_read_bytes_no_length() {
let data = b"raw bytes no length";
let mut buf = Vec::new();
write_bytes_no_length(&mut buf, data).unwrap();
assert_eq!(buf, data);
let read_back =
read_bytes_no_length(&mut Cursor::new(&buf), data.len()).unwrap();
assert_eq!(read_back, data);
}
#[test]
fn test_bool_both_values() {
for val in [true, false] {
let mut buf = Vec::new();
write_bool(&mut buf, val).unwrap();
assert_eq!(buf.len(), bool_size());
let decoded = read_bool(&mut Cursor::new(&buf)).unwrap();
assert_eq!(decoded, val);
}
}
}