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> {
layout_from_flatbuffer_with_options(flatbuffer, dtype, layout_ctx, ctx, layouts, false)
}
pub fn layout_from_flatbuffer_with_options(
flatbuffer: FlatBuffer,
dtype: &DType,
layout_ctx: &ReadContext,
ctx: &ReadContext,
layouts: &LayoutRegistry,
allow_unknown: bool,
) -> 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);
if encoding.is_none() && allow_unknown {
return foreign_layout_from_fb(fb_layout, dtype, layout_ctx);
}
let encoding =
encoding.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(),
allow_unknown,
)
};
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)
}
fn foreign_layout_from_fb(
fb_layout: layout::Layout<'_>,
dtype: &DType,
layout_ctx: &ReadContext,
) -> VortexResult<LayoutRef> {
let encoding_id = layout_ctx
.resolve(fb_layout.encoding())
.ok_or_else(|| vortex_err!("Invalid encoding ID: {}", fb_layout.encoding()))?;
let children = fb_layout
.children()
.unwrap_or_default()
.iter()
.map(|child| foreign_layout_from_fb(child, dtype, layout_ctx))
.collect::<VortexResult<Vec<_>>>()?;
Ok(crate::layouts::foreign::new_foreign_layout(
encoding_id,
dtype.clone(),
fb_layout.row_count(),
fb_layout
.metadata()
.map(|m| m.bytes().to_vec())
.unwrap_or_default(),
fb_layout
.segments()
.unwrap_or_default()
.iter()
.map(SegmentId::from)
.collect(),
children,
))
}
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,
},
))
}
}
#[cfg(test)]
mod tests {
use flatbuffers::FlatBufferBuilder;
use vortex_array::dtype::DType;
use vortex_array::dtype::Nullability;
use vortex_flatbuffers::layout as fbl;
use vortex_session::registry::ReadContext;
use super::layout_from_flatbuffer_with_options;
use crate::LayoutEncodingId;
use crate::session::LayoutSession;
#[test]
fn unknown_layout_encoding_allow_unknown() {
let mut fbb = FlatBufferBuilder::new();
let child_metadata = fbb.create_vector(&[9u8]);
let child = fbl::Layout::create(
&mut fbb,
&fbl::LayoutArgs {
encoding: 1,
row_count: 3,
metadata: Some(child_metadata),
children: None,
segments: None,
},
);
let children = fbb.create_vector(&[child]);
let metadata = fbb.create_vector(&[1u8, 2, 3]);
let segments = fbb.create_vector(&[7u32]);
let root = fbl::Layout::create(
&mut fbb,
&fbl::LayoutArgs {
encoding: 0,
row_count: 10,
metadata: Some(metadata),
children: Some(children),
segments: Some(segments),
},
);
fbb.finish_minimal(root);
let (buf, start) = fbb.collapse();
let layout_buffer = vortex_flatbuffers::FlatBuffer::align_from(
vortex_buffer::ByteBuffer::from(buf).slice(start..),
);
let layout_ctx = ReadContext::new([
LayoutEncodingId::new_ref("vortex.test.foreign_layout"),
LayoutEncodingId::new_ref("vortex.test.foreign_child_layout"),
]);
let array_ctx = ReadContext::new([]);
let layouts = LayoutSession::default().registry().clone();
let layout = layout_from_flatbuffer_with_options(
layout_buffer,
&DType::Variant(Nullability::Nullable),
&layout_ctx,
&array_ctx,
&layouts,
true,
)
.unwrap();
assert_eq!(layout.encoding_id().as_ref(), "vortex.test.foreign_layout");
assert_eq!(layout.row_count(), 10);
assert_eq!(layout.metadata(), vec![1, 2, 3]);
assert_eq!(layout.segment_ids().len(), 1);
assert_eq!(*layout.segment_ids()[0], 7);
assert_eq!(layout.nchildren(), 1);
let child = layout.child(0).unwrap();
assert_eq!(
child.encoding_id().as_ref(),
"vortex.test.foreign_child_layout"
);
assert_eq!(child.metadata(), vec![9]);
}
}