Skip to content

Commit 6181d44

Browse files
authored
Add Page message to Vortex IPC (#333)
A page holds arbitrary bytes. This can be used by consumers to add custom data into a Vortex stream / file.
1 parent f515492 commit 6181d44

File tree

6 files changed

+89
-12
lines changed

6 files changed

+89
-12
lines changed

vortex-ipc/flatbuffers/message.fbs

+6
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,16 @@ table Chunk {
3535
buffer_size: uint64;
3636
}
3737

38+
table Page {
39+
buffer_size: uint32;
40+
padding: uint16;
41+
}
42+
3843
union MessageHeader {
3944
Context,
4045
Schema,
4146
Chunk,
47+
Page,
4248
}
4349

4450
table Message {

vortex-ipc/src/lib.rs

+7-8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ extern crate core;
33
pub use message_reader::*;
44
pub use message_writer::*;
55
use vortex_error::{vortex_err, VortexError};
6+
pub mod chunked_reader;
7+
pub mod io;
8+
mod message_reader;
9+
mod message_writer;
10+
mod messages;
11+
pub mod stream_reader;
12+
pub mod writer;
613

714
pub const ALIGNMENT: usize = 64;
815

@@ -33,14 +40,6 @@ pub mod flatbuffers {
3340
}
3441
}
3542

36-
pub mod chunked_reader;
37-
pub mod io;
38-
mod message_reader;
39-
mod message_writer;
40-
mod messages;
41-
pub mod stream_reader;
42-
pub mod writer;
43-
4443
pub(crate) const fn missing(field: &'static str) -> impl FnOnce() -> VortexError {
4544
move || vortex_err!(InvalidSerde: "missing field: {}", field)
4645
}

vortex-ipc/src/message_reader.rs

+17
Original file line numberDiff line numberDiff line change
@@ -271,4 +271,21 @@ impl<R: VortexRead> MessageReader<R> {
271271
}),
272272
)
273273
}
274+
275+
pub async fn maybe_read_page(&mut self) -> VortexResult<Option<Buffer>> {
276+
if self.peek().and_then(|m| m.header_as_page()).is_none() {
277+
return Ok(None);
278+
}
279+
let page_msg = self.next().await?.header_as_page().unwrap();
280+
281+
let buffer_len = page_msg.buffer_size() as usize;
282+
let total_len = buffer_len + (page_msg.padding() as usize);
283+
284+
let mut buffer = self
285+
.read
286+
.read_into(BytesMut::with_capacity(total_len))
287+
.await?;
288+
buffer.truncate(buffer_len);
289+
Ok(Some(Buffer::from(buffer.freeze())))
290+
}
274291
}

vortex-ipc/src/message_writer.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ use flatbuffers::FlatBufferBuilder;
44
use itertools::Itertools;
55
use vortex::{ArrayData, ViewContext};
66
use vortex_buffer::io_buf::IoBuf;
7+
use vortex_buffer::Buffer;
78
use vortex_dtype::DType;
89
use vortex_flatbuffers::WriteFlatBuffer;
910

1011
use crate::io::VortexWrite;
11-
use crate::messages::{IPCChunk, IPCContext, IPCMessage, IPCSchema};
12+
use crate::messages::{IPCChunk, IPCContext, IPCMessage, IPCPage, IPCSchema};
1213
use crate::ALIGNMENT;
1314

1415
const ZEROS: [u8; 512] = [0u8; 512];
@@ -81,6 +82,19 @@ impl<W: VortexWrite> MessageWriter<W> {
8182
Ok(())
8283
}
8384

