This is an automated email from the ASF dual-hosted git repository.

agrove pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/master by this push:
     new b84d823  ARROW-7475: [Rust] Arrow IPC Stream writer
b84d823 is described below

commit b84d8238d643318967cb282fddf76eb78d44a11c
Author: Neville Dipale <[email protected]>
AuthorDate: Sun Jan 26 13:32:14 2020 -0700

    ARROW-7475: [Rust] Arrow IPC Stream writer
    
    The stream writer is applied on top of ARROW-5182 #6107 (file writer) and 
should be merged after.
    
    Closes #6281 from nevi-me/ARROW-7475 and squashes the following commits:
    
    11ed168b7 <Neville Dipale> fix IPC test file location
    0f90b3e57 <Neville Dipale> ARROW-7475:  Arrow IPC Stream writer
    cb82160db <Neville Dipale> remove redundant header_bytes
    
    Authored-by: Neville Dipale <[email protected]>
    Signed-off-by: Andy Grove <[email protected]>
---
 rust/arrow/src/ipc/gen/File.rs         |  31 +++++++
 rust/arrow/src/ipc/gen/Message.rs      |   3 +-
 rust/arrow/src/ipc/gen/Schema.rs       |  80 ++++++++++++++++-
 rust/arrow/src/ipc/gen/SparseTensor.rs | 154 +++++++++++++++++++++++++--------
 rust/arrow/src/ipc/writer.rs           | 116 +++++++++++++++++++++++--
 5 files changed, 336 insertions(+), 48 deletions(-)

diff --git a/rust/arrow/src/ipc/gen/File.rs b/rust/arrow/src/ipc/gen/File.rs
index e68c8e5..a805100 100644
--- a/rust/arrow/src/ipc/gen/File.rs
+++ b/rust/arrow/src/ipc/gen/File.rs
@@ -125,6 +125,9 @@ impl<'a> Footer<'a> {
         args: &'args FooterArgs<'args>,
     ) -> flatbuffers::WIPOffset<Footer<'bldr>> {
         let mut builder = FooterBuilder::new(_fbb);
+        if let Some(x) = args.custom_metadata {
+            builder.add_custom_metadata(x);
+        }
         if let Some(x) = args.recordBatches {
             builder.add_recordBatches(x);
         }
@@ -142,6 +145,7 @@ impl<'a> Footer<'a> {
     pub const VT_SCHEMA: flatbuffers::VOffsetT = 6;
     pub const VT_DICTIONARIES: flatbuffers::VOffsetT = 8;
     pub const VT_RECORDBATCHES: flatbuffers::VOffsetT = 10;
+    pub const VT_CUSTOM_METADATA: flatbuffers::VOffsetT = 12;
 
     #[inline]
     pub fn version(&self) -> MetadataVersion {
@@ -172,6 +176,15 @@ impl<'a> Footer<'a> {
             )
             .map(|v| v.safe_slice())
     }
+    /// User-defined metadata
+    #[inline]
+    pub fn custom_metadata(
+        &self,
+    ) -> Option<flatbuffers::Vector<'a, 
flatbuffers::ForwardsUOffset<KeyValue<'a>>>> {
+        self._tab.get::<flatbuffers::ForwardsUOffset<
+            flatbuffers::Vector<flatbuffers::ForwardsUOffset<KeyValue<'a>>>,
+        >>(Footer::VT_CUSTOM_METADATA, None)
+    }
 }
 
 pub struct FooterArgs<'a> {
@@ -179,6 +192,11 @@ pub struct FooterArgs<'a> {
     pub schema: Option<flatbuffers::WIPOffset<Schema<'a>>>,
     pub dictionaries: Option<flatbuffers::WIPOffset<flatbuffers::Vector<'a, 
Block>>>,
     pub recordBatches: Option<flatbuffers::WIPOffset<flatbuffers::Vector<'a, 
Block>>>,
+    pub custom_metadata: Option<
+        flatbuffers::WIPOffset<
+            flatbuffers::Vector<'a, 
flatbuffers::ForwardsUOffset<KeyValue<'a>>>,
+        >,
+    >,
 }
 impl<'a> Default for FooterArgs<'a> {
     #[inline]
@@ -188,6 +206,7 @@ impl<'a> Default for FooterArgs<'a> {
             schema: None,
             dictionaries: None,
             recordBatches: None,
+            custom_metadata: None,
         }
     }
 }
