Coverage Report

Created: 2025-08-26 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/Users/andrewlamb/Software/arrow-rs/arrow-array/src/builder/fixed_size_binary_builder.rs
Line
Count
Source
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
18
use crate::builder::ArrayBuilder;
19
use crate::{ArrayRef, FixedSizeBinaryArray};
20
use arrow_buffer::Buffer;
21
use arrow_buffer::NullBufferBuilder;
22
use arrow_data::ArrayData;
23
use arrow_schema::{ArrowError, DataType};
24
use std::any::Any;
25
use std::sync::Arc;
26
27
/// Builder for [`FixedSizeBinaryArray`]
28
/// ```
29
/// # use arrow_array::builder::FixedSizeBinaryBuilder;
30
/// # use arrow_array::Array;
31
/// #
32
/// let mut builder = FixedSizeBinaryBuilder::with_capacity(3, 5);
33
/// // [b"hello", null, b"arrow"]
34
/// builder.append_value(b"hello").unwrap();
35
/// builder.append_null();
36
/// builder.append_value(b"arrow").unwrap();
37
///
38
/// let array = builder.finish();
39
/// assert_eq!(array.value(0), b"hello");
40
/// assert!(array.is_null(1));
41
/// assert_eq!(array.value(2), b"arrow");
42
/// ```
43
#[derive(Debug)]
44
pub struct FixedSizeBinaryBuilder {
45
    values_builder: Vec<u8>,
46
    null_buffer_builder: NullBufferBuilder,
47
    value_length: i32,
48
}
49
50
impl FixedSizeBinaryBuilder {
51
    /// Creates a new [`FixedSizeBinaryBuilder`]
52
0
    pub fn new(byte_width: i32) -> Self {
53
0
        Self::with_capacity(1024, byte_width)
54
0
    }
55
56
    /// Creates a new [`FixedSizeBinaryBuilder`], `capacity` is the number of byte slices
57
    /// that can be appended without reallocating
58
0
    pub fn with_capacity(capacity: usize, byte_width: i32) -> Self {
59
0
        assert!(
60
0
            byte_width >= 0,
61
0
            "value length ({byte_width}) of the array must >= 0"
62
        );
63
0
        Self {
64
0
            values_builder: Vec::with_capacity(capacity * byte_width as usize),
65
0
            null_buffer_builder: NullBufferBuilder::new(capacity),
66
0
            value_length: byte_width,
67
0
        }
68
0
    }
69
70
    /// Appends a byte slice into the builder.
71
    ///
72
    /// Automatically update the null buffer to delimit the slice appended in as a
73
    /// distinct value element.
74
    #[inline]
75
0
    pub fn append_value(&mut self, value: impl AsRef<[u8]>) -> Result<(), ArrowError> {
76
0
        if self.value_length != value.as_ref().len() as i32 {
77
0
            Err(ArrowError::InvalidArgumentError(
78
0
                "Byte slice does not have the same length as FixedSizeBinaryBuilder value lengths"
79
0
                    .to_string(),
80
0
            ))
81
        } else {
82
0
            self.values_builder.extend_from_slice(value.as_ref());
83
0
            self.null_buffer_builder.append_non_null();
84
0
            Ok(())
85
        }
86
0
    }
87
88
    /// Append a null value to the array.
89
    #[inline]
90
0
    pub fn append_null(&mut self) {
91
0
        self.values_builder
92
0
            .extend(std::iter::repeat_n(0u8, self.value_length as usize));
93
0
        self.null_buffer_builder.append_null();
94
0
    }
95
96
    /// Appends `n` `null`s into the builder.
97
    #[inline]
98
    pub fn append_nulls(&mut self, n: usize) {
99
        self.values_builder
100
            .extend(std::iter::repeat_n(0u8, self.value_length as usize * n));
101
        self.null_buffer_builder.append_n_nulls(n);
102
    }
103
104
    /// Returns the current values buffer as a slice
105
0
    pub fn values_slice(&self) -> &[u8] {
106
0
        self.values_builder.as_slice()
107
0
    }
108
109
    /// Builds the [`FixedSizeBinaryArray`] and reset this builder.
110
0
    pub fn finish(&mut self) -> FixedSizeBinaryArray {
111
0
        let array_length = self.len();
112
0
        let array_data_builder = ArrayData::builder(DataType::FixedSizeBinary(self.value_length))
113
0
            .add_buffer(std::mem::take(&mut self.values_builder).into())
114
0
            .nulls(self.null_buffer_builder.finish())
115
0
            .len(array_length);
116
0
        let array_data = unsafe { array_data_builder.build_unchecked() };
117
0
        FixedSizeBinaryArray::from(array_data)
118
0
    }
119
120
    /// Builds the [`FixedSizeBinaryArray`] without resetting the builder.
121
0
    pub fn finish_cloned(&self) -> FixedSizeBinaryArray {
122
0
        let array_length = self.len();
123
0
        let values_buffer = Buffer::from_slice_ref(self.values_builder.as_slice());
124
0
        let array_data_builder = ArrayData::builder(DataType::FixedSizeBinary(self.value_length))
125
0
            .add_buffer(values_buffer)
126
0
            .nulls(self.null_buffer_builder.finish_cloned())
127
0
            .len(array_length);
128
0
        let array_data = unsafe { array_data_builder.build_unchecked() };
129
0
        FixedSizeBinaryArray::from(array_data)
130
0
    }
131
132
    /// Returns the current null buffer as a slice
133
0
    pub fn validity_slice(&self) -> Option<&[u8]> {
134
0
        self.null_buffer_builder.as_slice()
135
0
    }
136
}
137
138
impl ArrayBuilder for FixedSizeBinaryBuilder {
139
    /// Returns the builder as a non-mutable `Any` reference.
140
0
    fn as_any(&self) -> &dyn Any {
141
0
        self
142
0
    }
143
144
    /// Returns the builder as a mutable `Any` reference.
145
0
    fn as_any_mut(&mut self) -> &mut dyn Any {
146
0
        self
147
0
    }
148
149
    /// Returns the boxed builder as a box of `Any`.
150
0
    fn into_box_any(self: Box<Self>) -> Box<dyn Any> {
151
0
        self
152
0
    }
153
154
    /// Returns the number of array slots in the builder
155
0
    fn len(&self) -> usize {
156
0
        self.null_buffer_builder.len()
157
0
    }
158
159
    /// Builds the array and reset this builder.
160
0
    fn finish(&mut self) -> ArrayRef {
161
0
        Arc::new(self.finish())
162
0
    }
163
164
    /// Builds the array without resetting the builder.
165
0
    fn finish_cloned(&self) -> ArrayRef {
166
0
        Arc::new(self.finish_cloned())
167
0
    }
168
}
169
170
#[cfg(test)]
171
mod tests {
172
    use super::*;
173
174
    use crate::Array;
175
176
    #[test]
177
    fn test_fixed_size_binary_builder() {
178
        let mut builder = FixedSizeBinaryBuilder::with_capacity(3, 5);
179
180
        //  [b"hello", null, "arrow", null, null, "world"]
181
        builder.append_value(b"hello").unwrap();
182
        builder.append_null();
183
        builder.append_value(b"arrow").unwrap();
184
        builder.append_nulls(2);
185
        builder.append_value(b"world").unwrap();
186
        let array: FixedSizeBinaryArray = builder.finish();
187
188
        assert_eq!(&DataType::FixedSizeBinary(5), array.data_type());
189
        assert_eq!(6, array.len());
190
        assert_eq!(3, array.null_count());
191
        assert_eq!(10, array.value_offset(2));
192
        assert_eq!(15, array.value_offset(3));
193
        assert_eq!(5, array.value_length());
194
        assert!(array.is_null(3));
195
        assert!(array.is_null(4));
196
    }
197
198
    #[test]
199
    fn test_fixed_size_binary_builder_finish_cloned() {
200
        let mut builder = FixedSizeBinaryBuilder::with_capacity(3, 5);
201
202
        //  [b"hello", null, "arrow"]
203
        builder.append_value(b"hello").unwrap();
204
        builder.append_null();
205
        builder.append_value(b"arrow").unwrap();
206
        let mut array: FixedSizeBinaryArray = builder.finish_cloned();
207
208
        assert_eq!(&DataType::FixedSizeBinary(5), array.data_type());
209
        assert_eq!(3, array.len());
210
        assert_eq!(1, array.null_count());
211
        assert_eq!(10, array.value_offset(2));
212
        assert_eq!(5, array.value_length());
213
214
        //  [b"finis", null, "clone"]
215
        builder.append_value(b"finis").unwrap();
216
        builder.append_null();
217
        builder.append_value(b"clone").unwrap();
218
219
        array = builder.finish();
220
221
        assert_eq!(&DataType::FixedSizeBinary(5), array.data_type());
222
        assert_eq!(6, array.len());
223
        assert_eq!(2, array.null_count());
224
        assert_eq!(25, array.value_offset(5));
225
        assert_eq!(5, array.value_length());
226
    }
227
228
    #[test]
229
    fn test_fixed_size_binary_builder_with_zero_value_length() {
230
        let mut builder = FixedSizeBinaryBuilder::new(0);
231
232
        builder.append_value(b"").unwrap();
233
        builder.append_null();
234
        builder.append_value(b"").unwrap();
235
        assert!(!builder.is_empty());
236
237
        let array: FixedSizeBinaryArray = builder.finish();
238
        assert_eq!(&DataType::FixedSizeBinary(0), array.data_type());
239
        assert_eq!(3, array.len());
240
        assert_eq!(1, array.null_count());
241
        assert_eq!(0, array.value_offset(2));
242
        assert_eq!(0, array.value_length());
243
        assert_eq!(b"", array.value(0));
244
        assert_eq!(b"", array.value(2));
245
    }
246
247
    #[test]
248
    #[should_panic(
249
        expected = "Byte slice does not have the same length as FixedSizeBinaryBuilder value lengths"
250
    )]
251
    fn test_fixed_size_binary_builder_with_inconsistent_value_length() {
252
        let mut builder = FixedSizeBinaryBuilder::with_capacity(1, 4);
253
        builder.append_value(b"hello").unwrap();
254
    }
255
    #[test]
256
    fn test_fixed_size_binary_builder_empty() {
257
        let mut builder = FixedSizeBinaryBuilder::new(5);
258
        assert!(builder.is_empty());
259
260
        let fixed_size_binary_array = builder.finish();
261
        assert_eq!(
262
            &DataType::FixedSizeBinary(5),
263
            fixed_size_binary_array.data_type()
264
        );
265
        assert_eq!(0, fixed_size_binary_array.len());
266
    }
267
268
    #[test]
269
    #[should_panic(expected = "value length (-1) of the array must >= 0")]
270
    fn test_fixed_size_binary_builder_invalid_value_length() {
271
        let _ = FixedSizeBinaryBuilder::with_capacity(15, -1);
272
    }
273
}