1use arrow_array::builder::BufferBuilder;
23use arrow_array::cast::AsArray;
24use arrow_array::types::*;
25use arrow_array::*;
26use arrow_buffer::{ArrowNativeType, MutableBuffer, NullBuffer, OffsetBuffer};
27use arrow_schema::{ArrowError, DataType};
28use num_traits::Zero;
29use std::cmp::Ordering;
30use std::sync::Arc;
31
32pub fn substring(
73 array: &dyn Array,
74 start: i64,
75 length: Option<u64>,
76) -> Result<ArrayRef, ArrowError> {
77 match array.data_type() {
78 DataType::Dictionary(_, _) => {
79 let dictionary = array.as_any_dictionary();
80 let values = substring(dictionary.values(), start, length)?;
81 Ok(Arc::new(dictionary.with_values(values)))
82 }
83 DataType::LargeBinary => {
84 byte_substring(array.as_binary::<i64>(), start, length.map(|e| e as i64))
85 }
86 DataType::Binary => byte_substring(
87 array.as_binary::<i32>(),
88 start as i32,
89 length.map(|e| e as i32),
90 ),
91 DataType::FixedSizeBinary(old_len) => {
92 let old_len: usize = (*old_len)
93 .try_into()
94 .expect("negative FixedSizeBinary value length");
95 fixed_size_binary_substring(array.as_fixed_size_binary(), old_len, start, length)
96 }
97 DataType::LargeUtf8 => {
98 byte_substring(array.as_string::<i64>(), start, length.map(|e| e as i64))
99 }
100 DataType::Utf8 => byte_substring(
101 array.as_string::<i32>(),
102 start as i32,
103 length.map(|e| e as i32),
104 ),
105 _ => Err(ArrowError::ComputeError(format!(
106 "substring does not support type {:?}",
107 array.data_type()
108 ))),
109 }
110}
111
112pub fn substring_by_char<OffsetSize: OffsetSizeTrait>(
141 array: &GenericStringArray<OffsetSize>,
142 start: i64,
143 length: Option<u64>,
144) -> Result<GenericStringArray<OffsetSize>, ArrowError> {
145 let mut vals = BufferBuilder::<u8>::new({
146 let offsets = array.value_offsets();
147 (offsets[array.len()] - offsets[0]).to_usize().unwrap()
148 });
149 let mut new_offsets = BufferBuilder::<OffsetSize>::new(array.len() + 1);
150 new_offsets.append(OffsetSize::zero());
151 let length = length.map(|len| len.to_usize().unwrap());
152
153 array.iter().for_each(|val| {
154 if let Some(val) = val {
155 let char_count = val.chars().count();
156 let start = if start >= 0 {
157 start.to_usize().unwrap()
158 } else {
159 char_count - (-start).to_usize().unwrap().min(char_count)
160 };
161 let (start_offset, end_offset) = get_start_end_offset(val, start, length);
162 vals.append_slice(&val.as_bytes()[start_offset..end_offset]);
163 }
164 new_offsets.append(OffsetSize::from_usize(vals.len()).unwrap());
165 });
166 let offsets = OffsetBuffer::new(new_offsets.finish().into());
167 let values = vals.finish();
168 let nulls = array
169 .nulls()
170 .map(|n| n.inner().sliced())
171 .and_then(|b| NullBuffer::from_unsliced_buffer(b, array.len()));
172 Ok(GenericStringArray::<OffsetSize>::new(
173 offsets, values, nulls,
174 ))
175}
176
177fn get_start_end_offset(val: &str, start: usize, length: Option<usize>) -> (usize, usize) {
183 let len = val.len();
184 let mut offset_char_iter = val.char_indices();
185 let start_offset = offset_char_iter
186 .nth(start)
187 .map_or(len, |(offset, _)| offset);
188 let end_offset = length.map_or(len, |length| {
189 if length > 0 {
190 offset_char_iter
191 .nth(length - 1)
192 .map_or(len, |(offset, _)| offset)
193 } else {
194 start_offset
195 }
196 });
197 (start_offset, end_offset)
198}
199
200fn byte_substring<T: ByteArrayType>(
201 array: &GenericByteArray<T>,
202 start: T::Offset,
203 length: Option<T::Offset>,
204) -> Result<ArrayRef, ArrowError>
205where
206 <T as ByteArrayType>::Native: PartialEq,
207{
208 let offsets = array.value_offsets();
209 let data = array.value_data();
210 let zero = <T::Offset as Zero>::zero();
211
212 let check_char_boundary = {
214 |offset: T::Offset| {
215 if !matches!(T::DATA_TYPE, DataType::Utf8 | DataType::LargeUtf8) {
216 return Ok(offset);
217 }
218 let data_str = unsafe { std::str::from_utf8_unchecked(data) };
220 let offset_usize = offset.as_usize();
221 if data_str.is_char_boundary(offset_usize) {
222 Ok(offset)
223 } else {
224 Err(ArrowError::ComputeError(format!(
225 "The offset {offset_usize} is at an invalid utf-8 boundary."
226 )))
227 }
228 }
229 };
230
231 let mut new_starts_ends: Vec<(T::Offset, T::Offset)> = Vec::with_capacity(array.len());
233 let mut new_offsets: Vec<T::Offset> = Vec::with_capacity(array.len() + 1);
234 let mut len_so_far = zero;
235 new_offsets.push(zero);
236
237 offsets
238 .windows(2)
239 .try_for_each(|pair| -> Result<(), ArrowError> {
240 let new_start = match start.cmp(&zero) {
241 Ordering::Greater => check_char_boundary((pair[0] + start).min(pair[1]))?,
242 Ordering::Equal => pair[0],
243 Ordering::Less => check_char_boundary((pair[1] + start).max(pair[0]))?,
244 };
245 let new_end = match length {
246 Some(length) => check_char_boundary((length + new_start).min(pair[1]))?,
247 None => pair[1],
248 };
249 len_so_far += new_end - new_start;
250 new_starts_ends.push((new_start, new_end));
251 new_offsets.push(len_so_far);
252 Ok(())
253 })?;
254
255 let mut new_values = MutableBuffer::new(new_offsets.last().unwrap().as_usize());
257
258 new_starts_ends
259 .iter()
260 .map(|(start, end)| {
261 let start = start.as_usize();
262 let end = end.as_usize();
263 &data[start..end]
264 })
265 .for_each(|slice| new_values.extend_from_slice(slice));
266
267 let offsets = OffsetBuffer::new(new_offsets.into());
268 let values = new_values.into();
269 let nulls = array
270 .nulls()
271 .map(|n| n.inner().sliced())
272 .and_then(|b| NullBuffer::from_unsliced_buffer(b, array.len()));
273 Ok(Arc::new(GenericByteArray::<T>::new(offsets, values, nulls)))
274}
275
276fn fixed_size_binary_substring(
277 array: &FixedSizeBinaryArray,
278 old_len: usize,
279 start: i64,
280 length: Option<u64>,
281) -> Result<ArrayRef, ArrowError> {
282 let new_start = match start.cmp(&0) {
283 Ordering::Greater => usize::try_from(start).unwrap_or(usize::MAX).min(old_len),
284 Ordering::Equal => 0,
285 Ordering::Less => {
286 let offset = usize::try_from(start.unsigned_abs()).unwrap_or(usize::MAX);
287 old_len.saturating_sub(offset)
288 }
289 };
290
291 let new_len = match length {
292 Some(len) => usize::try_from(len)
293 .unwrap_or(usize::MAX)
294 .min(old_len - new_start),
295 None => old_len - new_start,
296 };
297
298 let num_of_elements = array.len();
300 let data = array.value_data();
301 let capacity = num_of_elements
302 .checked_mul(new_len)
303 .expect("capacity overflow");
304 let mut new_values = MutableBuffer::new(capacity);
305 (0..num_of_elements)
306 .map(|idx| {
307 let offset = idx * array.value_size();
308 (offset + new_start, offset + new_start + new_len)
309 })
310 .for_each(|(start, end)| new_values.extend_from_slice(&data[start..end]));
311
312 let mut nulls = array
313 .nulls()
314 .map(|n| n.inner().sliced())
315 .and_then(|b| NullBuffer::from_unsliced_buffer(b, num_of_elements));
316
317 if new_len == 0 && nulls.is_none() {
318 nulls = Some(NullBuffer::new_valid(num_of_elements));
323 }
324
325 let new_len: i32 = new_len.try_into().expect("new_len overflow");
326
327 Ok(Arc::new(FixedSizeBinaryArray::new(
328 new_len,
329 new_values.into(),
330 nulls,
331 )))
332}
333
334#[cfg(test)]
335mod tests {
336 use super::*;
337 use arrow_buffer::BooleanBuffer;
338 use arrow_buffer::Buffer;
339
340 macro_rules! gen_test_cases {
351 ($input:expr, $(($start:expr, $len:expr, $result:expr)), *) => {
352 [
353 $(
354 ($input.clone(), $start, $len, $result),
355 )*
356 ]
357 };
358 }
359
360 macro_rules! do_test {
367 ($cases:expr, $array_ty:ty, $substring_fn:ident) => {
368 $cases
369 .into_iter()
370 .for_each(|(array, start, length, expected)| {
371 let array = <$array_ty>::from(array);
372 let result = $substring_fn(&array, start, length).unwrap();
373 let result = result.as_any().downcast_ref::<$array_ty>().unwrap();
374 let expected = <$array_ty>::from(expected);
375 assert_eq!(&expected, result);
376 })
377 };
378 }
379
380 macro_rules! do_test_tryfrom {
382 ($cases:expr, $array_ty:ty, $substring_fn:ident) => {
383 $cases
384 .into_iter()
385 .for_each(|(array, start, length, expected)| {
386 let array = <$array_ty>::try_from(array).unwrap();
387 let result = $substring_fn(&array, start, length).unwrap();
388 let result = result.as_any().downcast_ref::<$array_ty>().unwrap();
389 let expected = <$array_ty>::try_from(expected).unwrap();
390 assert_eq!(&expected, result);
391 })
392 };
393 }
394
395 fn with_nulls_generic_binary<O: OffsetSizeTrait>() {
396 let input = vec![
397 Some("hello".as_bytes()),
398 None,
399 Some(&[0xf8, 0xf9, 0xff, 0xfa]),
400 ];
401 let base_case = gen_test_cases!(
403 vec![None, None, None],
404 (-1, Some(1), vec![None, None, None])
405 );
406 let cases = gen_test_cases!(
407 input,
408 (0, None, input.clone()),
410 (0, Some(0), vec![Some(&[]), None, Some(&[])]),
412 (1000, Some(0), vec![Some(&[]), None, Some(&[])]),
414 (-1000, None, input.clone()),
416 (0, Some(1000), input.clone())
418 );
419
420 do_test!(
421 [&base_case[..], &cases[..]].concat(),
422 GenericBinaryArray<O>,
423 substring
424 );
425 }
426
427 #[test]
428 fn with_nulls_binary() {
429 with_nulls_generic_binary::<i32>()
430 }
431
432 #[test]
433 fn with_nulls_large_binary() {
434 with_nulls_generic_binary::<i64>()
435 }
436
437 fn without_nulls_generic_binary<O: OffsetSizeTrait>() {
438 let input = vec!["hello".as_bytes(), b"", &[0xf8, 0xf9, 0xff, 0xfa]];
439 let base_case = gen_test_cases!(
441 vec!["".as_bytes(), b"", b""],
442 (2, Some(1), vec!["".as_bytes(), b"", b""])
443 );
444 let cases = gen_test_cases!(
445 input,
446 (0, None, input.clone()),
448 (1, None, vec![b"ello", b"", &[0xf9, 0xff, 0xfa]]),
450 (2, None, vec![b"llo", b"", &[0xff, 0xfa]]),
451 (3, None, vec![b"lo", b"", &[0xfa]]),
452 (10, None, vec![b"", b"", b""]),
453 (-1, None, vec![b"o", b"", &[0xfa]]),
455 (-2, None, vec![b"lo", b"", &[0xff, 0xfa]]),
456 (-3, None, vec![b"llo", b"", &[0xf9, 0xff, 0xfa]]),
457 (-10, None, input.clone()),
458 (1, Some(1), vec![b"e", b"", &[0xf9]]),
460 (1, Some(2), vec![b"el", b"", &[0xf9, 0xff]]),
461 (1, Some(3), vec![b"ell", b"", &[0xf9, 0xff, 0xfa]]),
462 (1, Some(4), vec![b"ello", b"", &[0xf9, 0xff, 0xfa]]),
463 (-3, Some(1), vec![b"l", b"", &[0xf9]]),
464 (-3, Some(2), vec![b"ll", b"", &[0xf9, 0xff]]),
465 (-3, Some(3), vec![b"llo", b"", &[0xf9, 0xff, 0xfa]]),
466 (-3, Some(4), vec![b"llo", b"", &[0xf9, 0xff, 0xfa]])
467 );
468
469 do_test!(
470 [&base_case[..], &cases[..]].concat(),
471 GenericBinaryArray<O>,
472 substring
473 );
474 }
475
476 #[test]
477 fn without_nulls_binary() {
478 without_nulls_generic_binary::<i32>()
479 }
480
481 #[test]
482 fn without_nulls_large_binary() {
483 without_nulls_generic_binary::<i64>()
484 }
485
486 fn generic_binary_with_non_zero_offset<O: OffsetSizeTrait>() {
487 let values = 0_u8..15;
488 let offsets = &[
489 O::zero(),
490 O::from_usize(5).unwrap(),
491 O::from_usize(10).unwrap(),
492 O::from_usize(15).unwrap(),
493 ];
494 let bitmap = [0b101_u8];
496
497 let offsets = OffsetBuffer::new(Buffer::from_slice_ref(offsets).into());
498 let values = Buffer::from_iter(values);
499 let nulls = Some(NullBuffer::new(BooleanBuffer::new(
500 Buffer::from(bitmap),
501 0,
502 3,
503 )));
504 let array = GenericBinaryArray::<O>::new(offsets, values, nulls).slice(1, 2);
506 let result = substring(&array, 1, None).unwrap();
508 let result = result
509 .as_any()
510 .downcast_ref::<GenericBinaryArray<O>>()
511 .unwrap();
512 let expected =
513 GenericBinaryArray::<O>::from_opt_vec(vec![None, Some(&[11_u8, 12, 13, 14])]);
514 assert_eq!(result, &expected);
515 }
516
517 #[test]
518 fn binary_with_non_zero_offset() {
519 generic_binary_with_non_zero_offset::<i32>()
520 }
521
522 #[test]
523 fn large_binary_with_non_zero_offset() {
524 generic_binary_with_non_zero_offset::<i64>()
525 }
526
527 #[test]
528 fn with_nulls_fixed_size_binary() {
529 let input = vec![Some("cat".as_bytes()), None, Some(&[0xf8, 0xf9, 0xff])];
530 let base_case =
532 gen_test_cases!(vec![None, None, None], (3, Some(2), vec![None, None, None]));
533 let cases = gen_test_cases!(
534 input,
535 (0, None, input.clone()),
537 (1, None, vec![Some(b"at"), None, Some(&[0xf9, 0xff])]),
539 (2, None, vec![Some(b"t"), None, Some(&[0xff])]),
540 (3, None, vec![Some(b""), None, Some(b"")]),
541 (10, None, vec![Some(b""), None, Some(b"")]),
542 (-1, None, vec![Some(b"t"), None, Some(&[0xff])]),
544 (-2, None, vec![Some(b"at"), None, Some(&[0xf9, 0xff])]),
545 (-3, None, input.clone()),
546 (-10, None, input.clone()),
547 (1, Some(1), vec![Some(b"a"), None, Some(&[0xf9])]),
549 (1, Some(2), vec![Some(b"at"), None, Some(&[0xf9, 0xff])]),
550 (1, Some(3), vec![Some(b"at"), None, Some(&[0xf9, 0xff])]),
551 (-3, Some(1), vec![Some(b"c"), None, Some(&[0xf8])]),
552 (-3, Some(2), vec![Some(b"ca"), None, Some(&[0xf8, 0xf9])]),
553 (-3, Some(3), input.clone()),
554 (-3, Some(4), input.clone())
555 );
556
557 do_test_tryfrom!(
558 [&base_case[..], &cases[..]].concat(),
559 FixedSizeBinaryArray,
560 substring
561 );
562 }
563
564 #[test]
565 fn without_nulls_fixed_size_binary() {
566 let input = vec!["cat".as_bytes(), b"dog", &[0xf8, 0xf9, 0xff]];
567 let base_case = gen_test_cases!(
569 vec!["".as_bytes(), &[], &[]],
570 (1, Some(2), vec!["".as_bytes(), &[], &[]])
571 );
572 let cases = gen_test_cases!(
573 input,
574 (0, None, input.clone()),
576 (1, None, vec![b"at", b"og", &[0xf9, 0xff]]),
578 (2, None, vec![b"t", b"g", &[0xff]]),
579 (3, None, vec![&[], &[], &[]]),
580 (10, None, vec![&[], &[], &[]]),
581 (-1, None, vec![b"t", b"g", &[0xff]]),
583 (-2, None, vec![b"at", b"og", &[0xf9, 0xff]]),
584 (-3, None, input.clone()),
585 (-10, None, input.clone()),
586 (1, Some(1), vec![b"a", b"o", &[0xf9]]),
588 (1, Some(2), vec![b"at", b"og", &[0xf9, 0xff]]),
589 (1, Some(3), vec![b"at", b"og", &[0xf9, 0xff]]),
590 (-3, Some(1), vec![b"c", b"d", &[0xf8]]),
591 (-3, Some(2), vec![b"ca", b"do", &[0xf8, 0xf9]]),
592 (-3, Some(3), input.clone()),
593 (-3, Some(4), input.clone())
594 );
595
596 do_test_tryfrom!(
597 [&base_case[..], &cases[..]].concat(),
598 FixedSizeBinaryArray,
599 substring
600 );
601 }
602
603 #[test]
604 fn fixed_size_binary_with_non_zero_offset() {
605 let values = b"hellotherearrow";
606 let bits_v = [0b101_u8];
608
609 let nulls = Some(NullBuffer::new(BooleanBuffer::new(
610 Buffer::from(bits_v),
611 0,
612 3,
613 )));
614 let array = FixedSizeBinaryArray::new(5, Buffer::from(values), nulls).slice(1, 2);
616 let result = substring(&array, 1, None).unwrap();
618 let result = result
619 .as_any()
620 .downcast_ref::<FixedSizeBinaryArray>()
621 .unwrap();
622 let expected = FixedSizeBinaryArray::try_from_sparse_iter_with_size(
623 vec![None, Some(b"rrow")].into_iter(),
624 4,
625 )
626 .unwrap();
627 assert_eq!(result, &expected);
628 }
629
630 fn with_nulls_generic_string<O: OffsetSizeTrait>() {
631 let input = vec![Some("hello"), None, Some("word")];
632 let base_case = gen_test_cases!(vec![None, None, None], (0, None, vec![None, None, None]));
634 let cases = gen_test_cases!(
635 input,
636 (0, None, input.clone()),
638 (0, Some(0), vec![Some(""), None, Some("")]),
640 (1000, Some(0), vec![Some(""), None, Some("")]),
642 (-1000, None, input.clone()),
644 (0, Some(1000), input.clone())
646 );
647
648 do_test!(
649 [&base_case[..], &cases[..]].concat(),
650 GenericStringArray<O>,
651 substring
652 );
653 }
654
655 #[test]
656 fn with_nulls_string() {
657 with_nulls_generic_string::<i32>()
658 }
659
660 #[test]
661 fn with_nulls_large_string() {
662 with_nulls_generic_string::<i64>()
663 }
664
665 fn without_nulls_generic_string<O: OffsetSizeTrait>() {
666 let input = vec!["hello", "", "word"];
667 let base_case = gen_test_cases!(vec!["", "", ""], (0, None, vec!["", "", ""]));
669 let cases = gen_test_cases!(
670 input,
671 (0, None, input.clone()),
673 (1, None, vec!["ello", "", "ord"]),
674 (2, None, vec!["llo", "", "rd"]),
675 (3, None, vec!["lo", "", "d"]),
676 (10, None, vec!["", "", ""]),
677 (-1, None, vec!["o", "", "d"]),
679 (-2, None, vec!["lo", "", "rd"]),
680 (-3, None, vec!["llo", "", "ord"]),
681 (-10, None, input.clone()),
682 (1, Some(1), vec!["e", "", "o"]),
684 (1, Some(2), vec!["el", "", "or"]),
685 (1, Some(3), vec!["ell", "", "ord"]),
686 (1, Some(4), vec!["ello", "", "ord"]),
687 (-3, Some(1), vec!["l", "", "o"]),
688 (-3, Some(2), vec!["ll", "", "or"]),
689 (-3, Some(3), vec!["llo", "", "ord"]),
690 (-3, Some(4), vec!["llo", "", "ord"])
691 );
692
693 do_test!(
694 [&base_case[..], &cases[..]].concat(),
695 GenericStringArray<O>,
696 substring
697 );
698 }
699
700 #[test]
701 fn without_nulls_string() {
702 without_nulls_generic_string::<i32>()
703 }
704
705 #[test]
706 fn without_nulls_large_string() {
707 without_nulls_generic_string::<i64>()
708 }
709
710 fn generic_string_with_non_zero_offset<O: OffsetSizeTrait>() {
711 let values = b"hellotherearrow";
712 let offsets = &[
713 O::zero(),
714 O::from_usize(5).unwrap(),
715 O::from_usize(10).unwrap(),
716 O::from_usize(15).unwrap(),
717 ];
718 let bitmap = [0b101_u8];
720
721 let offsets = OffsetBuffer::new(Buffer::from_slice_ref(offsets).into());
722 let values = Buffer::from(values);
723 let nulls = Some(NullBuffer::new(BooleanBuffer::new(
724 Buffer::from(bitmap),
725 0,
726 3,
727 )));
728 let array = GenericStringArray::<O>::new(offsets, values, nulls).slice(1, 2);
730 let result = substring(&array, 1, None).unwrap();
732 let result = result
733 .as_any()
734 .downcast_ref::<GenericStringArray<O>>()
735 .unwrap();
736 let expected = GenericStringArray::<O>::from(vec![None, Some("rrow")]);
737 assert_eq!(result, &expected);
738 }
739
740 #[test]
741 fn string_with_non_zero_offset() {
742 generic_string_with_non_zero_offset::<i32>()
743 }
744
745 #[test]
746 fn large_string_with_non_zero_offset() {
747 generic_string_with_non_zero_offset::<i64>()
748 }
749
750 fn with_nulls_generic_string_by_char<O: OffsetSizeTrait>() {
751 let input = vec![Some("hello"), None, Some("Γ ⊢x:T")];
752 let base_case = gen_test_cases!(vec![None, None, None], (0, None, vec![None, None, None]));
754 let cases = gen_test_cases!(
755 input,
756 (0, None, input.clone()),
758 (0, Some(0), vec![Some(""), None, Some("")]),
760 (1000, Some(0), vec![Some(""), None, Some("")]),
762 (-1000, None, input.clone()),
764 (0, Some(1000), input.clone())
766 );
767
768 do_test!(
769 [&base_case[..], &cases[..]].concat(),
770 GenericStringArray<O>,
771 substring_by_char
772 );
773 }
774
775 #[test]
776 fn with_nulls_string_by_char() {
777 with_nulls_generic_string_by_char::<i32>()
778 }
779
780 #[test]
781 fn with_nulls_large_string_by_char() {
782 with_nulls_generic_string_by_char::<i64>()
783 }
784
785 fn without_nulls_generic_string_by_char<O: OffsetSizeTrait>() {
786 let input = vec!["hello", "", "Γ ⊢x:T"];
787 let base_case = gen_test_cases!(vec!["", "", ""], (0, None, vec!["", "", ""]));
789 let cases = gen_test_cases!(
790 input,
791 (0, None, input.clone()),
793 (1, None, vec!["ello", "", " ⊢x:T"]),
795 (2, None, vec!["llo", "", "⊢x:T"]),
796 (3, None, vec!["lo", "", "x:T"]),
797 (10, None, vec!["", "", ""]),
798 (-1, None, vec!["o", "", "T"]),
800 (-2, None, vec!["lo", "", ":T"]),
801 (-4, None, vec!["ello", "", "⊢x:T"]),
802 (-10, None, input.clone()),
803 (1, Some(1), vec!["e", "", " "]),
805 (1, Some(2), vec!["el", "", " ⊢"]),
806 (1, Some(3), vec!["ell", "", " ⊢x"]),
807 (1, Some(6), vec!["ello", "", " ⊢x:T"]),
808 (-4, Some(1), vec!["e", "", "⊢"]),
809 (-4, Some(2), vec!["el", "", "⊢x"]),
810 (-4, Some(3), vec!["ell", "", "⊢x:"]),
811 (-4, Some(4), vec!["ello", "", "⊢x:T"])
812 );
813
814 do_test!(
815 [&base_case[..], &cases[..]].concat(),
816 GenericStringArray<O>,
817 substring_by_char
818 );
819 }
820
821 #[test]
822 fn without_nulls_string_by_char() {
823 without_nulls_generic_string_by_char::<i32>()
824 }
825
826 #[test]
827 fn without_nulls_large_string_by_char() {
828 without_nulls_generic_string_by_char::<i64>()
829 }
830
831 fn generic_string_by_char_with_non_zero_offset<O: OffsetSizeTrait>() {
832 let values = "S→T = Πx:S.T";
833 let offsets = &[
834 O::zero(),
835 O::from_usize(values.char_indices().nth(3).map(|(pos, _)| pos).unwrap()).unwrap(),
836 O::from_usize(values.char_indices().nth(6).map(|(pos, _)| pos).unwrap()).unwrap(),
837 O::from_usize(values.len()).unwrap(),
838 ];
839 let bitmap = [0b101_u8];
841
842 let offsets = OffsetBuffer::new(Buffer::from_slice_ref(offsets).into());
843 let values = Buffer::from(values.as_bytes());
844 let nulls = Some(NullBuffer::new(BooleanBuffer::new(
845 Buffer::from(bitmap),
846 0,
847 3,
848 )));
849 let array = GenericStringArray::<O>::new(offsets, values, nulls).slice(1, 2);
851 let result = substring_by_char(&array, 1, None).unwrap();
853 let expected = GenericStringArray::<O>::from(vec![None, Some("x:S.T")]);
854 assert_eq!(result, expected);
855 }
856
857 #[test]
858 fn string_with_non_zero_offset_by_char() {
859 generic_string_by_char_with_non_zero_offset::<i32>()
860 }
861
862 #[test]
863 fn large_string_with_non_zero_offset_by_char() {
864 generic_string_by_char_with_non_zero_offset::<i64>()
865 }
866
867 #[test]
868 fn dictionary() {
869 _dictionary::<Int8Type>();
870 _dictionary::<Int16Type>();
871 _dictionary::<Int32Type>();
872 _dictionary::<Int64Type>();
873 _dictionary::<UInt8Type>();
874 _dictionary::<UInt16Type>();
875 _dictionary::<UInt32Type>();
876 _dictionary::<UInt64Type>();
877 }
878
879 fn _dictionary<K: ArrowDictionaryKeyType>() {
880 const TOTAL: i32 = 100;
881
882 let v = ["aaa", "bbb", "ccc", "ddd", "eee"];
883 let data: Vec<Option<&str>> = (0..TOTAL)
884 .map(|n| {
885 let i = n % 5;
886 if i == 3 { None } else { Some(v[i as usize]) }
887 })
888 .collect();
889
890 let dict_array: DictionaryArray<K> = data.clone().into_iter().collect();
891
892 let expected: Vec<Option<&str>> = data.iter().map(|opt| opt.map(|s| &s[1..3])).collect();
893
894 let res = substring(&dict_array, 1, Some(2)).unwrap();
895 let actual = res.as_any().downcast_ref::<DictionaryArray<K>>().unwrap();
896 let actual: Vec<Option<&str>> = actual
897 .values()
898 .as_any()
899 .downcast_ref::<GenericStringArray<i32>>()
900 .unwrap()
901 .take_iter(actual.keys_iter())
902 .collect();
903
904 for i in 0..TOTAL as usize {
905 assert_eq!(expected[i], actual[i],);
906 }
907 }
908
909 #[test]
910 fn check_invalid_array_type() {
911 let array = Int32Array::from(vec![Some(1), Some(2), Some(3)]);
912 let err = substring(&array, 0, None).unwrap_err().to_string();
913 assert!(err.contains("substring does not support type"));
914 }
915
916 #[test]
918 fn check_start_index() {
919 let array = StringArray::from(vec![Some("E=mc²"), Some("ascii")]);
920 let err = substring(&array, -1, None).unwrap_err().to_string();
921 assert!(err.contains("invalid utf-8 boundary"));
922 }
923
924 #[test]
925 fn check_length() {
926 let array = StringArray::from(vec![Some("E=mc²"), Some("ascii")]);
927 let err = substring(&array, 0, Some(5)).unwrap_err().to_string();
928 assert!(err.contains("invalid utf-8 boundary"));
929 }
930
931 #[test]
932 fn non_utf8_bytes() {
933 let bytes: &[u8] = &[0xE4, 0xBD, 0xA0, 0xE5, 0xA5, 0xBD, 0xE8, 0xAF, 0xAD];
935 let array = BinaryArray::from(vec![Some(bytes)]);
936 let arr = substring(&array, 0, Some(5)).unwrap();
937 let actual = arr.as_any().downcast_ref::<BinaryArray>().unwrap();
938
939 let expected_bytes: &[u8] = &[0xE4, 0xBD, 0xA0, 0xE5, 0xA5];
940 let expected = BinaryArray::from(vec![Some(expected_bytes)]);
941 assert_eq!(expected, *actual);
942 }
943}