@@ -233,6 +252,18 @@ impl<'a: 'b, 'b> FooterBuilder<'a, 'b> {
         );
     }
     #[inline]
+    pub fn add_custom_metadata(
+        &mut self,
+        custom_metadata: flatbuffers::WIPOffset<
+            flatbuffers::Vector<'b, 
flatbuffers::ForwardsUOffset<KeyValue<'b>>>,
+        >,
+    ) {
+        self.fbb_.push_slot_always::<flatbuffers::WIPOffset<_>>(
+            Footer::VT_CUSTOM_METADATA,
+            custom_metadata,
+        );
+    }
+    #[inline]
     pub fn new(
         _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>,
     ) -> FooterBuilder<'a, 'b> {
diff --git a/rust/arrow/src/ipc/gen/Message.rs 
b/rust/arrow/src/ipc/gen/Message.rs
index 5e403b0..0907ea8 100644
--- a/rust/arrow/src/ipc/gen/Message.rs
+++ b/rust/arrow/src/ipc/gen/Message.rs
@@ -385,7 +385,8 @@ impl<'a> DictionaryBatch<'a> {
             )
     }
     /// If isDelta is true the values in the dictionary are to be appended to a
-    /// dictionary with the indicated id
+    /// dictionary with the indicated id. If isDelta is false this dictionary
+    /// should replace the existing dictionary.
     #[inline]
     pub fn isDelta(&self) -> bool {
         self._tab
diff --git a/rust/arrow/src/ipc/gen/Schema.rs b/rust/arrow/src/ipc/gen/Schema.rs
index 30113bd..24136ad 100644
--- a/rust/arrow/src/ipc/gen/Schema.rs
+++ b/rust/arrow/src/ipc/gen/Schema.rs
@@ -486,6 +486,63 @@ pub fn enum_name_type(e: Type) -> &'static str {
 
 pub struct TypeUnionTableOffset {}
 /// ----------------------------------------------------------------------
+/// Dictionary encoding metadata
+/// Maintained for forwards compatibility, in the future
+/// Dictionaries might be explicit maps between integers and values
+/// allowing for non-contiguous index values
+#[allow(non_camel_case_types)]
+#[repr(i16)]
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+pub enum DictionaryKind {
+    DenseArray = 0,
+}
+
+const ENUM_MIN_DICTIONARY_KIND: i16 = 0;
+const ENUM_MAX_DICTIONARY_KIND: i16 = 0;
+
+impl<'a> flatbuffers::Follow<'a> for DictionaryKind {
+    type Inner = Self;
+    #[inline]
+    fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+        flatbuffers::read_scalar_at::<Self>(buf, loc)
+    }
+}
+
+impl flatbuffers::EndianScalar for DictionaryKind {
+    #[inline]
+    fn to_little_endian(self) -> Self {
+        let n = i16::to_le(self as i16);
+        let p = &n as *const i16 as *const DictionaryKind;
+        unsafe { *p }
+    }
+    #[inline]
+    fn from_little_endian(self) -> Self {
+        let n = i16::from_le(self as i16);
+        let p = &n as *const i16 as *const DictionaryKind;
+        unsafe { *p }
+    }
+}
+
+impl flatbuffers::Push for DictionaryKind {
+    type Output = DictionaryKind;
+    #[inline]
+    fn push(&self, dst: &mut [u8], _rest: &[u8]) {
+        flatbuffers::emplace_scalar::<DictionaryKind>(dst, *self);
+    }
+}
+
+#[allow(non_camel_case_types)]
+const ENUM_VALUES_DICTIONARY_KIND: [DictionaryKind; 1] = 
[DictionaryKind::DenseArray];
+
+#[allow(non_camel_case_types)]
+const ENUM_NAMES_DICTIONARY_KIND: [&'static str; 1] = ["DenseArray"];
+
+pub fn enum_name_dictionary_kind(e: DictionaryKind) -> &'static str {
+    let index = e as i16;
+    ENUM_NAMES_DICTIONARY_KIND[index as usize]
+}
+
+/// ----------------------------------------------------------------------
 /// Endianness of the platform producing the data
 #[allow(non_camel_case_types)]
 #[repr(i16)]
