use std::env;
use std::sync::LazyLock;
use flatbuffers::FlatBufferBuilder;
use flatbuffers::VerifierOptions;
use flatbuffers::WIPOffset;
use flatbuffers::root_with_opts;
use vortex_array::dtype::DType;
use vortex_error::VortexResult;
use vortex_error::vortex_err;
use vortex_flatbuffers::FlatBuffer;
use vortex_flatbuffers::FlatBufferRoot;
use vortex_flatbuffers::WriteFlatBuffer;
use vortex_flatbuffers::layout;
use vortex_session::registry::ReadContext;
use crate::Layout;
use crate::LayoutContext;
use crate::LayoutRef;
use crate::children::ViewedLayoutChildren;
use crate::segments::SegmentId;
use crate::session::LayoutRegistry;
static LAYOUT_VERIFIER: LazyLock<VerifierOptions> = LazyLock::new(|| {
VerifierOptions {
max_tables: env::var("VORTEX_MAX_LAYOUT_TABLES")
.ok()
.and_then(|lmt| lmt.parse::<usize>().ok())
.unwrap_or(1000000),
max_depth: env::var("VORTEX_MAX_LAYOUT_DEPTH")
.ok()
.and_then(|lmt| lmt.parse::<usize>().ok())
.unwrap_or(64),
max_apparent_size: 1 << 31,
ignore_missing_null_terminator: false,
}
});
pub fn layout_from_flatbuffer(
flatbuffer: FlatBuffer,
dtype: &DType,
layout_ctx: &ReadContext,
ctx: &ReadContext,
layouts: &LayoutRegistry,
) -> VortexResult<LayoutRef> {
let fb_layout = root_with_opts::<layout::Layout>(&LAYOUT_VERIFIER, &flatbuffer)?;
let encoding_id = layout_ctx
.resolve(fb_layout.encoding())
.ok_or_else(|| vortex_err!("Invalid encoding ID: {}", fb_layout.encoding()))?;
let encoding = layouts
.find(&encoding_id)
.ok_or_else(|| vortex_err!("Invalid encoding ID: {}", fb_layout.encoding()))?;
let viewed_children = unsafe {
ViewedLayoutChildren::new_unchecked(
flatbuffer.clone(),
fb_layout._tab.loc(),
ctx.clone(),
layout_ctx.clone(),
layouts.clone(),
)
};
let layout = encoding.build(
dtype,
fb_layout.row_count(),
fb_layout
.metadata()
.map(|m| m.bytes())
.unwrap_or_else(|| &[]),
fb_layout
.segments()
.unwrap_or_default()
.iter()
.map(SegmentId::from)
.collect(),
&viewed_children,
ctx,
)?;
Ok(layout)
}
impl dyn Layout + '_ {
pub fn flatbuffer_writer<'a>(
&'a self,
ctx: &'a LayoutContext,
) -> impl WriteFlatBuffer<Target<'a> = layout::Layout<'a>> + FlatBufferRoot + 'a {
LayoutFlatBufferWriter { layout: self, ctx }
}
}
struct LayoutFlatBufferWriter<'a> {
layout: &'a dyn Layout,
ctx: &'a LayoutContext,
}
impl FlatBufferRoot for LayoutFlatBufferWriter<'_> {}
impl WriteFlatBuffer for LayoutFlatBufferWriter<'_> {
type Target<'fb> = layout::Layout<'fb>;
fn write_flatbuffer<'fb>(
&self,
fbb: &mut FlatBufferBuilder<'fb>,
) -> VortexResult<WIPOffset<Self::Target<'fb>>> {
let child_layouts = self.layout.children()?;
let children = child_layouts
.iter()
.map(|layout| {
LayoutFlatBufferWriter {
layout: layout.as_ref(),
ctx: self.ctx,
}
.write_flatbuffer(fbb)
})
.collect::<VortexResult<Vec<_>>>()?;
let children = (!children.is_empty()).then(|| fbb.create_vector(&children));
let metadata = self.layout.metadata();
let metadata = (!metadata.is_empty()).then(|| fbb.create_vector(&metadata));
let segments = self
.layout
.segment_ids()
.into_iter()
.map(|s| *s)
.collect::<Vec<_>>();
let segments = (!segments.is_empty()).then(|| fbb.create_vector(&segments));
let encoding = self.ctx.intern(&self.layout.encoding_id()).ok_or_else(|| {
vortex_err!(
"Failed to intern layout encoding ID: {}",
self.layout.encoding_id()
)
})?;
Ok(layout::Layout::create(
fbb,
&layout::LayoutArgs {
encoding,
row_count: self.layout.row_count(),
metadata,
children,
segments,
},
))
}
}