wp_connector_utils/arrow/
encode.rs1use arrow::ipc::writer::StreamWriter;
8use arrow::record_batch::RecordBatch;
9use orion_error::conversion::SourceRawErr;
10use wp_connector_api::{SinkReason, SinkResult};
11
12pub fn encode_batch_ipc_stream(batch: &RecordBatch) -> SinkResult<Vec<u8>> {
17 let schema = batch.schema();
18 let mut buf = Vec::new();
19 {
20 let mut writer = StreamWriter::try_new(&mut buf, &schema)
21 .source_raw_err(SinkReason::Sink, "arrow create stream writer")?;
22 writer
23 .write(batch)
24 .source_raw_err(SinkReason::Sink, "arrow encode batch")?;
25 writer
26 .finish()
27 .source_raw_err(SinkReason::Sink, "arrow finish stream")?;
28 }
29 Ok(buf)
30}
31
32pub fn encode_ipc_frame(tag: &str, batch: &RecordBatch) -> SinkResult<Vec<u8>> {
33 let tag_bytes = tag.as_bytes();
34 let schema = batch.schema();
35 let mut buf = Vec::with_capacity(4 + tag_bytes.len() + 1024);
36 buf.extend_from_slice(&(tag_bytes.len() as u32).to_be_bytes());
37 buf.extend_from_slice(tag_bytes);
38 {
39 let mut writer = StreamWriter::try_new(&mut buf, &schema)
40 .source_raw_err(SinkReason::Sink, "arrow create framed writer")?;
41 writer
42 .write(batch)
43 .source_raw_err(SinkReason::Sink, "arrow encode framed")?;
44 writer
45 .finish()
46 .source_raw_err(SinkReason::Sink, "arrow finish framed")?;
47 }
48 Ok(buf)
49}
50
51pub fn encode_ipc_frame_multi(tag: &str, batches: &[RecordBatch]) -> SinkResult<Vec<u8>> {
52 if batches.is_empty() {
53 let tag_bytes = tag.as_bytes();
54 let mut buf = Vec::with_capacity(4 + tag_bytes.len());
55 buf.extend_from_slice(&(tag_bytes.len() as u32).to_be_bytes());
56 buf.extend_from_slice(tag_bytes);
57 return Ok(buf);
58 }
59 let tag_bytes = tag.as_bytes();
60 let schema = batches[0].schema();
61 let mut buf = Vec::with_capacity(4 + tag_bytes.len() + 1024);
62 buf.extend_from_slice(&(tag_bytes.len() as u32).to_be_bytes());
63 buf.extend_from_slice(tag_bytes);
64 {
65 let mut writer = StreamWriter::try_new(&mut buf, &schema)
66 .source_raw_err(SinkReason::Sink, "arrow create framed multi writer")?;
67 for batch in batches {
68 writer
69 .write(batch)
70 .source_raw_err(SinkReason::Sink, "arrow encode framed multi")?;
71 }
72 writer
73 .finish()
74 .source_raw_err(SinkReason::Sink, "arrow finish framed multi")?;
75 }
76 Ok(buf)
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use arrow::array::StringArray;
83 use arrow::datatypes::{DataType, Field, Schema};
84 use std::sync::Arc;
85
86 fn make_schema() -> Arc<Schema> {
87 Arc::new(Schema::new(vec![Field::new("x", DataType::Utf8, false)]))
88 }
89
90 fn make_batch(schema: &Arc<Schema>, values: Vec<&str>) -> RecordBatch {
91 RecordBatch::try_new(schema.clone(), vec![Arc::new(StringArray::from(values))]).unwrap()
92 }
93
94 #[test]
95 fn ipc_roundtrip() {
96 let s = make_schema();
97 let b = make_batch(&s, vec!["hi"]);
98 let ipc = encode_batch_ipc_stream(&b).unwrap();
99 assert!(ipc.len() > 8);
101 assert_eq!(&ipc[0..4], &[0xff, 0xff, 0xff, 0xff]);
102 }
103
104 #[test]
105 fn framed_roundtrip() {
106 let s = make_schema();
107 let b = make_batch(&s, vec!["hi"]);
108 let frame = encode_ipc_frame("my_tag", &b).unwrap();
109 assert_eq!(&frame[0..4], &6u32.to_be_bytes());
111 assert_eq!(&frame[4..10], b"my_tag");
112 }
113
114 #[test]
115 fn ipc_frame_multi_roundtrip() {
116 let s = make_schema();
117 let b1 = make_batch(&s, vec!["a"]);
118 let b2 = make_batch(&s, vec!["b", "c"]);
119 let frame = encode_ipc_frame_multi("multi", &[b1, b2]).unwrap();
120 assert_eq!(&frame[0..4], &5u32.to_be_bytes());
121 assert_eq!(&frame[4..9], b"multi");
122 }
123
124 #[test]
125 fn ipc_frame_multi_empty() {
126 let frame = encode_ipc_frame_multi("tag", &[]).unwrap();
127 assert_eq!(&frame[0..4], &3u32.to_be_bytes());
128 assert_eq!(&frame[4..7], b"tag");
129 }
130}