@@ -2358,8 +2415,6 @@ impl<'a: 'b, 'b> KeyValueBuilder<'a, 'b> {
 pub enum DictionaryEncodingOffset {}
 #[derive(Copy, Clone, Debug, PartialEq)]
 
-/// ----------------------------------------------------------------------
-/// Dictionary encoding metadata
 pub struct DictionaryEncoding<'a> {
     pub _tab: flatbuffers::Table<'a>,
 }
@@ -2389,6 +2444,7 @@ impl<'a> DictionaryEncoding<'a> {
         if let Some(x) = args.indexType {
             builder.add_indexType(x);
         }
+        builder.add_dictionaryKind(args.dictionaryKind);
         builder.add_isOrdered(args.isOrdered);
         builder.finish()
     }
@@ -2396,6 +2452,7 @@ impl<'a> DictionaryEncoding<'a> {
     pub const VT_ID: flatbuffers::VOffsetT = 4;
     pub const VT_INDEXTYPE: flatbuffers::VOffsetT = 6;
     pub const VT_ISORDERED: flatbuffers::VOffsetT = 8;
+    pub const VT_DICTIONARYKIND: flatbuffers::VOffsetT = 10;
 
     /// The known dictionary id in the application where this data is used. In
     /// the file or streaming formats, the dictionary ids are found in the
@@ -2425,12 +2482,22 @@ impl<'a> DictionaryEncoding<'a> {
             .get::<bool>(DictionaryEncoding::VT_ISORDERED, Some(false))
             .unwrap()
     }
+    #[inline]
+    pub fn dictionaryKind(&self) -> DictionaryKind {
+        self._tab
+            .get::<DictionaryKind>(
+                DictionaryEncoding::VT_DICTIONARYKIND,
+                Some(DictionaryKind::DenseArray),
+            )
+            .unwrap()
+    }
 }
 
 pub struct DictionaryEncodingArgs<'a> {
     pub id: i64,
     pub indexType: Option<flatbuffers::WIPOffset<Int<'a>>>,
     pub isOrdered: bool,
+    pub dictionaryKind: DictionaryKind,
 }
 impl<'a> Default for DictionaryEncodingArgs<'a> {
     #[inline]
@@ -2439,6 +2506,7 @@ impl<'a> Default for DictionaryEncodingArgs<'a> {
             id: 0,
             indexType: None,
             isOrdered: false,
+            dictionaryKind: DictionaryKind::DenseArray,
         }
     }
 }
@@ -2464,6 +2532,14 @@ impl<'a: 'b, 'b> DictionaryEncodingBuilder<'a, 'b> {
             .push_slot::<bool>(DictionaryEncoding::VT_ISORDERED, isOrdered, 
false);
     }
     #[inline]