85+
pub async fn write_page(&mut self, buffer: Buffer) -> io::Result<()> {
86+
self.write_message(IPCMessage::Page(IPCPage(&buffer)))
87+
.await?;
88+
let buffer_len = buffer.len();
89+
self.write_all(buffer).await?;
90+
91+
let aligned_size = (buffer_len + (self.alignment - 1)) & !(self.alignment - 1);
92+
let padding = aligned_size - buffer_len;
93+
self.write_all(&ZEROS[0..padding]).await?;
94+
95+
Ok(())
96+
}
97+
8498
async fn write_message<F: WriteFlatBuffer>(&mut self, flatbuffer: F) -> io::Result<()> {
8599
// We reuse the scratch buffer each time and then replace it at the end.
86100
// The scratch buffer may be missing if a previous write failed. We could use scopeguard

vortex-ipc/src/messages.rs

+26-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use flatbuffers::{FlatBufferBuilder, WIPOffset};
22
use itertools::Itertools;
33
use vortex::flatbuffers as fba;
44
use vortex::{ArrayData, Context, ViewContext};
5+
use vortex_buffer::Buffer;
56
use vortex_dtype::DType;
67
use vortex_error::{vortex_err, VortexError};
78
use vortex_flatbuffers::{FlatBufferRoot, WriteFlatBuffer};
@@ -14,15 +15,14 @@ pub enum IPCMessage<'a> {
1415
Context(IPCContext<'a>),
1516
Schema(IPCSchema<'a>),
1617
Chunk(IPCChunk<'a>),
18+
Page(IPCPage<'a>),
1719
}
1820

1921
pub struct IPCContext<'a>(pub &'a ViewContext);
20-
2122
pub struct IPCSchema<'a>(pub &'a DType);
22-
2323
pub struct IPCChunk<'a>(pub &'a ViewContext, pub &'a ArrayData);
24-
2524
pub struct IPCArray<'a>(pub &'a ViewContext, pub &'a ArrayData);
25+
pub struct IPCPage<'a>(pub &'a Buffer);
2626

2727
impl FlatBufferRoot for IPCMessage<'_> {}
2828

@@ -37,6 +37,7 @@ impl WriteFlatBuffer for IPCMessage<'_> {
3737
Self::Context(f) => f.write_flatbuffer(fbb).as_union_value(),
3838
Self::Schema(f) => f.write_flatbuffer(fbb).as_union_value(),
3939
Self::Chunk(f) => f.write_flatbuffer(fbb).as_union_value(),
40+
Self::Page(f) => f.write_flatbuffer(fbb).as_union_value(),
4041
};
4142

4243
let mut msg = fb::MessageBuilder::new(fbb);
@@ -45,6 +46,7 @@ impl WriteFlatBuffer for IPCMessage<'_> {
4546
Self::Context(_) => fb::MessageHeader::Context,
4647
Self::Schema(_) => fb::MessageHeader::Schema,
4748
Self::Chunk(_) => fb::MessageHeader::Chunk,
49+
Self::Page(_) => fb::MessageHeader::Page,
4850
});
4951
msg.add_header(header);
5052
msg.finish()
@@ -205,3 +207,24 @@ impl<'a> WriteFlatBuffer for IPCArray<'a> {
205207
)
206208
}
207209
}
210+
211+
impl<'a> WriteFlatBuffer for IPCPage<'a> {
212+
type Target<'t> = fb::Page<'t>;
213+
214+
fn write_flatbuffer<'fb>(
215+
&self,
216+
fbb: &mut FlatBufferBuilder<'fb>,
217+
) -> WIPOffset<Self::Target<'fb>> {
218+
let buffer_size = self.0.len();
219+
let aligned_size = (buffer_size + (ALIGNMENT - 1)) & !(ALIGNMENT - 1);
220+
let padding_size = aligned_size - buffer_size;
221+
222+
fb::Page::create(
223+
fbb,
224+
&fb::PageArgs {
225+
buffer_size: buffer_size as u32,
226+
padding: padding_size as u16,
227+
},
228+
)
229+
}
230+
}

vortex-ipc/src/stream_reader/mod.rs

+18
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
use std::ops::Deref;
22
use std::sync::Arc;
33

4+
use futures_util::stream::try_unfold;
5+
use futures_util::Stream;
46
use vortex::stream::ArrayStream;
57
use vortex::{Context, ViewContext};
8+
use vortex_buffer::Buffer;
69
use vortex_dtype::DType;
710
use vortex_error::VortexResult;
811

@@ -64,4 +67,19 @@ impl<R: VortexRead> StreamArrayReader<R> {
6467
let dtype = self.dtype.as_ref().expect("DType not set").deref().clone();
6568
self.msgs.array_stream(view_context, dtype)
6669
}
70+
71+
/// Reads a single page from the stream.
72+
pub async fn next_page(&mut self) -> VortexResult<Option<Buffer>> {
73+
self.msgs.maybe_read_page().await
74+
}
75+
76+
/// Reads consecutive pages from the stream until the message type changes.
77+
pub async fn page_stream(&mut self) -> impl Stream<Item = VortexResult<Buffer>> + '_ {
78+
try_unfold(self, |reader| async {
79+
match reader.next_page().await? {
80+
Some(page) => Ok(Some((page, reader))),
81+
None => Ok(None),
82+
}
83+
})
84+
}
6785
}

0 commit comments

Comments
 (0)