pub mod cursor;
pub mod decode;
pub(crate) mod decode_helpers;
mod index_manifest_entry_decode;
pub(crate) mod manifest_entry_decode;
mod manifest_file_meta_decode;
pub mod ocf;
pub mod schema;
use cursor::AvroCursor;
use decode::AvroRecordDecode;
use ocf::parse_ocf_streaming;
use schema::WriterSchema;
use std::sync::{Arc, RwLock};
pub struct SchemaCache {
cache: Vec<(String, Arc<WriterSchema>)>,
}
impl SchemaCache {
pub fn new() -> Self {
Self { cache: Vec::new() }
}
pub fn get_or_parse(&mut self, schema_json: &str) -> crate::Result<Arc<WriterSchema>> {
if let Some(cached) = self.cache.iter().find(|(k, _)| k == schema_json) {
return Ok(Arc::clone(&cached.1));
}
let ws = Arc::new(WriterSchema::parse(schema_json)?);
self.cache.push((schema_json.to_string(), Arc::clone(&ws)));
Ok(ws)
}
}
impl Default for SchemaCache {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone)]
pub struct SharedSchemaCache {
inner: Arc<RwLock<SchemaCache>>,
}
impl SharedSchemaCache {
pub fn new() -> Self {
Self {
inner: Arc::new(RwLock::new(SchemaCache::new())),
}
}
pub fn get_or_parse(&self, schema_json: &str) -> crate::Result<Arc<WriterSchema>> {
{
let cache = self.inner.read().unwrap_or_else(|e| e.into_inner());
if let Some(cached) = cache.cache.iter().find(|(k, _)| k == schema_json) {
return Ok(Arc::clone(&cached.1));
}
}
self.inner
.write()
.unwrap_or_else(|e| e.into_inner())
.get_or_parse(schema_json)
}
}
impl Default for SharedSchemaCache {
fn default() -> Self {
Self::new()
}
}
pub fn from_avro_bytes_fast<T: AvroRecordDecode>(bytes: &[u8]) -> crate::Result<Vec<T>> {
let mut cache = SchemaCache::new();
from_avro_bytes_with_cache(bytes, &mut cache)
}
pub fn from_avro_bytes_with_cache<T: AvroRecordDecode>(
bytes: &[u8],
cache: &mut SchemaCache,
) -> crate::Result<Vec<T>> {
let (header, mut block_iter) = parse_ocf_streaming(bytes)?;
let writer_schema = cache.get_or_parse(&header.schema_json)?;
let mut results = Vec::new();
while let Some(block) = block_iter.next_block()? {
results.reserve(block.object_count);
let mut cursor = AvroCursor::new(&block.data);
for _ in 0..block.object_count {
let record = decode_top_level_record::<T>(&mut cursor, &writer_schema)?;
results.push(record);
}
}
Ok(results)
}
pub fn from_manifest_bytes_filtered<F>(
bytes: &[u8],
cache: &mut SchemaCache,
filter: &mut F,
) -> crate::Result<Vec<crate::spec::ManifestEntry>>
where
F: FnMut(crate::spec::FileKind, &[u8], i32, i32) -> bool,
{
let (header, mut block_iter) = parse_ocf_streaming(bytes)?;
let writer_schema = cache.get_or_parse(&header.schema_json)?;
decode_manifest_streaming(&mut block_iter, &writer_schema, filter)
}
pub fn from_manifest_bytes_filtered_shared<F>(
bytes: &[u8],
shared_cache: &SharedSchemaCache,
filter: &mut F,
) -> crate::Result<Vec<crate::spec::ManifestEntry>>
where
F: FnMut(crate::spec::FileKind, &[u8], i32, i32) -> bool,
{
let (header, mut block_iter) = parse_ocf_streaming(bytes)?;
let writer_schema = shared_cache.get_or_parse(&header.schema_json)?;
decode_manifest_streaming(&mut block_iter, &writer_schema, filter)
}
fn decode_manifest_streaming<F>(
block_iter: &mut ocf::OcfBlockIter<'_>,
writer_schema: &WriterSchema,
filter: &mut F,
) -> crate::Result<Vec<crate::spec::ManifestEntry>>
where
F: FnMut(crate::spec::FileKind, &[u8], i32, i32) -> bool,
{
let mut results = Vec::new();
while let Some(block) = block_iter.next_block()? {
results.reserve(block.object_count);
let mut cursor = AvroCursor::new(&block.data);
for _ in 0..block.object_count {
if let Some(entry) = manifest_entry_decode::decode_manifest_entries_filtered(
&mut cursor,
writer_schema,
writer_schema.is_union_wrapped,
filter,
)? {
results.push(entry);
}
}
}
Ok(results)
}
fn decode_top_level_record<T: AvroRecordDecode>(
cursor: &mut AvroCursor,
writer_schema: &WriterSchema,
) -> crate::Result<T> {
if writer_schema.is_union_wrapped {
let idx = cursor.read_union_index()?;
if idx == 0 {
return Err(crate::Error::UnexpectedError {
message: "avro decode: unexpected null in top-level union".into(),
source: None,
});
}
}
T::decode(cursor, writer_schema)
}