+    pub fn add_dictionaryKind(&mut self, dictionaryKind: DictionaryKind) {
+        self.fbb_.push_slot::<DictionaryKind>(
+            DictionaryEncoding::VT_DICTIONARYKIND,
+            dictionaryKind,
+            DictionaryKind::DenseArray,
+        );
+    }
+    #[inline]
     pub fn new(
         _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>,
     ) -> DictionaryEncodingBuilder<'a, 'b> {
diff --git a/rust/arrow/src/ipc/gen/SparseTensor.rs 
b/rust/arrow/src/ipc/gen/SparseTensor.rs
index 8c04c6c..c716897 100644
--- a/rust/arrow/src/ipc/gen/SparseTensor.rs
+++ b/rust/arrow/src/ipc/gen/SparseTensor.rs
@@ -25,12 +25,70 @@ use std::{cmp::Ordering, mem};
 // automatically generated by the FlatBuffers compiler, do not modify
 
 #[allow(non_camel_case_types)]
+#[repr(i16)]
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+pub enum SparseMatrixCompressedAxis {
+    Row = 0,
+    Column = 1,
+}
+
+const ENUM_MIN_SPARSE_MATRIX_COMPRESSED_AXIS: i16 = 0;
+const ENUM_MAX_SPARSE_MATRIX_COMPRESSED_AXIS: i16 = 1;
+
+impl<'a> flatbuffers::Follow<'a> for SparseMatrixCompressedAxis {
+    type Inner = Self;
+    #[inline]
+    fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+        flatbuffers::read_scalar_at::<Self>(buf, loc)
+    }
+}
+
+impl flatbuffers::EndianScalar for SparseMatrixCompressedAxis {
+    #[inline]
+    fn to_little_endian(self) -> Self {
+        let n = i16::to_le(self as i16);
+        let p = &n as *const i16 as *const SparseMatrixCompressedAxis;
+        unsafe { *p }
+    }
+    #[inline]
+    fn from_little_endian(self) -> Self {
+        let n = i16::from_le(self as i16);
+        let p = &n as *const i16 as *const SparseMatrixCompressedAxis;
+        unsafe { *p }
+    }
+}
+
+impl flatbuffers::Push for SparseMatrixCompressedAxis {
+    type Output = SparseMatrixCompressedAxis;
+    #[inline]
+    fn push(&self, dst: &mut [u8], _rest: &[u8]) {
+        flatbuffers::emplace_scalar::<SparseMatrixCompressedAxis>(dst, *self);
+    }
+}
+
+#[allow(non_camel_case_types)]
+const ENUM_VALUES_SPARSE_MATRIX_COMPRESSED_AXIS: [SparseMatrixCompressedAxis; 
2] = [
+    SparseMatrixCompressedAxis::Row,
+    SparseMatrixCompressedAxis::Column,
+];
+
+#[allow(non_camel_case_types)]
+const ENUM_NAMES_SPARSE_MATRIX_COMPRESSED_AXIS: [&'static str; 2] = ["Row", 
"Column"];
+
+pub fn enum_name_sparse_matrix_compressed_axis(
+    e: SparseMatrixCompressedAxis,
+) -> &'static str {
+    let index = e as i16;
+    ENUM_NAMES_SPARSE_MATRIX_COMPRESSED_AXIS[index as usize]
+}
+
+#[allow(non_camel_case_types)]
 #[repr(u8)]
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
 pub enum SparseTensorIndex {
     NONE = 0,
     SparseTensorIndexCOO = 1,
-    SparseMatrixIndexCSR = 2,
+    SparseMatrixIndexCSX = 2,
 }
 
 const ENUM_MIN_SPARSE_TENSOR_INDEX: u8 = 0;
@@ -71,12 +129,12 @@ impl flatbuffers::Push for SparseTensorIndex {
 const ENUM_VALUES_SPARSE_TENSOR_INDEX: [SparseTensorIndex; 3] = [
     SparseTensorIndex::NONE,
     SparseTensorIndex::SparseTensorIndexCOO,
-    SparseTensorIndex::SparseMatrixIndexCSR,
+    SparseTensorIndex::SparseMatrixIndexCSX,
 ];
 
 #[allow(non_camel_case_types)]
 const ENUM_NAMES_SPARSE_TENSOR_INDEX: [&'static str; 3] =
-    ["NONE", "SparseTensorIndexCOO", "SparseMatrixIndexCSR"];
+    ["NONE", "SparseTensorIndexCOO", "SparseMatrixIndexCSX"];
 
 pub fn enum_name_sparse_tensor_index(e: SparseTensorIndex) -> &'static str {
     let index = e as u8;
@@ -244,16 +302,16 @@ impl<'a: 'b, 'b> SparseTensorIndexCOOBuilder<'a, 'b> {
     }
 }
 
-pub enum SparseMatrixIndexCSROffset {}
+pub enum SparseMatrixIndexCSXOffset {}
 #[derive(Copy, Clone, Debug, PartialEq)]
 
-/// Compressed Sparse Row format, that is matrix-specific.
-pub struct SparseMatrixIndexCSR<'a> {
+/// Compressed Sparse format, that is matrix-specific.
+pub struct SparseMatrixIndexCSX<'a> {
     pub _tab: flatbuffers::Table<'a>,
 }
 
-impl<'a> flatbuffers::Follow<'a> for SparseMatrixIndexCSR<'a> {
-    type Inner = SparseMatrixIndexCSR<'a>;
+impl<'a> flatbuffers::Follow<'a> for SparseMatrixIndexCSX<'a> {
+    type Inner = SparseMatrixIndexCSX<'a>;
     #[inline]
     fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
         Self {
@@ -262,17 +320,17 @@ impl<'a> flatbuffers::Follow<'a> for 
SparseMatrixIndexCSR<'a> {
     }
 }
 
