Skip to main content

arrow_array/builder/
fixed_size_binary_builder.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use crate::array::Array;
19use crate::builder::ArrayBuilder;
20use crate::{ArrayRef, FixedSizeBinaryArray};
21use arrow_buffer::Buffer;
22use arrow_buffer::NullBufferBuilder;
23use arrow_data::ArrayData;
24use arrow_schema::{ArrowError, DataType};
25use std::any::Any;
26use std::sync::Arc;
27
28/// Builder for [`FixedSizeBinaryArray`]
29/// ```
30/// # use arrow_array::builder::FixedSizeBinaryBuilder;
31/// # use arrow_array::Array;
32/// #
33/// let mut builder = FixedSizeBinaryBuilder::with_capacity(3, 5);
34/// // [b"hello", null, b"arrow"]
35/// builder.append_value(b"hello").unwrap();
36/// builder.append_null();
37/// builder.append_value(b"arrow").unwrap();
38///
39/// let array = builder.finish();
40/// assert_eq!(array.value(0), b"hello");
41/// assert!(array.is_null(1));
42/// assert_eq!(array.value(2), b"arrow");
43/// ```
44#[derive(Debug)]
45pub struct FixedSizeBinaryBuilder {
46    values_builder: Vec<u8>,
47    null_buffer_builder: NullBufferBuilder,
48    value_length: i32,
49}
50
51impl FixedSizeBinaryBuilder {
52    /// Creates a new [`FixedSizeBinaryBuilder`]
53    pub fn new(byte_width: i32) -> Self {
54        Self::with_capacity(1024, byte_width)
55    }
56
57    /// Creates a new [`FixedSizeBinaryBuilder`], `capacity` is the number of byte slices
58    /// that can be appended without reallocating
59    ///
60    /// # Panics
61    ///
62    /// Panics if `byte_width < 0`
63    pub fn with_capacity(capacity: usize, byte_width: i32) -> Self {
64        assert!(
65            byte_width >= 0,
66            "value length ({byte_width}) of the array must >= 0"
67        );
68        Self {
69            values_builder: Vec::with_capacity(capacity * byte_width as usize),
70            null_buffer_builder: NullBufferBuilder::new(capacity),
71            value_length: byte_width,
72        }
73    }
74
75    /// Appends a byte slice into the builder.
76    ///
77    /// Automatically update the null buffer to delimit the slice appended in as a
78    /// distinct value element.
79    #[inline]
80    pub fn append_value(&mut self, value: impl AsRef<[u8]>) -> Result<(), ArrowError> {
81        if self.value_length != value.as_ref().len() as i32 {
82            Err(ArrowError::InvalidArgumentError(
83                "Byte slice does not have the same length as FixedSizeBinaryBuilder value lengths"
84                    .to_string(),
85            ))
86        } else {
87            self.values_builder.extend_from_slice(value.as_ref());
88            self.null_buffer_builder.append_non_null();
89            Ok(())
90        }
91    }
92
93    /// Append a null value to the array.
94    #[inline]
95    pub fn append_null(&mut self) {
96        self.values_builder
97            .extend(std::iter::repeat_n(0u8, self.value_length as usize));
98        self.null_buffer_builder.append_null();
99    }
100
101    /// Appends `n` `null`s into the builder.
102    #[inline]
103    pub fn append_nulls(&mut self, n: usize) {
104        self.values_builder
105            .extend(std::iter::repeat_n(0u8, self.value_length as usize * n));
106        self.null_buffer_builder.append_n_nulls(n);
107    }
108
109    /// Appends all elements in array into the builder.
110    pub fn append_array(&mut self, array: &FixedSizeBinaryArray) -> Result<(), ArrowError> {
111        if self.value_length != array.value_length() {
112            return Err(ArrowError::InvalidArgumentError(
113                "Cannot append FixedSizeBinaryArray with different value length".to_string(),
114            ));
115        }
116        let buffer = array.value_data();
117        self.values_builder.extend_from_slice(buffer);
118        if let Some(validity) = array.nulls() {
119            self.null_buffer_builder.append_buffer(validity);
120        } else {
121            self.null_buffer_builder.append_n_non_nulls(array.len());
122        }
123        Ok(())
124    }
125
126    /// Returns the current values buffer as a slice
127    pub fn values_slice(&self) -> &[u8] {
128        self.values_builder.as_slice()
129    }
130
131    /// Builds the [`FixedSizeBinaryArray`] and reset this builder.
132    pub fn finish(&mut self) -> FixedSizeBinaryArray {
133        let array_length = self.len();
134        let array_data_builder = ArrayData::builder(DataType::FixedSizeBinary(self.value_length))
135            .add_buffer(std::mem::take(&mut self.values_builder).into())
136            .nulls(self.null_buffer_builder.finish())
137            .len(array_length);
138        // SAFETY: value_length >= 0, values.len() == len * value_length, and nulls.len() == len — all guaranteed by the builder
139        let array_data = unsafe { array_data_builder.build_unchecked() };
140        FixedSizeBinaryArray::from(array_data)
141    }
142
143    /// Builds the [`FixedSizeBinaryArray`] without resetting the builder.
144    pub fn finish_cloned(&self) -> FixedSizeBinaryArray {
145        let array_length = self.len();
146        let values_buffer = Buffer::from_slice_ref(self.values_builder.as_slice());
147        let array_data_builder = ArrayData::builder(DataType::FixedSizeBinary(self.value_length))
148            .add_buffer(values_buffer)
149            .nulls(self.null_buffer_builder.finish_cloned())
150            .len(array_length);
151        // SAFETY: value_length >= 0, values.len() == len * value_length, and nulls.len() == len — all guaranteed by the builder
152        let array_data = unsafe { array_data_builder.build_unchecked() };
153        FixedSizeBinaryArray::from(array_data)
154    }
155
156    /// Returns the current null buffer as a slice
157    pub fn validity_slice(&self) -> Option<&[u8]> {
158        self.null_buffer_builder.as_slice()
159    }
160    ///  Returns the total memory capacity in bytes currently
161    pub fn capacity(&self) -> usize {
162        self.values_builder.capacity() + self.null_buffer_builder.allocated_size() + 4 // i32 
163    }
164}
165
166impl ArrayBuilder for FixedSizeBinaryBuilder {
167    /// Returns the builder as a non-mutable `Any` reference.
168    fn as_any(&self) -> &dyn Any {
169        self
170    }
171
172    /// Returns the builder as a mutable `Any` reference.
173    fn as_any_mut(&mut self) -> &mut dyn Any {
174        self
175    }
176
177    /// Returns the boxed builder as a box of `Any`.
178    fn into_box_any(self: Box<Self>) -> Box<dyn Any> {
179        self
180    }
181
182    /// Returns the number of array slots in the builder
183    fn len(&self) -> usize {
184        self.null_buffer_builder.len()
185    }
186
187    /// Builds the array and reset this builder.
188    fn finish(&mut self) -> ArrayRef {
189        Arc::new(self.finish())
190    }
191
192    /// Builds the array without resetting the builder.
193    fn finish_cloned(&self) -> ArrayRef {
194        Arc::new(self.finish_cloned())
195    }
196}
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201
202    use crate::Array;
203
204    #[test]
205    fn test_fixed_size_binary_builder() {
206        let mut builder = FixedSizeBinaryBuilder::with_capacity(3, 5);
207
208        //  [b"hello", null, "arrow", null, null, "world"]
209        builder.append_value(b"hello").unwrap();
210        builder.append_null();
211        builder.append_value(b"arrow").unwrap();
212        builder.append_nulls(2);
213        builder.append_value(b"world").unwrap();
214        let array = builder.finish();
215
216        assert_eq!(&DataType::FixedSizeBinary(5), array.data_type());
217        assert_eq!(6, array.len());
218        assert_eq!(3, array.null_count());
219        assert_eq!(5, array.value_length());
220        assert_eq!(b"arrow", array.value(2));
221        assert!(array.is_null(3));
222        assert!(array.is_null(4));
223        assert_eq!(b"world", array.value(5));
224    }
225
226    #[test]
227    fn test_fixed_size_binary_builder_finish_cloned() {
228        let mut builder = FixedSizeBinaryBuilder::with_capacity(3, 5);
229
230        //  [b"hello", null, "arrow"]
231        builder.append_value(b"hello").unwrap();
232        builder.append_null();
233        builder.append_value(b"arrow").unwrap();
234        let mut array = builder.finish_cloned();
235
236        assert_eq!(&DataType::FixedSizeBinary(5), array.data_type());
237        assert_eq!(3, array.len());
238        assert_eq!(1, array.null_count());
239        assert_eq!(5, array.value_length());
240        assert_eq!(b"arrow", array.value(2));
241
242        //  [b"finis", null, "clone"]
243        builder.append_value(b"finis").unwrap();
244        builder.append_null();
245        builder.append_value(b"clone").unwrap();
246
247        array = builder.finish();
248
249        assert_eq!(&DataType::FixedSizeBinary(5), array.data_type());
250        assert_eq!(6, array.len());
251        assert_eq!(2, array.null_count());
252        assert_eq!(5, array.value_length());
253        assert_eq!(b"clone", array.value(5));
254    }
255
256    #[test]
257    fn test_fixed_size_binary_builder_with_zero_value_length() {
258        let mut builder = FixedSizeBinaryBuilder::new(0);
259
260        builder.append_value(b"").unwrap();
261        builder.append_null();
262        builder.append_value(b"").unwrap();
263        assert!(!builder.is_empty());
264
265        let array = builder.finish();
266        assert_eq!(&DataType::FixedSizeBinary(0), array.data_type());
267        assert_eq!(3, array.len());
268        assert_eq!(1, array.null_count());
269        assert_eq!(0, array.value_length());
270        assert_eq!(b"", array.value(0));
271        assert_eq!(b"", array.value(2));
272    }
273
274    #[test]
275    #[should_panic(
276        expected = "Byte slice does not have the same length as FixedSizeBinaryBuilder value lengths"
277    )]
278    fn test_fixed_size_binary_builder_with_inconsistent_value_length() {
279        let mut builder = FixedSizeBinaryBuilder::with_capacity(1, 4);
280        builder.append_value(b"hello").unwrap();
281    }
282    #[test]
283    fn test_fixed_size_binary_builder_empty() {
284        let mut builder = FixedSizeBinaryBuilder::new(5);
285        assert!(builder.is_empty());
286
287        let fixed_size_binary_array = builder.finish();
288        assert_eq!(
289            &DataType::FixedSizeBinary(5),
290            fixed_size_binary_array.data_type()
291        );
292        assert_eq!(0, fixed_size_binary_array.len());
293    }
294
295    #[test]
296    #[should_panic(expected = "value length (-1) of the array must >= 0")]
297    fn test_fixed_size_binary_builder_invalid_value_length() {
298        let _ = FixedSizeBinaryBuilder::with_capacity(15, -1);
299    }
300
301    #[test]
302    fn test_fixed_size_binary_builder_append_array() {
303        let mut other_builder = FixedSizeBinaryBuilder::with_capacity(3, 5);
304        other_builder.append_value(b"hello").unwrap();
305        other_builder.append_null();
306        other_builder.append_value(b"arrow").unwrap();
307        let other_array = other_builder.finish();
308
309        let mut builder = FixedSizeBinaryBuilder::with_capacity(6, 5);
310        builder.append_array(&other_array).unwrap();
311        // Append again to test if breaks when appending multiple times
312        builder.append_array(&other_array).unwrap();
313        let array = builder.finish();
314
315        assert_eq!(array.value_length(), other_array.value_length());
316        assert_eq!(&DataType::FixedSizeBinary(5), array.data_type());
317        assert_eq!(6, array.len());
318        assert_eq!(2, array.null_count());
319        assert_eq!(b"hello", array.value(0));
320        assert!(array.is_null(1));
321        assert_eq!(b"arrow", array.value(2));
322
323        assert_eq!(b"hello", array.value(3));
324        assert!(array.is_null(4));
325        assert_eq!(b"arrow", array.value(5));
326    }
327
328    #[test]
329    #[should_panic(expected = "Cannot append FixedSizeBinaryArray with different value length")]
330    fn test_fixed_size_binary_builder_append_array_invalid_value_length() {
331        let mut other_builder = FixedSizeBinaryBuilder::with_capacity(3, 4);
332        other_builder.append_value(b"test").unwrap();
333        let other_array = other_builder.finish();
334        let mut builder = FixedSizeBinaryBuilder::with_capacity(3, 5);
335        builder.append_array(&other_array).unwrap();
336    }
337}