1use arrow_buffer::ArrowNativeType;
25use lance_core::{Error, Result};
26
27use std::str::FromStr;
28
29use crate::compression::{BlockCompressor, BlockDecompressor};
30use crate::encodings::physical::binary::{BinaryBlockDecompressor, VariableEncoder};
31use crate::format::{
32 ProtobufUtils21,
33 pb21::{self, CompressiveEncoding},
34};
35use crate::{
36 buffer::LanceBuffer,
37 compression::VariablePerValueDecompressor,
38 data::{BlockInfo, DataBlock, VariableWidthBlock},
39 encodings::logical::primitive::fullzip::{PerValueCompressor, PerValueDataBlock},
40};
41
42#[derive(Debug, Clone, Copy, PartialEq)]
43pub struct CompressionConfig {
44 pub(crate) scheme: CompressionScheme,
45 pub(crate) level: Option<i32>,
46}
47
48impl CompressionConfig {
49 pub fn new(scheme: CompressionScheme, level: Option<i32>) -> Self {
51 Self { scheme, level }
52 }
53
54 pub fn scheme(&self) -> CompressionScheme {
56 self.scheme
57 }
58
59 pub fn level(&self) -> Option<i32> {
61 self.level
62 }
63}
64
65impl Default for CompressionConfig {
66 fn default() -> Self {
67 Self {
68 scheme: CompressionScheme::Lz4,
69 level: Some(0),
70 }
71 }
72}
73
74#[derive(Debug, Clone, Copy, PartialEq)]
75pub enum CompressionScheme {
76 None,
77 Fsst,
78 Zstd,
79 Lz4,
80}
81
82impl TryFrom<CompressionScheme> for pb21::CompressionScheme {
83 type Error = Error;
84
85 fn try_from(scheme: CompressionScheme) -> Result<Self> {
86 match scheme {
87 CompressionScheme::Lz4 => Ok(Self::CompressionAlgorithmLz4),
88 CompressionScheme::Zstd => Ok(Self::CompressionAlgorithmZstd),
89 _ => Err(Error::invalid_input(format!(
90 "Unsupported compression scheme: {:?}",
91 scheme
92 ))),
93 }
94 }
95}
96
97impl TryFrom<pb21::CompressionScheme> for CompressionScheme {
98 type Error = Error;
99
100 fn try_from(scheme: pb21::CompressionScheme) -> Result<Self> {
101 match scheme {
102 pb21::CompressionScheme::CompressionAlgorithmLz4 => Ok(Self::Lz4),
103 pb21::CompressionScheme::CompressionAlgorithmZstd => Ok(Self::Zstd),
104 _ => Err(Error::invalid_input(format!(
105 "Unsupported compression scheme: {:?}",
106 scheme
107 ))),
108 }
109 }
110}
111
112impl std::fmt::Display for CompressionScheme {
113 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
114 let scheme_str = match self {
115 Self::Fsst => "fsst",
116 Self::Zstd => "zstd",
117 Self::None => "none",
118 Self::Lz4 => "lz4",
119 };
120 write!(f, "{}", scheme_str)
121 }
122}
123
124impl FromStr for CompressionScheme {
125 type Err = Error;
126
127 fn from_str(s: &str) -> Result<Self> {
128 match s {
129 "none" => Ok(Self::None),
130 "fsst" => Ok(Self::Fsst),
131 "zstd" => Ok(Self::Zstd),
132 "lz4" => Ok(Self::Lz4),
133 _ => Err(Error::invalid_input(format!(
134 "Unknown compression scheme: {}",
135 s
136 ))),
137 }
138 }
139}
140
141pub trait BufferCompressor: std::fmt::Debug + Send + Sync {
142 fn compress(&self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<()>;
143 fn decompress(&self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<()>;
144 fn config(&self) -> CompressionConfig;
145}
146
147#[cfg(feature = "zstd")]
148mod zstd {
149 use std::io::{Cursor, Write};
150 use std::sync::{Mutex, OnceLock};
151
152 use super::*;
153
154 use ::zstd::bulk::{Compressor, decompress_to_buffer};
155 use ::zstd::stream::copy_decode;
156
157 pub struct ZstdBufferCompressor {
171 compression_level: i32,
172 compressor: OnceLock<std::result::Result<Mutex<Compressor<'static>>, String>>,
173 }
174
175 impl std::fmt::Debug for ZstdBufferCompressor {
176 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177 f.debug_struct("ZstdBufferCompressor")
178 .field("compression_level", &self.compression_level)
179 .finish()
180 }
181 }
182
183 impl ZstdBufferCompressor {
184 pub fn new(compression_level: i32) -> Self {
185 Self {
186 compression_level,
187 compressor: OnceLock::new(),
188 }
189 }
190
191 fn get_compressor(&self) -> Result<&Mutex<Compressor<'static>>> {
192 self.compressor
193 .get_or_init(|| {
194 Compressor::new(self.compression_level)
195 .map(Mutex::new)
196 .map_err(|e| e.to_string())
197 })
198 .as_ref()
199 .map_err(|e| Error::internal(format!("Failed to create zstd compressor: {}", e)))
200 }
201
202 fn is_raw_stream_format(&self, input_buf: &[u8]) -> bool {
204 if input_buf.len() < 8 {
205 return true; }
207 let mut magic_buf = [0u8; 4];
209 magic_buf.copy_from_slice(&input_buf[..4]);
210 let magic = u32::from_le_bytes(magic_buf);
211
212 const ZSTD_MAGIC_NUMBER: u32 = 0xFD2FB528;
214 if magic == ZSTD_MAGIC_NUMBER {
215 const FHD_BYTE_INDEX: usize = 4;
219 let fhd_byte = input_buf[FHD_BYTE_INDEX];
220 const FHD_RESERVED_BIT_MASK: u8 = 0b0001_0000;
221 let reserved_bit = fhd_byte & FHD_RESERVED_BIT_MASK;
222
223 if reserved_bit != 0 {
224 false
228 } else {
229 true
232 }
233 } else {
234 false
236 }
237 }
238
239 fn decompress_length_prefixed_zstd(
240 &self,
241 input_buf: &[u8],
242 output_buf: &mut Vec<u8>,
243 ) -> Result<()> {
244 const LENGTH_PREFIX_SIZE: usize = 8;
245 let mut len_buf = [0u8; LENGTH_PREFIX_SIZE];
246 len_buf.copy_from_slice(&input_buf[..LENGTH_PREFIX_SIZE]);
247
248 let uncompressed_len = u64::from_le_bytes(len_buf) as usize;
249
250 let start = output_buf.len();
251 output_buf.resize(start + uncompressed_len, 0);
252
253 let compressed_data = &input_buf[LENGTH_PREFIX_SIZE..];
254 decompress_to_buffer(compressed_data, &mut output_buf[start..])?;
255 Ok(())
256 }
257 }
258
259 impl BufferCompressor for ZstdBufferCompressor {
260 fn compress(&self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<()> {
261 output_buf.write_all(&(input_buf.len() as u64).to_le_bytes())?;
262
263 let max_compressed_size = ::zstd::zstd_safe::compress_bound(input_buf.len());
264 let start_pos = output_buf.len();
265 output_buf.resize(start_pos + max_compressed_size, 0);
266
267 let compressed_size = self
268 .get_compressor()?
269 .lock()
270 .unwrap()
271 .compress_to_buffer(input_buf, &mut output_buf[start_pos..])
272 .map_err(|e| Error::internal(format!("Zstd compression error: {}", e)))?;
273
274 output_buf.truncate(start_pos + compressed_size);
275 Ok(())
276 }
277
278 fn decompress(&self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<()> {
279 if input_buf.is_empty() {
280 return Ok(());
281 }
282
283 let is_raw_stream_format = self.is_raw_stream_format(input_buf);
284 if is_raw_stream_format {
285 copy_decode(Cursor::new(input_buf), output_buf)?;
286 } else {
287 self.decompress_length_prefixed_zstd(input_buf, output_buf)?;
288 }
289
290 Ok(())
291 }
292
293 fn config(&self) -> CompressionConfig {
294 CompressionConfig {
295 scheme: CompressionScheme::Zstd,
296 level: Some(self.compression_level),
297 }
298 }
299 }
300}
301
302#[cfg(feature = "lz4")]
303mod lz4 {
304 use super::*;
305
306 #[derive(Debug, Default)]
307 pub struct Lz4BufferCompressor {}
308
309 impl BufferCompressor for Lz4BufferCompressor {
310 fn compress(&self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<()> {
311 let start_pos = output_buf.len();
313
314 let max_size = ::lz4::block::compress_bound(input_buf.len())?;
316 output_buf.resize(start_pos + max_size + 4, 0);
318
319 let compressed_size = ::lz4::block::compress_to_buffer(
320 input_buf,
321 None,
322 true,
323 &mut output_buf[start_pos..],
324 )
325 .map_err(|err| Error::internal(format!("LZ4 compression error: {}", err)))?;
326
327 output_buf.truncate(start_pos + compressed_size);
329 Ok(())
330 }
331
332 fn decompress(&self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<()> {
333 if input_buf.len() < 4 {
336 return Err(Error::internal("LZ4 compressed data too short".to_string()));
337 }
338
339 let uncompressed_size =
341 u32::from_le_bytes([input_buf[0], input_buf[1], input_buf[2], input_buf[3]])
342 as usize;
343
344 let start_pos = output_buf.len();
346
347 output_buf.resize(start_pos + uncompressed_size, 0);
349
350 let decompressed_size =
352 ::lz4::block::decompress_to_buffer(input_buf, None, &mut output_buf[start_pos..])
353 .map_err(|err| Error::internal(format!("LZ4 decompression error: {}", err)))?;
354
355 output_buf.truncate(start_pos + decompressed_size);
357
358 Ok(())
359 }
360
361 fn config(&self) -> CompressionConfig {
362 CompressionConfig {
363 scheme: CompressionScheme::Lz4,
364 level: None,
365 }
366 }
367 }
368}
369
370#[derive(Debug, Default)]
371pub struct NoopBufferCompressor {}
372
373impl BufferCompressor for NoopBufferCompressor {
374 fn compress(&self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<()> {
375 output_buf.extend_from_slice(input_buf);
376 Ok(())
377 }
378
379 fn decompress(&self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<()> {
380 output_buf.extend_from_slice(input_buf);
381 Ok(())
382 }
383
384 fn config(&self) -> CompressionConfig {
385 CompressionConfig {
386 scheme: CompressionScheme::None,
387 level: None,
388 }
389 }
390}
391
392pub struct GeneralBufferCompressor {}
393
394impl GeneralBufferCompressor {
395 pub fn get_compressor(
396 compression_config: CompressionConfig,
397 ) -> Result<Box<dyn BufferCompressor>> {
398 match compression_config.scheme {
399 CompressionScheme::Fsst => Err(Error::invalid_input_source(
401 "fsst is not usable as a general buffer compressor".into(),
402 )),
403 CompressionScheme::Zstd => {
404 #[cfg(feature = "zstd")]
405 {
406 Ok(Box::new(zstd::ZstdBufferCompressor::new(
407 compression_config.level.unwrap_or(0),
408 )))
409 }
410 #[cfg(not(feature = "zstd"))]
411 {
412 Err(Error::invalid_input_source(
413 "package was not built with zstd support".into(),
414 ))
415 }
416 }
417 CompressionScheme::Lz4 => {
418 #[cfg(feature = "lz4")]
419 {
420 Ok(Box::new(lz4::Lz4BufferCompressor::default()))
421 }
422 #[cfg(not(feature = "lz4"))]
423 {
424 Err(Error::invalid_input_source(
425 "package was not built with lz4 support".into(),
426 ))
427 }
428 }
429 CompressionScheme::None => Ok(Box::new(NoopBufferCompressor {})),
430 }
431 }
432}
433
434#[derive(Debug)]
437pub struct GeneralBlockDecompressor {
438 inner: Box<dyn BlockDecompressor>,
439 compressor: Box<dyn BufferCompressor>,
440}
441
442impl GeneralBlockDecompressor {
443 pub fn try_new(
444 inner: Box<dyn BlockDecompressor>,
445 compression: CompressionConfig,
446 ) -> Result<Self> {
447 let compressor = GeneralBufferCompressor::get_compressor(compression)?;
448 Ok(Self { inner, compressor })
449 }
450}
451
452impl BlockDecompressor for GeneralBlockDecompressor {
453 fn decompress(&self, data: LanceBuffer, num_values: u64) -> Result<DataBlock> {
454 let mut decompressed = Vec::new();
455 self.compressor.decompress(&data, &mut decompressed)?;
456 self.inner
457 .decompress(LanceBuffer::from(decompressed), num_values)
458 }
459}
460
461#[derive(Debug)]
463pub struct CompressedBufferEncoder {
464 pub(crate) compressor: Box<dyn BufferCompressor>,
465}
466
467impl Default for CompressedBufferEncoder {
468 fn default() -> Self {
469 #[cfg(feature = "zstd")]
471 let (scheme, level) = (CompressionScheme::Zstd, Some(0));
472 #[cfg(all(feature = "lz4", not(feature = "zstd")))]
473 let (scheme, level) = (CompressionScheme::Lz4, None);
474 #[cfg(not(any(feature = "zstd", feature = "lz4")))]
475 let (scheme, level) = (CompressionScheme::None, None);
476
477 let compressor =
478 GeneralBufferCompressor::get_compressor(CompressionConfig { scheme, level }).unwrap();
479 Self { compressor }
480 }
481}
482
483impl CompressedBufferEncoder {
484 pub fn try_new(compression_config: CompressionConfig) -> Result<Self> {
485 let compressor = GeneralBufferCompressor::get_compressor(compression_config)?;
486 Ok(Self { compressor })
487 }
488
489 pub fn from_scheme(scheme: pb21::CompressionScheme) -> Result<Self> {
490 let scheme = CompressionScheme::try_from(scheme)?;
491 Ok(Self {
492 compressor: GeneralBufferCompressor::get_compressor(CompressionConfig {
493 scheme,
494 level: Some(0),
495 })?,
496 })
497 }
498}
499
500impl CompressedBufferEncoder {
501 pub fn per_value_compress<T: ArrowNativeType>(
502 &self,
503 data: &[u8],
504 offsets: &[T],
505 compressed: &mut Vec<u8>,
506 ) -> Result<LanceBuffer> {
507 let mut new_offsets: Vec<T> = Vec::with_capacity(offsets.len());
508 new_offsets.push(T::from_usize(0).unwrap());
509
510 for off in offsets.windows(2) {
511 let start = off[0].as_usize();
512 let end = off[1].as_usize();
513 self.compressor.compress(&data[start..end], compressed)?;
514 new_offsets.push(T::from_usize(compressed.len()).unwrap());
515 }
516
517 Ok(LanceBuffer::reinterpret_vec(new_offsets))
518 }
519
520 pub fn per_value_decompress<T: ArrowNativeType>(
521 &self,
522 data: &[u8],
523 offsets: &[T],
524 decompressed: &mut Vec<u8>,
525 ) -> Result<LanceBuffer> {
526 let mut new_offsets: Vec<T> = Vec::with_capacity(offsets.len());
527 new_offsets.push(T::from_usize(0).unwrap());
528
529 for off in offsets.windows(2) {
530 let start = off[0].as_usize();
531 let end = off[1].as_usize();
532 self.compressor
533 .decompress(&data[start..end], decompressed)?;
534 new_offsets.push(T::from_usize(decompressed.len()).unwrap());
535 }
536
537 Ok(LanceBuffer::reinterpret_vec(new_offsets))
538 }
539}
540
541impl PerValueCompressor for CompressedBufferEncoder {
542 fn compress(&self, data: DataBlock) -> Result<(PerValueDataBlock, CompressiveEncoding)> {
543 let data_type = data.name();
544 let data = data.as_variable_width().ok_or(Error::internal(format!(
545 "Attempt to use CompressedBufferEncoder on data of type {}",
546 data_type
547 )))?;
548
549 let data_bytes = &data.data;
550 let mut compressed = Vec::with_capacity(data_bytes.len());
551
552 let new_offsets = match data.bits_per_offset {
553 32 => self.per_value_compress::<u32>(
554 data_bytes,
555 &data.offsets.borrow_to_typed_slice::<u32>(),
556 &mut compressed,
557 )?,
558 64 => self.per_value_compress::<u64>(
559 data_bytes,
560 &data.offsets.borrow_to_typed_slice::<u64>(),
561 &mut compressed,
562 )?,
563 _ => unreachable!(),
564 };
565
566 let compressed = PerValueDataBlock::Variable(VariableWidthBlock {
567 bits_per_offset: data.bits_per_offset,
568 data: LanceBuffer::from(compressed),
569 offsets: new_offsets,
570 num_values: data.num_values,
571 block_info: BlockInfo::new(),
572 });
573
574 let encoding = ProtobufUtils21::wrapped(
577 self.compressor.config(),
578 ProtobufUtils21::variable(
579 ProtobufUtils21::flat(data.bits_per_offset as u64, None),
580 None,
581 ),
582 )?;
583
584 Ok((compressed, encoding))
585 }
586}
587
588impl VariablePerValueDecompressor for CompressedBufferEncoder {
589 fn decompress(&self, data: VariableWidthBlock) -> Result<DataBlock> {
590 let data_bytes = &data.data;
591 let mut decompressed = Vec::with_capacity(data_bytes.len() * 2);
592
593 let new_offsets = match data.bits_per_offset {
594 32 => self.per_value_decompress(
595 data_bytes,
596 &data.offsets.borrow_to_typed_slice::<u32>(),
597 &mut decompressed,
598 )?,
599 64 => self.per_value_decompress(
600 data_bytes,
601 &data.offsets.borrow_to_typed_slice::<u64>(),
602 &mut decompressed,
603 )?,
604 _ => unreachable!(),
605 };
606 Ok(DataBlock::VariableWidth(VariableWidthBlock {
607 bits_per_offset: data.bits_per_offset,
608 data: LanceBuffer::from(decompressed),
609 offsets: new_offsets,
610 num_values: data.num_values,
611 block_info: BlockInfo::new(),
612 }))
613 }
614}
615
616impl BlockCompressor for CompressedBufferEncoder {
617 fn compress(&self, data: DataBlock) -> Result<LanceBuffer> {
618 let encoded = match data {
619 DataBlock::FixedWidth(fixed_width) => fixed_width.data,
620 DataBlock::VariableWidth(variable_width) => {
621 let encoder = VariableEncoder::default();
623 BlockCompressor::compress(&encoder, DataBlock::VariableWidth(variable_width))?
624 }
625 _ => {
626 return Err(Error::invalid_input_source(
627 "Unsupported data block type".into(),
628 ));
629 }
630 };
631
632 let mut compressed = Vec::new();
633 self.compressor.compress(&encoded, &mut compressed)?;
634 Ok(LanceBuffer::from(compressed))
635 }
636}
637
638impl BlockDecompressor for CompressedBufferEncoder {
639 fn decompress(&self, data: LanceBuffer, num_values: u64) -> Result<DataBlock> {
640 let mut decompressed = Vec::new();
641 self.compressor.decompress(&data, &mut decompressed)?;
642
643 let inner_decoder = BinaryBlockDecompressor::default();
645 inner_decoder.decompress(LanceBuffer::from(decompressed), num_values)
646 }
647}
648
649#[cfg(test)]
650mod tests {
651 use super::*;
652 use std::str::FromStr;
653
654 use crate::encodings::physical::block::zstd::ZstdBufferCompressor;
655
656 #[test]
657 fn test_compression_scheme_from_str() {
658 assert_eq!(
659 CompressionScheme::from_str("none").unwrap(),
660 CompressionScheme::None
661 );
662 assert_eq!(
663 CompressionScheme::from_str("zstd").unwrap(),
664 CompressionScheme::Zstd
665 );
666 }
667
668 #[test]
669 fn test_compression_scheme_from_str_invalid() {
670 assert!(CompressionScheme::from_str("invalid").is_err());
671 }
672
673 #[cfg(feature = "zstd")]
674 mod zstd {
675 use std::io::Write;
676
677 use super::*;
678
679 #[test]
680 fn test_compress_zstd_with_length_prefixed() {
681 let compressor = ZstdBufferCompressor::new(0);
682 let input_data = b"Hello, world!";
683 let mut compressed_data = Vec::new();
684
685 compressor
686 .compress(input_data, &mut compressed_data)
687 .unwrap();
688 let mut decompressed_data = Vec::new();
689 compressor
690 .decompress(&compressed_data, &mut decompressed_data)
691 .unwrap();
692 assert_eq!(input_data, decompressed_data.as_slice());
693 }
694
695 #[test]
696 fn test_zstd_compress_decompress_multiple_times() {
697 let compressor = ZstdBufferCompressor::new(0);
698 let (input_data_1, input_data_2) = (b"Hello ", b"World");
699 let mut compressed_data = Vec::new();
700
701 compressor
702 .compress(input_data_1, &mut compressed_data)
703 .unwrap();
704 let compressed_length_1 = compressed_data.len();
705
706 compressor
707 .compress(input_data_2, &mut compressed_data)
708 .unwrap();
709
710 let mut decompressed_data = Vec::new();
711 compressor
712 .decompress(
713 &compressed_data[..compressed_length_1],
714 &mut decompressed_data,
715 )
716 .unwrap();
717
718 compressor
719 .decompress(
720 &compressed_data[compressed_length_1..],
721 &mut decompressed_data,
722 )
723 .unwrap();
724
725 assert_eq!(
727 decompressed_data.len(),
728 input_data_1.len() + input_data_2.len()
729 );
730 assert_eq!(
731 &decompressed_data[..input_data_1.len()],
732 input_data_1,
733 "First part of decompressed data should match input_1"
734 );
735 assert_eq!(
736 &decompressed_data[input_data_1.len()..],
737 input_data_2,
738 "Second part of decompressed data should match input_2"
739 );
740 }
741
742 #[test]
743 fn test_compress_zstd_raw_stream_format_and_decompress_with_length_prefixed() {
744 let compressor = ZstdBufferCompressor::new(0);
745 let input_data = b"Hello, world!";
746 let mut compressed_data = Vec::new();
747
748 let mut encoder = ::zstd::Encoder::new(&mut compressed_data, 0).unwrap();
750 encoder.write_all(input_data).unwrap();
751 encoder.finish().expect("failed to encode data with zstd");
752
753 let mut decompressed_data = Vec::new();
755 compressor
756 .decompress(&compressed_data, &mut decompressed_data)
757 .unwrap();
758 assert_eq!(input_data, decompressed_data.as_slice());
759 }
760 }
761
762 #[cfg(feature = "lz4")]
763 mod lz4 {
764 use std::{collections::HashMap, sync::Arc};
765
766 use arrow_schema::{DataType, Field};
767 use lance_datagen::array::{binary_prefix_plus_counter, utf8_prefix_plus_counter};
768
769 use super::*;
770
771 use crate::constants::DICT_SIZE_RATIO_META_KEY;
772 use crate::{
773 constants::{
774 COMPRESSION_META_KEY, DICT_DIVISOR_META_KEY, STRUCTURAL_ENCODING_FULLZIP,
775 STRUCTURAL_ENCODING_META_KEY,
776 },
777 encodings::physical::block::lz4::Lz4BufferCompressor,
778 testing::{FnArrayGeneratorProvider, TestCases, check_round_trip_encoding_generated},
779 };
780
781 #[test]
782 fn test_lz4_compress_decompress() {
783 let compressor = Lz4BufferCompressor::default();
784 let input_data = b"Hello, world!";
785 let mut compressed_data = Vec::new();
786
787 compressor
788 .compress(input_data, &mut compressed_data)
789 .unwrap();
790 let mut decompressed_data = Vec::new();
791 compressor
792 .decompress(&compressed_data, &mut decompressed_data)
793 .unwrap();
794 assert_eq!(input_data, decompressed_data.as_slice());
795 }
796
797 #[test_log::test(tokio::test)]
798 async fn test_lz4_compress_round_trip() {
799 for data_type in &[
800 DataType::Utf8,
801 DataType::LargeUtf8,
802 DataType::Binary,
803 DataType::LargeBinary,
804 ] {
805 let field = Field::new("", data_type.clone(), false);
806 let mut field_meta = HashMap::new();
807 field_meta.insert(COMPRESSION_META_KEY.to_string(), "lz4".to_string());
808 field_meta.insert(DICT_DIVISOR_META_KEY.to_string(), "100000".to_string());
811 field_meta.insert(DICT_SIZE_RATIO_META_KEY.to_string(), "0.0001".to_string());
812 field_meta.insert(
814 STRUCTURAL_ENCODING_META_KEY.to_string(),
815 STRUCTURAL_ENCODING_FULLZIP.to_string(),
816 );
817 let field = field.with_metadata(field_meta);
818 let test_cases = TestCases::basic()
819 .with_page_sizes(vec![1024 * 1024])
821 .with_expected_encoding("zstd")
822 .with_structural_encodings();
823
824 let datagen = Box::new(FnArrayGeneratorProvider::new(move || match data_type {
827 DataType::Utf8 => utf8_prefix_plus_counter("compressme", false),
828 DataType::Binary => {
829 binary_prefix_plus_counter(Arc::from(b"compressme".to_owned()), false)
830 }
831 DataType::LargeUtf8 => utf8_prefix_plus_counter("compressme", true),
832 DataType::LargeBinary => {
833 binary_prefix_plus_counter(Arc::from(b"compressme".to_owned()), true)
834 }
835 _ => panic!("Unsupported data type: {:?}", data_type),
836 }));
837
838 check_round_trip_encoding_generated(field, datagen, test_cases).await;
839 }
840 }
841 }
842}