-impl<'a> SparseMatrixIndexCSR<'a> {
+impl<'a> SparseMatrixIndexCSX<'a> {
     #[inline]
     pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
-        SparseMatrixIndexCSR { _tab: table }
+        SparseMatrixIndexCSX { _tab: table }
     }
     #[allow(unused_mut)]
     pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
         _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>,
-        args: &'args SparseMatrixIndexCSRArgs<'args>,
-    ) -> flatbuffers::WIPOffset<SparseMatrixIndexCSR<'bldr>> {
-        let mut builder = SparseMatrixIndexCSRBuilder::new(_fbb);
+        args: &'args SparseMatrixIndexCSXArgs<'args>,
+    ) -> flatbuffers::WIPOffset<SparseMatrixIndexCSX<'bldr>> {
+        let mut builder = SparseMatrixIndexCSXBuilder::new(_fbb);
         if let Some(x) = args.indicesBuffer {
             builder.add_indicesBuffer(x);
         }
@@ -285,19 +343,31 @@ impl<'a> SparseMatrixIndexCSR<'a> {
         if let Some(x) = args.indptrType {
             builder.add_indptrType(x);
         }
+        builder.add_compressedAxis(args.compressedAxis);
         builder.finish()
     }
 
-    pub const VT_INDPTRTYPE: flatbuffers::VOffsetT = 4;
-    pub const VT_INDPTRBUFFER: flatbuffers::VOffsetT = 6;
-    pub const VT_INDICESTYPE: flatbuffers::VOffsetT = 8;
-    pub const VT_INDICESBUFFER: flatbuffers::VOffsetT = 10;
+    pub const VT_COMPRESSEDAXIS: flatbuffers::VOffsetT = 4;
+    pub const VT_INDPTRTYPE: flatbuffers::VOffsetT = 6;
+    pub const VT_INDPTRBUFFER: flatbuffers::VOffsetT = 8;
+    pub const VT_INDICESTYPE: flatbuffers::VOffsetT = 10;
+    pub const VT_INDICESBUFFER: flatbuffers::VOffsetT = 12;
 
+    /// Which axis, row or column, is compressed
+    #[inline]
+    pub fn compressedAxis(&self) -> SparseMatrixCompressedAxis {
+        self._tab
+            .get::<SparseMatrixCompressedAxis>(
+                SparseMatrixIndexCSX::VT_COMPRESSEDAXIS,
+                Some(SparseMatrixCompressedAxis::Row),
+            )
+            .unwrap()
+    }
     /// The type of values in indptrBuffer
     #[inline]
     pub fn indptrType(&self) -> Option<Int<'a>> {
         self._tab.get::<flatbuffers::ForwardsUOffset<Int<'a>>>(
-            SparseMatrixIndexCSR::VT_INDPTRTYPE,
+            SparseMatrixIndexCSX::VT_INDPTRTYPE,
             None,
         )
     }
@@ -326,13 +396,13 @@ impl<'a> SparseMatrixIndexCSR<'a> {
     #[inline]
     pub fn indptrBuffer(&self) -> Option<&'a Buffer> {
         self._tab
-            .get::<Buffer>(SparseMatrixIndexCSR::VT_INDPTRBUFFER, None)
+            .get::<Buffer>(SparseMatrixIndexCSX::VT_INDPTRBUFFER, None)
     }
     /// The type of values in indicesBuffer
     #[inline]
     pub fn indicesType(&self) -> Option<Int<'a>> {
         self._tab.get::<flatbuffers::ForwardsUOffset<Int<'a>>>(
-            SparseMatrixIndexCSR::VT_INDICESTYPE,
+            SparseMatrixIndexCSX::VT_INDICESTYPE,
             None,
         )
     }
@@ -348,20 +418,22 @@ impl<'a> SparseMatrixIndexCSR<'a> {
     #[inline]
     pub fn indicesBuffer(&self) -> Option<&'a Buffer> {
         self._tab
-            .get::<Buffer>(SparseMatrixIndexCSR::VT_INDICESBUFFER, None)
+            .get::<Buffer>(SparseMatrixIndexCSX::VT_INDICESBUFFER, None)
     }
 }
 
