use crate::Error;
pub struct AvroCursor<'a> {
data: &'a [u8],
pos: usize,
}
impl<'a> AvroCursor<'a> {
#[inline]
pub fn new(data: &'a [u8]) -> Self {
Self { data, pos: 0 }
}
#[inline]
#[allow(dead_code)]
pub fn position(&self) -> usize {
self.pos
}
#[inline]
pub fn remaining(&self) -> usize {
self.data.len() - self.pos
}
#[inline]
fn require(&self, n: usize) -> crate::Result<()> {
if self.pos + n > self.data.len() {
return Err(Error::UnexpectedError {
message: format!(
"avro cursor: need {} bytes at offset {}, but only {} remain",
n,
self.pos,
self.remaining()
),
source: None,
});
}
Ok(())
}
#[inline]
pub fn read_long(&mut self) -> crate::Result<i64> {
let raw = if self.data.len() - self.pos >= 10 {
self.read_varint_fast()
} else {
self.read_varint_slow()
}?;
Ok(((raw >> 1) as i64) ^ -((raw & 1) as i64))
}
#[inline]
fn read_varint_fast(&mut self) -> crate::Result<u64> {
let buf = &self.data[self.pos..];
let mut raw: u64 = 0;
let mut shift: u32 = 0;
let mut i = 0;
loop {
let b = buf[i] as u64;
i += 1;
raw |= (b & 0x7F) << shift;
if b & 0x80 == 0 {
self.pos += i;
return Ok(raw);
}
shift += 7;
if shift >= 64 {
return Err(Error::UnexpectedError {
message: "avro cursor: varint overflow".into(),
source: None,
});
}
}
}
#[inline]
fn read_varint_slow(&mut self) -> crate::Result<u64> {
let mut raw: u64 = 0;
let mut shift: u32 = 0;
loop {
self.require(1)?;
let b = self.data[self.pos] as u64;
self.pos += 1;
raw |= (b & 0x7F) << shift;
if b & 0x80 == 0 {
return Ok(raw);
}
shift += 7;
if shift >= 64 {
return Err(Error::UnexpectedError {
message: "avro cursor: varint overflow".into(),
source: None,
});
}
}
}
#[inline]
pub fn read_int(&mut self) -> crate::Result<i32> {
let raw = if self.data.len() - self.pos >= 5 {
self.read_varint_int_fast()
} else {
self.read_varint_slow()
}?;
let zigzag = ((raw >> 1) as i64) ^ -((raw & 1) as i64);
Ok(zigzag as i32)
}
#[inline]
fn read_varint_int_fast(&mut self) -> crate::Result<u64> {
let buf = &self.data[self.pos..];
let mut raw: u64 = 0;
let mut shift: u32 = 0;
let mut i = 0;
loop {
let b = buf[i] as u64;
i += 1;
raw |= (b & 0x7F) << shift;
if b & 0x80 == 0 {
self.pos += i;
return Ok(raw);
}
shift += 7;
if shift >= 35 {
return Err(Error::UnexpectedError {
message: "avro cursor: int varint overflow".into(),
source: None,
});
}
}
}
#[inline]
#[allow(dead_code)]
pub fn read_boolean(&mut self) -> crate::Result<bool> {
self.require(1)?;
let b = self.data[self.pos];
self.pos += 1;
Ok(b != 0)
}
#[inline]
#[allow(dead_code)]
pub fn read_float(&mut self) -> crate::Result<f32> {
self.require(4)?;
let bytes: [u8; 4] = self.data[self.pos..self.pos + 4].try_into().unwrap();
self.pos += 4;
Ok(f32::from_le_bytes(bytes))
}
#[inline]
#[allow(dead_code)]
pub fn read_double(&mut self) -> crate::Result<f64> {
self.require(8)?;
let bytes: [u8; 8] = self.data[self.pos..self.pos + 8].try_into().unwrap();
self.pos += 8;
Ok(f64::from_le_bytes(bytes))
}
#[inline]
pub fn read_bytes(&mut self) -> crate::Result<&'a [u8]> {
let raw_len = self.read_long()?;
if raw_len < 0 {
return Err(Error::UnexpectedError {
message: format!("avro cursor: negative bytes length: {raw_len}"),
source: None,
});
}
let len = raw_len as usize;
self.require(len)?;
let slice = &self.data[self.pos..self.pos + len];
self.pos += len;
Ok(slice)
}
#[inline]
pub fn read_string(&mut self) -> crate::Result<&'a str> {
let bytes = self.read_bytes()?;
std::str::from_utf8(bytes).map_err(|e| Error::UnexpectedError {
message: format!("avro cursor: invalid UTF-8 string: {e}"),
source: None,
})
}
#[inline]
pub fn read_union_index(&mut self) -> crate::Result<i64> {
self.read_long()
}
#[inline]
pub fn skip_raw(&mut self, n: usize) -> crate::Result<()> {
self.require(n)?;
self.pos += n;
Ok(())
}
#[inline]
pub fn skip_bytes(&mut self) -> crate::Result<()> {
let raw_len = self.read_long()?;
if raw_len < 0 {
return Err(Error::UnexpectedError {
message: format!("avro cursor: negative bytes length: {raw_len}"),
source: None,
});
}
self.skip_raw(raw_len as usize)
}
#[inline]
pub fn skip_long(&mut self) -> crate::Result<()> {
self.read_long().map(|_| ())
}
#[inline]
pub fn read_fixed(&mut self, len: usize) -> crate::Result<&'a [u8]> {
self.require(len)?;
let slice = &self.data[self.pos..self.pos + len];
self.pos += len;
Ok(slice)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn zigzag_encode(n: i64) -> Vec<u8> {
let mut encoded: u64 = ((n << 1) ^ (n >> 63)) as u64;
let mut buf = Vec::new();
loop {
if encoded & !0x7F == 0 {
buf.push(encoded as u8);
break;
}
buf.push((encoded & 0x7F | 0x80) as u8);
encoded >>= 7;
}
buf
}
#[test]
fn test_read_long_values() {
for val in [0i64, 1, -1, 42, -42, 127, -128, i64::MAX, i64::MIN] {
let bytes = zigzag_encode(val);
let mut cursor = AvroCursor::new(&bytes);
assert_eq!(cursor.read_long().unwrap(), val, "failed for {val}");
assert_eq!(cursor.remaining(), 0);
}
}
#[test]
fn test_read_int() {
let bytes = zigzag_encode(100);
let mut cursor = AvroCursor::new(&bytes);
assert_eq!(cursor.read_int().unwrap(), 100);
}
#[test]
fn test_read_boolean() {
let mut cursor = AvroCursor::new(&[0, 1]);
assert!(!cursor.read_boolean().unwrap());
assert!(cursor.read_boolean().unwrap());
}
#[test]
fn test_read_float_double() {
let f_bytes = std::f32::consts::PI.to_le_bytes();
let d_bytes = std::f64::consts::E.to_le_bytes();
let mut data = Vec::new();
data.extend_from_slice(&f_bytes);
data.extend_from_slice(&d_bytes);
let mut cursor = AvroCursor::new(&data);
assert!((cursor.read_float().unwrap() - std::f32::consts::PI).abs() < 1e-6);
assert!((cursor.read_double().unwrap() - std::f64::consts::E).abs() < 1e-10);
}
#[test]
fn test_read_bytes_and_string() {
let mut data = Vec::new();
data.extend_from_slice(&zigzag_encode(5));
data.extend_from_slice(b"hello");
data.extend_from_slice(&zigzag_encode(5));
data.extend_from_slice(b"world");
let mut cursor = AvroCursor::new(&data);
assert_eq!(cursor.read_bytes().unwrap(), b"hello");
assert_eq!(cursor.read_string().unwrap(), "world");
}
#[test]
fn test_skip() {
let mut data = Vec::new();
data.extend_from_slice(&zigzag_encode(3));
data.extend_from_slice(b"abc");
data.extend_from_slice(&zigzag_encode(99));
let mut cursor = AvroCursor::new(&data);
cursor.skip_bytes().unwrap();
assert_eq!(cursor.read_int().unwrap(), 99);
}
#[test]
fn test_eof_error() {
let mut cursor = AvroCursor::new(&[]);
assert!(cursor.read_long().is_err());
}
}