-pub struct SparseMatrixIndexCSRArgs<'a> {
+pub struct SparseMatrixIndexCSXArgs<'a> {
+    pub compressedAxis: SparseMatrixCompressedAxis,
     pub indptrType: Option<flatbuffers::WIPOffset<Int<'a>>>,
     pub indptrBuffer: Option<&'a Buffer>,
     pub indicesType: Option<flatbuffers::WIPOffset<Int<'a>>>,
     pub indicesBuffer: Option<&'a Buffer>,
 }
-impl<'a> Default for SparseMatrixIndexCSRArgs<'a> {
+impl<'a> Default for SparseMatrixIndexCSXArgs<'a> {
     #[inline]
     fn default() -> Self {
-        SparseMatrixIndexCSRArgs {
+        SparseMatrixIndexCSXArgs {
+            compressedAxis: SparseMatrixCompressedAxis::Row,
             indptrType: None,
             indptrBuffer: None,
             indicesType: None,
@@ -369,51 +441,59 @@ impl<'a> Default for SparseMatrixIndexCSRArgs<'a> {
         }
     }
 }
-pub struct SparseMatrixIndexCSRBuilder<'a: 'b, 'b> {
+pub struct SparseMatrixIndexCSXBuilder<'a: 'b, 'b> {
     fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>,
     start_: flatbuffers::WIPOffset<flatbuffers::TableUnfinishedWIPOffset>,
 }
-impl<'a: 'b, 'b> SparseMatrixIndexCSRBuilder<'a, 'b> {
+impl<'a: 'b, 'b> SparseMatrixIndexCSXBuilder<'a, 'b> {
+    #[inline]
+    pub fn add_compressedAxis(&mut self, compressedAxis: 
SparseMatrixCompressedAxis) {
+        self.fbb_.push_slot::<SparseMatrixCompressedAxis>(
+            SparseMatrixIndexCSX::VT_COMPRESSEDAXIS,
+            compressedAxis,
+            SparseMatrixCompressedAxis::Row,
+        );
+    }
     #[inline]
     pub fn add_indptrType(&mut self, indptrType: 
flatbuffers::WIPOffset<Int<'b>>) {
         self.fbb_.push_slot_always::<flatbuffers::WIPOffset<Int>>(
-            SparseMatrixIndexCSR::VT_INDPTRTYPE,
+            SparseMatrixIndexCSX::VT_INDPTRTYPE,
             indptrType,
         );
     }
     #[inline]
     pub fn add_indptrBuffer(&mut self, indptrBuffer: &'b Buffer) {
         self.fbb_.push_slot_always::<&Buffer>(
-            SparseMatrixIndexCSR::VT_INDPTRBUFFER,
+            SparseMatrixIndexCSX::VT_INDPTRBUFFER,
             indptrBuffer,
         );
     }
     #[inline]
     pub fn add_indicesType(&mut self, indicesType: 
flatbuffers::WIPOffset<Int<'b>>) {
         self.fbb_.push_slot_always::<flatbuffers::WIPOffset<Int>>(
-            SparseMatrixIndexCSR::VT_INDICESTYPE,
+            SparseMatrixIndexCSX::VT_INDICESTYPE,
             indicesType,
         );
     }
     #[inline]
     pub fn add_indicesBuffer(&mut self, indicesBuffer: &'b Buffer) {
         self.fbb_.push_slot_always::<&Buffer>(
-            SparseMatrixIndexCSR::VT_INDICESBUFFER,
+            SparseMatrixIndexCSX::VT_INDICESBUFFER,
             indicesBuffer,
         );
     }
     #[inline]
     pub fn new(
         _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>,
-    ) -> SparseMatrixIndexCSRBuilder<'a, 'b> {
+    ) -> SparseMatrixIndexCSXBuilder<'a, 'b> {
         let start = _fbb.start_table();
-        SparseMatrixIndexCSRBuilder {
+        SparseMatrixIndexCSXBuilder {
             fbb_: _fbb,
             start_: start,
         }
     }
     #[inline]
-    pub fn finish(self) -> flatbuffers::WIPOffset<SparseMatrixIndexCSR<'a>> {
+    pub fn finish(self) -> flatbuffers::WIPOffset<SparseMatrixIndexCSX<'a>> {
         let o = self.fbb_.end_table(self.start_);
         flatbuffers::WIPOffset::new(o.value())
     }
@@ -755,12 +835,12 @@ impl<'a> SparseTensor<'a> {
 
     #[inline]
     #[allow(non_snake_case)]
-    pub fn sparseIndex_as_sparse_matrix_index_csr(
+    pub fn sparseIndex_as_sparse_matrix_index_csx(
         &self,
-    ) -> Option<SparseMatrixIndexCSR<'a>> {
-        if self.sparseIndex_type() == SparseTensorIndex::SparseMatrixIndexCSR {
+    ) -> Option<SparseMatrixIndexCSX<'a>> {
+        if self.sparseIndex_type() == SparseTensorIndex::SparseMatrixIndexCSX {
             self.sparseIndex()
-                .map(|u| SparseMatrixIndexCSR::init_from_table(u))
+                .map(|u| SparseMatrixIndexCSX::init_from_table(u))
         } else {
             None
         }
diff --git a/rust/arrow/src/ipc/writer.rs b/rust/arrow/src/ipc/writer.rs
index 0800bd6..36c89c7 100644
--- a/rust/arrow/src/ipc/writer.rs
+++ b/rust/arrow/src/ipc/writer.rs
@@ -36,8 +36,6 @@ pub struct FileWriter<W: Write> {
     writer: BufWriter<W>,
     /// A reference to the schema, used in validating record batches
     schema: Schema,
-    /// The number of bytes written for the header (up to schema)
-    header_bytes: usize,
     /// The number of bytes between each block of bytes, as an offset for 
random access
     block_offsets: usize,
     /// Dictionary blocks that will be written as part of the IPC footer
@@ -61,7 +59,6 @@ impl<W: Write> FileWriter<W> {
         Ok(Self {
             writer,
             schema: schema.clone(),
-            header_bytes: written,
             block_offsets: written,
             dictionary_blocks: vec![],
             record_blocks: vec![],
@@ -76,7 +73,7 @@ impl<W: Write> FileWriter<W> {
                 "Cannot write record batch to file writer as it is 
closed".to_string(),
             ));
         }
-        let (meta, data) = write_record_batch(&mut self.writer, batch)?;
+        let (meta, data) = write_record_batch(&mut self.writer, batch, false)?;
         // add a record block for the footer
         self.record_blocks.push(ipc::Block::new(
             self.block_offsets as i64,
@@ -87,7 +84,7 @@ impl<W: Write> FileWriter<W> {
         Ok(())
     }
 
-    /// write footer and closing tag, then mark the writer as done
+    /// Write footer and closing tag, then mark the writer as done
     pub fn finish(&mut self) -> Result<()> {
         let mut fbb = FlatBufferBuilder::new();
         let dictionaries = fbb.create_vector(&self.dictionary_blocks);
@@ -160,6 +157,58 @@ impl<W: Write> Drop for FileWriter<W> {
     }
 }
 
+pub struct StreamWriter<W: Write> {
+    /// The object to write to
+    writer: BufWriter<W>,
+    /// A reference to the schema, used in validating record batches
+    schema: Schema,
+    /// Whether the writer footer has been written, and the writer is finished
+    finished: bool,
+}
+
+impl<W: Write> StreamWriter<W> {
+    /// Try create a new writer, with the schema written as part of the header
+    pub fn try_new(writer: W, schema: &Schema) -> Result<Self> {
+        let mut writer = BufWriter::new(writer);
+        // write the schema, set the written bytes to the schema
+        write_schema(&mut writer, schema)?;
+        Ok(Self {
+            writer,
+            schema: schema.clone(),
+            finished: false,
+        })
+    }
+
+    /// Write a record batch to the stream
+    pub fn write(&mut self, batch: &RecordBatch) -> Result<()> {
+        if self.finished {
+            return Err(ArrowError::IoError(
+                "Cannot write record batch to stream writer as it is 
closed".to_string(),
+            ));
+        }
+        write_record_batch(&mut self.writer, batch, true)?;
+        Ok(())
+    }
+
+    /// Write continuation bytes, and mark the stream as done
+    pub fn finish(&mut self) -> Result<()> {
+        self.writer.write(&[0u8, 0, 0, 0])?;
+        self.writer.write(&[255u8, 255, 255, 255])?;
+        self.finished = true;
+
+        Ok(())
+    }
+}
+
+/// Finish the stream if it is not 'finished' when it goes out of scope
+impl<W: Write> Drop for StreamWriter<W> {
+    fn drop(&mut self) {
+        if !self.finished {
+            self.finish().unwrap();
+        }
+    }
+}
+
 /// Convert the schema to its IPC representation, and write it to the `writer`
 fn write_schema<R: Write>(writer: &mut BufWriter<R>, schema: &Schema) -> 
Result<usize> {
     let mut fbb = FlatBufferBuilder::new();
@@ -217,10 +266,12 @@ fn write_padded_data<R: Write>(
     Ok(total_len as usize)
 }
 
-/// Write a record batch to the writer
+/// Write a record batch to the writer, writing the message size before the 
message
+/// if the record batch is being written to a stream
 fn write_record_batch<R: Write>(
     writer: &mut BufWriter<R>,
     batch: &RecordBatch,
+    is_stream: bool,
 ) -> Result<(usize, usize)> {
     let mut fbb = FlatBufferBuilder::new();
 
@@ -261,6 +312,12 @@ fn write_record_batch<R: Write>(
     message.add_header(root);
     let root = message.finish();
     fbb.finish(root, None);
+    let finished_data = fbb.finished_data();
+    // write the length of data if writing to stream
+    if is_stream {
+        let total_len: u32 = finished_data.len() as u32;
+        writer.write(&total_len.to_le_bytes()[..])?;
+    }
     let meta_written =
         write_padded_data(writer, fbb.finished_data(), WriteDataType::Body)?;
     let arrow_data_written =
@@ -415,7 +472,7 @@ mod tests {
         ];
         paths.iter().for_each(|path| {
             let file = File::open(format!(
-                "{}/arrow-ipc/integration/0.14.1/{}.arrow_file",
+                "{}/arrow-ipc-stream/integration/0.14.1/{}.arrow_file",
                 testdata, path
             ))
             .unwrap();
@@ -443,11 +500,54 @@ mod tests {
             assert!(arrow_json.equals_reader(&mut reader));
         });
     }
+
+    #[test]
+    fn read_and_rewrite_generated_streams() {
+        let testdata = env::var("ARROW_TEST_DATA").expect("ARROW_TEST_DATA not 
defined");
+        // the test is repetitive, thus we can read all supported files at once
+        let paths = vec![
+            "generated_interval",
+            "generated_datetime",
+            "generated_nested",
+            "generated_primitive_no_batches",
+            "generated_primitive_zerolength",
+            "generated_primitive",
+        ];
+        paths.iter().for_each(|path| {
+            let file = File::open(format!(
+                "{}/arrow-ipc-stream/integration/0.14.1/{}.stream",
+                testdata, path
+            ))
+            .unwrap();
+
+            let mut reader = StreamReader::try_new(file).unwrap();
+
+            // read and rewrite the stream to a temp location
+            {
+                let file = 
File::create(format!("target/debug/testdata/{}.stream", path))
+                    .unwrap();
+                let mut writer = StreamWriter::try_new(file, 
&reader.schema()).unwrap();
+                while let Ok(Some(batch)) = reader.next() {
+                    writer.write(&batch).unwrap();
+                }
+                writer.finish().unwrap();
+            }
+
+            let file =
+                File::open(format!("target/debug/testdata/{}.stream", 
path)).unwrap();
+            let mut reader = StreamReader::try_new(file).unwrap();
+
+            // read expected JSON output
+            let arrow_json = read_gzip_json(path);
+            assert!(arrow_json.equals_reader(&mut reader));
+        });
+    }
+
     /// Read gzipped JSON file
     fn read_gzip_json(path: &str) -> ArrowJson {
         let testdata = env::var("ARROW_TEST_DATA").expect("ARROW_TEST_DATA not 
defined");
         let file = File::open(format!(
-            "{}/arrow-ipc/integration/0.14.1/{}.json.gz",
+            "{}/arrow-ipc-stream/integration/0.14.1/{}.json.gz",
             testdata, path
         ))
         .unwrap();

Reply via email to