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

alamb pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new c36e926c0c feat: Add support for `MapArray` in `arrow_row` (#9486)
c36e926c0c is described below

commit c36e926c0c8cee4ffefcd4eda96c6c11ac1a8632
Author: Raz Luvaton <[email protected]>
AuthorDate: Fri Jul 3 15:42:59 2026 +0300

    feat: Add support for `MapArray` in `arrow_row` (#9486)
    
    # Which issue does this PR close?
    
    - Closes #7879.
    
    # Rationale for this change
    
    Allow using arrow_row with more types
    
    # What changes are included in this PR?
    
    Reused code from list encoding for MapArray by adding some generics
    
    # Are these changes tested?
    
    Yes
    
    # Are there any user-facing changes?
    
    Yes, MapArray is now supported in row_format
    
    ---------
    
    Co-authored-by: Jeffrey Vo <[email protected]>
---
 arrow-row/src/lib.rs  | 1003 +++++++++++++++++++++++++++++++++++++++++++++----
 arrow-row/src/list.rs |  207 ++++++++--
 2 files changed, 1092 insertions(+), 118 deletions(-)

diff --git a/arrow-row/src/lib.rs b/arrow-row/src/lib.rs
index d02d29e4e9..83ebe79f72 100644
--- a/arrow-row/src/lib.rs
+++ b/arrow-row/src/lib.rs
@@ -334,11 +334,13 @@ mod variable;
 /// └───────┴───────────────┴───────┴─────────┴───────┘
 /// ```
 ///
-/// ## List Encoding
+/// ## List and Map Encoding
 ///
 /// Lists are encoded by first encoding all child elements to the row format.
 ///
-/// A list value is then encoded as the concatenation of each of the child 
elements,
+/// The Map encoding is the same with the only difference being that the child 
elements are key-value pairs.
+///
+/// A list/map value is then encoded as the concatenation of each of the child 
elements,
 /// separately encoded using the variable length encoding described above, 
followed
 /// by the variable length encoding of an empty byte array.
 ///
@@ -376,6 +378,41 @@ mod variable;
 ///
 /// With `[]` represented by an empty byte array, and `null` a null byte array.
 ///
+/// ### Map Equality
+///
+/// **Note:** Maps with different order of keys are not equal in row format
+/// i.e. row_format({"hello": 1, "world": 2}) != row_format({"world": 2, 
"hello": 1})
+///
+/// ```
+/// # use std::sync::Arc;
+/// # use arrow_row::{RowConverter, SortField};
+/// # use arrow_array::{Array, Int32Array, StringArray, MapArray};
+///
+/// // [{ "hello": 1, "world": 2 }, { "hey": 3, "you": 4 }]
+/// let map_1 = MapArray::from_vec_of_maps::<StringArray, Int32Array, _, 
_>(vec![
+///   Some(vec![("hello", Some(1)), ("world", Some(2))]),
+///   Some(vec![("hey", Some(3)), ("you", Some(4))]),
+/// ], false);
+/// // [{ "world": 2, "hello": 1 }, { "hey": 3, "you": 4 }]
+/// let map_2 = MapArray::from_vec_of_maps::<StringArray, Int32Array, _, 
_>(vec![
+///   Some(vec![("world", Some(2)), ("hello", Some(1))]),
+///   Some(vec![("hey", Some(3)), ("you", Some(4))]),
+/// ], false);
+///
+/// let converter = 
RowConverter::new(vec![SortField::new(map_1.data_type().clone())]).unwrap();
+///
+/// let map_1_rows = converter.convert_columns(&[Arc::new(map_1)]).unwrap();
+/// let map_2_rows = converter.convert_columns(&[Arc::new(map_2)]).unwrap();
+///
+/// // Attention! these maps are NOT equal since the order of the keys are 
different
+/// assert_ne!(map_1_rows.row(0), map_2_rows.row(0));
+///
+/// // These maps ARE equal since the order and the content of the keys and 
values are the same
+/// assert_eq!(map_1_rows.row(1), map_2_rows.row(1));
+/// ```
+///
+/// If you DO want to treat maps with different order of keys as the same in 
row format you should canonicalize them first.
+///
 /// ## Fixed Size List Encoding
 ///
 /// Fixed Size Lists are encoded by first encoding all child elements to the 
row format.
@@ -546,6 +583,8 @@ enum Codec {
     Struct(RowConverter, OwnedRow),
     /// A row converter for the child field
     List(RowConverter),
+    /// A row converter for the entries in map (keys and values and not the 
struct)
+    Map(RowConverter),
     /// A row converter for the values array of a run-end encoded array
     RunEndEncoded(RowConverter),
     /// Row converters for each union field (indexed by field position)
@@ -638,6 +677,33 @@ impl Codec {
                 let converter = RowConverter::new(vec![field])?;
                 Ok(Self::List(converter))
             }
+            DataType::Map(f, _) => {
+                // The encoded contents will be inverted if `descending` is 
set to true
+                // As such we set `descending` to false and negate nulls first 
if it
+                // `descending` set to true.
+                let options = SortOptions {
+                    descending: false,
+                    nulls_first: sort_field.options.nulls_first != 
sort_field.options.descending,
+                };
+
+                let DataType::Struct(fields) = f.data_type() else {
+                    return Err(ArrowError::InvalidArgumentError(format!(
+                        "expected struct field in map, got {:?}",
+                        f.data_type()
+                    )));
+                };
+
+                // For Map type we unwrap the intermediate struct type to 
avoid going through Struct codec to improve performance
+                let fields = fields
+                    .iter()
+                    .map(|struct_field| {
+                        
SortField::new_with_options(struct_field.data_type().clone(), options)
+                    })
+                    .collect::<Vec<_>>();
+                assert_eq!(fields.len(), 2);
+                let converter = RowConverter::new(fields)?;
+                Ok(Self::Map(converter))
+            }
             DataType::FixedSizeList(f, _) => {
                 let field = SortField::new_with_options(f.data_type().clone(), 
sort_field.options);
                 let converter = RowConverter::new(vec![field])?;
@@ -760,6 +826,22 @@ impl Codec {
                 let rows = converter.convert_columns(&[values])?;
                 Ok(Encoder::List(rows))
             }
+            Codec::Map(converter) => {
+                let map_array = as_map_array(array);
+
+                let first_offset = map_array.offsets()[0] as usize;
+                let last_offset = 
map_array.offsets()[map_array.offsets().len() - 1] as usize;
+
+                // entries can include more data than referenced in the 
MapArray, only encode
+                // the referenced entries.
+                let sliced_entries = map_array
+                    .entries()
+                    .slice(first_offset, last_offset - first_offset);
+
+                // the converter for the map is the keys and values and not 
the wrapping struct
+                let rows = 
converter.convert_columns(sliced_entries.columns())?;
+                Ok(Encoder::Map(rows))
+            }
             Codec::RunEndEncoded(converter) => {
                 let values = match array.data_type() {
                     DataType::RunEndEncoded(r, _) => match r.data_type() {
@@ -806,6 +888,7 @@ impl Codec {
             Codec::Dictionary(converter, nulls) => converter.size() + 
nulls.data.len(),
             Codec::Struct(converter, nulls) => converter.size() + 
nulls.data.len(),
             Codec::List(converter) => converter.size(),
+            Codec::Map(converter) => converter.size(),
             Codec::RunEndEncoded(converter) => converter.size(),
             Codec::Union(converters, _, null_rows) => {
                 converters.iter().map(|c| c.size()).sum::<usize>()
@@ -829,6 +912,8 @@ enum Encoder<'a> {
     Struct(Rows, Row<'a>),
     /// The row encoding of the child array
     List(Rows),
+    /// The row encoding of the map entries
+    Map(Rows),
     /// The row encoding of the values array
     RunEndEncoded(Rows),
     /// The row encoding of each union field's child array, type_ids buffer, 
offsets buffer (for Dense), and mode
@@ -896,7 +981,8 @@ impl RowConverter {
             | DataType::LargeList(f)
             | DataType::ListView(f)
             | DataType::LargeListView(f)
-            | DataType::FixedSizeList(f, _) => 
Self::supports_datatype(f.data_type()),
+            | DataType::FixedSizeList(f, _)
+            | DataType::Map(f, _) => Self::supports_datatype(f.data_type()),
             DataType::Struct(f) => f.iter().all(|x| 
Self::supports_datatype(x.data_type())),
             DataType::RunEndEncoded(_, values) => 
Self::supports_datatype(values.data_type()),
             DataType::Union(fs, _mode) => fs
@@ -1759,6 +1845,9 @@ fn row_lengths(cols: &[ArrayRef], encoders: &[Encoder]) 
-> LengthTracker {
                 ),
                 _ => unreachable!(),
             },
+            Encoder::Map(rows) => {
+                list::compute_lengths(tracker.materialized(), rows, 
as_map_array(array))
+            }
             Encoder::RunEndEncoded(rows) => match array.data_type() {
                 DataType::RunEndEncoded(r, _) => match r.data_type() {
                     DataType::Int16 => run::compute_lengths(
@@ -2000,6 +2089,7 @@ fn encode_column(
             }
             _ => unreachable!(),
         },
+        Encoder::Map(rows) => list::encode(data, offsets, rows, opts, 
as_map_array(column)),
         Encoder::RunEndEncoded(rows) => match column.data_type() {
             DataType::RunEndEncoded(r, _) => match r.data_type() {
                 DataType::Int16 => {
@@ -2148,12 +2238,12 @@ unsafe fn decode_column(
             })
         }
         Codec::List(converter) => match &field.data_type {
-            DataType::List(_) => {
-                Arc::new(unsafe { list::decode::<i32>(converter, rows, field, 
validate_utf8) }?)
-            }
-            DataType::LargeList(_) => {
-                Arc::new(unsafe { list::decode::<i64>(converter, rows, field, 
validate_utf8) }?)
-            }
+            DataType::List(_) => Arc::new(unsafe {
+                list::decode::<GenericListArray<i32>>(converter, rows, field, 
validate_utf8)
+            }?),
+            DataType::LargeList(_) => Arc::new(unsafe {
+                list::decode::<GenericListArray<i64>>(converter, rows, field, 
validate_utf8)
+            }?),
             DataType::ListView(_) => Arc::new(unsafe {
                 list::decode_list_view::<i32>(converter, rows, field, 
validate_utf8)
             }?),
@@ -2171,6 +2261,9 @@ unsafe fn decode_column(
             }?),
             _ => unreachable!(),
         },
+        Codec::Map(converter) => {
+            Arc::new(unsafe { list::decode::<MapArray>(converter, rows, field, 
validate_utf8) }?)
+        }
         Codec::RunEndEncoded(converter) => match &field.data_type {
             DataType::RunEndEncoded(run_ends, _) => match run_ends.data_type() 
{
                 DataType::Int16 => Arc::new(unsafe {
@@ -4130,6 +4223,337 @@ mod tests {
         assert_eq!(&back[1], &second);
     }
 
+    #[test]
+    fn test_single_map() {
+        let mut builder = MapBuilder::new(None, StringBuilder::new(), 
Int32Builder::new());
+        // Entry 0: {"hello": 1, "world": 2}
+        builder.keys().append_value("hello");
+        builder.values().append_value(1);
+        builder.keys().append_value("world");
+        builder.values().append_value(2);
+        builder.append(true).unwrap();
+
+        // Entry 1: {"foo": 3}
+        builder.keys().append_value("foo");
+        builder.values().append_value(3);
+        builder.append(true).unwrap();
+
+        // Entry 2: {} (empty map)
+        builder.append(true).unwrap();
+
+        // Entry 3: null (with masked data)
+        builder.keys().append_value("masked_key");
+        builder.values().append_value(999);
+        builder.append(false).unwrap();
+
+        // Entry 4: null (with unmasked data)
+        builder.append(false).unwrap();
+
+        // Entry 5: {"bar": null}
+        builder.keys().append_value("bar");
+        builder.values().append_null();
+        builder.append(true).unwrap();
+
+        // Entry 6: null (with different masked data)
+        builder.keys().append_value("other_masked");
+        builder.values().append_value(0);
+        builder.append(false).unwrap();
+
+        // Entry 7: {"a": 10, "b": 20, "c": 30}
+        builder.keys().append_value("a");
+        builder.values().append_value(10);
+        builder.keys().append_value("b");
+        builder.values().append_value(20);
+        builder.keys().append_value("c");
+        builder.values().append_value(30);
+        builder.append(true).unwrap();
+
+        let map = Arc::new(builder.finish()) as ArrayRef;
+        let d = map.data_type().clone();
+
+        let converter = 
RowConverter::new(vec![SortField::new(d.clone())]).unwrap();
+
+        let rows = converter.convert_columns(&[Arc::clone(&map)]).unwrap();
+
+        // null = null (different masked values or masking vs non-masking)
+        assert_eq!(rows.row(3), rows.row(4));
+        assert_eq!(rows.row(4), rows.row(6));
+
+        let back = converter.convert_rows(&rows).unwrap();
+        assert_eq!(back.len(), 1);
+        back[0].to_data().validate_full().unwrap();
+        assert_eq!(&back[0], &map);
+
+        let sliced_map = map.slice(1, map.len() - 2);
+        let rows_on_sliced = converter
+            .convert_columns(&[Arc::clone(&sliced_map)])
+            .unwrap();
+
+        let back = converter.convert_rows(&rows_on_sliced).unwrap();
+        assert_eq!(back.len(), 1);
+        back[0].to_data().validate_full().unwrap();
+        assert_eq!(&back[0], &sliced_map);
+    }
+
+    #[test]
+    fn two_maps_with_different_keys_order_should_sort_by_entry_order() {
+        let map_1: ArrayRef =
+            Arc::new(MapArray::from_vec_of_maps::<StringArray, Int32Array, _, 
_>(
+                vec![Some(vec![("hello", Some(1)), ("world", Some(2))])],
+                false,
+            ));
+        // { "world": 2, "hello": 1 }
+        let map_2: ArrayRef =
+            Arc::new(MapArray::from_vec_of_maps::<StringArray, Int32Array, _, 
_>(
+                vec![Some(vec![("world", Some(2)), ("hello", Some(1))])],
+                false,
+            ));
+
+        let converter = 
RowConverter::new(vec![SortField::new(map_1.data_type().clone())]).unwrap();
+
+        let map_1_rows = 
converter.convert_columns(&[Arc::clone(&map_1)]).unwrap();
+        let map_2_rows = 
converter.convert_columns(&[Arc::clone(&map_2)]).unwrap();
+
+        assert_ne!(map_1_rows.row(0), map_2_rows.row(0));
+        assert!(map_1_rows.row(0) < map_2_rows.row(0));
+
+        let back_1 = converter.convert_rows(&map_1_rows).unwrap();
+        let back_2 = converter.convert_rows(&map_2_rows).unwrap();
+        assert_eq!(&back_1[0], &map_1);
+        assert_eq!(&back_2[0], &map_2);
+    }
+
+    #[test]
+    fn test_nested_map() {
+        // Map<Utf8, Map<Utf8, Int32>>
+        let mut builder = MapBuilder::new(
+            None,
+            StringBuilder::new(),
+            MapBuilder::new(None, StringBuilder::new(), Int32Builder::new()),
+        );
+
+        // Entry 0: {"outer1": {"inner_a": 1, "inner_b": 2}, "outer2": 
{"inner_c": 3}}
+        builder.keys().append_value("outer1");
+        builder.values().keys().append_value("inner_a");
+        builder.values().values().append_value(1);
+        builder.values().keys().append_value("inner_b");
+        builder.values().values().append_value(2);
+        builder.values().append(true).unwrap();
+        builder.keys().append_value("outer2");
+        builder.values().keys().append_value("inner_c");
+        builder.values().values().append_value(3);
+        builder.values().append(true).unwrap();
+        builder.append(true).unwrap();
+
+        // Entry 1: {"x": {}} (inner map is empty)
+        builder.keys().append_value("x");
+        builder.values().append(true).unwrap();
+        builder.append(true).unwrap();
+
+        // Entry 2: {"y": null} (inner map is null)
+        builder.keys().append_value("y");
+        builder.values().keys().append_value("masked"); // MASKED
+        builder.values().values().append_value(0); // MASKED
+        builder.values().append(false).unwrap();
+        builder.append(true).unwrap();
+
+        // Entry 3: {"y": null} (inner map is non masked null)
+        builder.keys().append_value("y");
+        builder.values().append(false).unwrap(); // not masked
+        builder.append(true).unwrap();
+
+        // Entry 4: null (outer map is null)
+        builder.keys().append_value("masked_outer"); // MASKED
+        builder.values().keys().append_value("masked_inner"); // MASKED
+        builder.values().values().append_value(0); // MASKED
+        builder.values().append(true).unwrap(); // MASKED
+        builder.append(false).unwrap();
+
+        // Entry 5: null (outer map is null with inner map non masked null)
+        builder.keys().append_value("masked_outer"); // MASKED
+        builder.values().append(false).unwrap(); // not masked
+        builder.append(false).unwrap();
+
+        // Entry 6: non masked null (outer map is null)
+        builder.append(false).unwrap(); // not masked
+
+        // Entry 7: {} (outer map is empty)
+        builder.append(true).unwrap();
+
+        let map = Arc::new(builder.finish()) as ArrayRef;
+        let d = map.data_type().clone();
+
+        let converter = 
RowConverter::new(vec![SortField::new(d.clone())]).unwrap();
+
+        let rows = converter.convert_columns(&[Arc::clone(&map)]).unwrap();
+
+        let back = converter.convert_rows(&rows).unwrap();
+        assert_eq!(back.len(), 1);
+        back[0].to_data().validate_full().unwrap();
+        assert_eq!(&back[0], &map);
+
+        let sliced_map = map.slice(1, 3);
+        let rows_on_sliced = converter
+            .convert_columns(&[Arc::clone(&sliced_map)])
+            .unwrap();
+
+        let back = converter.convert_rows(&rows_on_sliced).unwrap();
+        assert_eq!(back.len(), 1);
+        back[0].to_data().validate_full().unwrap();
+        assert_eq!(&back[0], &sliced_map);
+    }
+
+    #[test]
+    fn test_single_map_with_non_nullable_values() {
+        // Use `with_values_field` on `MapBuilder` to set the values are not 
nullable
+        let value_field = Arc::new(Field::new("values", DataType::Int32, 
false));
+        let mut builder = MapBuilder::new(None, StringBuilder::new(), 
Int32Builder::new())
+            .with_values_field(value_field);
+        // Entry 0: {"a": 1, "b": 2}
+        builder.keys().append_value("a");
+        builder.values().append_value(1);
+        builder.keys().append_value("b");
+        builder.values().append_value(2);
+        builder.append(true).unwrap();
+        // Entry 1: null
+        builder.append(false).unwrap();
+        // Entry 2: {"c": 3}
+        builder.keys().append_value("c");
+        builder.values().append_value(3);
+        builder.append(true).unwrap();
+        // Entry 3: {}
+        builder.append(true).unwrap();
+        // Entry 4: null
+        builder.keys().append_value("masked"); // MASKED
+        builder.values().append_value(0); // MASKED
+        builder.append(false).unwrap();
+
+        let map = Arc::new(builder.finish()) as ArrayRef;
+        let d = map.data_type().clone();
+
+        let converter = 
RowConverter::new(vec![SortField::new(d.clone())]).unwrap();
+
+        let rows = converter.convert_columns(&[Arc::clone(&map)]).unwrap();
+
+        let back = converter.convert_rows(&rows).unwrap();
+        assert_eq!(back.len(), 1);
+        back[0].to_data().validate_full().unwrap();
+        assert_eq!(&back[0], &map);
+    }
+
+    #[test]
+    fn test_single_map_with_non_nullable_map_but_with_nullable_values() {
+        // Map column is non-nullable, but values are nullable
+        let value_field = Arc::new(Field::new("values", DataType::Int32, 
true));
+        let mut builder = MapBuilder::new(None, StringBuilder::new(), 
Int32Builder::new())
+            .with_values_field(value_field);
+
+        // Entry 0: {"a": 1, "b": null}
+        builder.keys().append_value("a");
+        builder.values().append_value(1);
+        builder.keys().append_value("b");
+        builder.values().append_null();
+        builder.append(true).unwrap();
+        // Entry 1: {"c": null, "d": null}
+        builder.keys().append_value("c");
+        builder.values().append_null();
+        builder.keys().append_value("d");
+        builder.values().append_null();
+        builder.append(true).unwrap();
+        // Entry 2: {}
+        builder.append(true).unwrap();
+        // Entry 3: {"e": 5}
+        builder.keys().append_value("e");
+        builder.values().append_value(5);
+        builder.append(true).unwrap();
+
+        let map = Arc::new(builder.finish()) as ArrayRef;
+        let d = map.data_type().clone();
+
+        let converter = 
RowConverter::new(vec![SortField::new(d.clone())]).unwrap();
+
+        let rows = converter.convert_columns(&[Arc::clone(&map)]).unwrap();
+
+        let back = converter.convert_rows(&rows).unwrap();
+        assert_eq!(back.len(), 1);
+        back[0].to_data().validate_full().unwrap();
+        assert_eq!(&back[0], &map);
+    }
+
+    #[test]
+    fn test_map_all_nulls() {
+        let mut builder = MapBuilder::new(None, StringBuilder::new(), 
Int32Builder::new());
+        // All entries are null
+        builder.keys().append_value("m1"); // MASKED
+        builder.values().append_value(1); // MASKED
+        builder.append(false).unwrap();
+        builder.keys().append_value("m2"); // MASKED
+        builder.values().append_value(2); // MASKED
+        builder.append(false).unwrap();
+
+        builder.append(false).unwrap(); // non masked
+
+        builder.keys().append_value("m3"); // MASKED
+        builder.values().append_value(3); // MASKED
+        builder.append(false).unwrap();
+
+        let map = Arc::new(builder.finish()) as ArrayRef;
+        let d = map.data_type().clone();
+
+        let converter = 
RowConverter::new(vec![SortField::new(d.clone())]).unwrap();
+
+        let rows = converter.convert_columns(&[Arc::clone(&map)]).unwrap();
+
+        // All null rows should be equal
+        rows.iter().for_each(|row| assert_eq!(row, rows.row(0)));
+
+        let back = converter.convert_rows(&rows).unwrap();
+        assert_eq!(back.len(), 1);
+        back[0].to_data().validate_full().unwrap();
+        assert_eq!(&back[0], &map);
+    }
+
+    #[test]
+    fn test_map_all_empty() {
+        let mut builder = MapBuilder::new(None, StringBuilder::new(), 
Int32Builder::new());
+        // All entries are empty maps
+        builder.append(true).unwrap();
+        builder.append(true).unwrap();
+        builder.append(true).unwrap();
+
+        let map = Arc::new(builder.finish()) as ArrayRef;
+        let d = map.data_type().clone();
+
+        let converter = 
RowConverter::new(vec![SortField::new(d.clone())]).unwrap();
+
+        let rows = converter.convert_columns(&[Arc::clone(&map)]).unwrap();
+
+        // All empty maps should be equal
+        rows.iter().for_each(|row| assert_eq!(row, rows.row(0)));
+
+        let back = converter.convert_rows(&rows).unwrap();
+        assert_eq!(back.len(), 1);
+        back[0].to_data().validate_full().unwrap();
+        assert_eq!(&back[0], &map);
+    }
+
+    #[test]
+    fn test_map_empty_array() {
+        // Zero-length map array
+        let builder = MapBuilder::new(None, StringBuilder::new(), 
Int32Builder::new());
+        let map = Arc::new(builder.finish_cloned()) as ArrayRef;
+        let d = map.data_type().clone();
+
+        let converter = 
RowConverter::new(vec![SortField::new(d.clone())]).unwrap();
+
+        let rows = converter.convert_columns(&[Arc::clone(&map)]).unwrap();
+
+        let back = converter.convert_rows(&rows).unwrap();
+        assert_eq!(back.len(), 1);
+        back[0].to_data().validate_full().unwrap();
+        assert_eq!(&back[0], &map);
+    }
+
     fn generate_primitive_array<K>(
         rng: &mut impl RngCore,
         len: usize,
@@ -4144,6 +4568,38 @@ mod tests {
             .collect()
     }
 
+    fn generate_all_unique_primitive_array<K>(
+        rng: &mut impl RngCore,
+        len: usize,
+    ) -> PrimitiveArray<K>
+    where
+        K: ArrowPrimitiveType,
+        K::Native: Hash + Eq,
+        StandardUniform: Distribution<K::Native>,
+    {
+        let possible_number_of_values = 
2i32.saturating_pow(size_of::<K::Native>() as u32 * 8);
+        assert!(
+            len <= possible_number_of_values as usize,
+            "len {len} is larger than the number of possible values 
{possible_number_of_values}"
+        );
+
+        let mut seen = std::collections::HashSet::new();
+        (0..len)
+            .map(|_| {
+                let mut value;
+                loop {
+                    value = rng.random();
+
+                    if seen.insert(value) {
+                        break;
+                    }
+                }
+
+                Some(value)
+            })
+            .collect()
+    }
+
     fn generate_boolean_array(
         rng: &mut impl RngCore,
         len: usize,
@@ -4351,6 +4807,76 @@ mod tests {
         )
     }
 
+    fn generate_map<R: RngCore, KeysFn, ValuesFn>(
+        rng: &mut R,
+        len: usize,
+        valid_percent: f64,
+        gen_keys: KeysFn,
+        gen_values: ValuesFn,
+    ) -> MapArray
+    where
+        KeysFn: FnOnce(&mut R, usize) -> ArrayRef,
+        ValuesFn: FnOnce(&mut R, usize) -> ArrayRef,
+    {
+        let offsets = OffsetBuffer::<i32>::from_lengths((0..len).map(|_| 
rng.random_range(0..10)));
+        let entries_len = offsets.last().unwrap().to_usize().unwrap();
+        let keys = gen_keys(rng, entries_len);
+        let values = gen_values(rng, entries_len);
+        let nulls = NullBuffer::from_iter((0..len).map(|_| 
rng.random_bool(valid_percent)));
+        let field = Arc::new(Field::new_map(
+            "",
+            "entries",
+            Field::new("keys", keys.data_type().clone(), false),
+            Field::new("values", values.data_type().clone(), true),
+            false,
+            true,
+        ));
+        let DataType::Map(struct_field, _) = field.data_type() else {
+            unreachable!();
+        };
+
+        let DataType::Struct(fields) = struct_field.data_type() else {
+            unreachable!();
+        };
+
+        let entries = StructArray::new(fields.clone(), vec![keys, values], 
None);
+
+        let map_array = MapArray::new(struct_field.clone(), offsets, entries, 
Some(nulls), false);
+
+        assert_valid_map(&map_array);
+
+        map_array
+    }
+
+    /// Assert that the map is valid; this includes unique map keys
+    ///
+    /// Can be removed once https://github.com/apache/arrow-rs/issues/9475 is 
resolved and `MapArray`
+    /// will not allow creation of invalid map arrays with duplicate keys.
+    ///
+    /// # Unique map keys
+    /// According to [arrow specification][1], the keys of a map must be 
unique, so this asserts that
+    ///
+    /// [1]: 
https://github.com/apache/arrow/blob/cbe2618431e413f12aa16aeba88b3a98914f194b/format/Schema.fbs#L124
+    fn assert_valid_map(array: &MapArray) {
+        let keys_arrow_row_converter =
+            
RowConverter::new(vec![SortField::new(array.key_type().clone())]).unwrap();
+
+        array.iter().enumerate().flat_map(|(index, entry)| entry.map(|entry| 
(index, Arc::clone(entry.column(0))))).for_each(|(entry_index, keys)| {
+            let keys_as_rows = 
keys_arrow_row_converter.convert_columns(&[Arc::clone(&keys)]).expect("should 
be able to convert keys");
+
+            for i in 0..keys_as_rows.num_rows() {
+                for j in (i + 1)..keys_as_rows.num_rows() {
+                    if keys_as_rows.row(i) == keys_as_rows.row(j) {
+                        let key_i = keys.slice(i, 1);
+                        let key_j = keys.slice(j, 1);
+
+                        assert_ne!(keys_as_rows.row(i), keys_as_rows.row(j), 
"map keys should be unique, but key {i} and key {j} are equal in entry 
{entry_index}. key {i} value is {key_i:?} and key {j} value is {key_j:?}");
+                    }
+                }
+            }
+        })
+    }
+
     fn generate_nulls(rng: &mut impl RngCore, len: usize) -> 
Option<NullBuffer> {
         Some(NullBuffer::from_iter(
             (0..len).map(|_| rng.random_bool(0.8)),
@@ -4449,6 +4975,55 @@ mod tests {
         GenericListArray::<O>::new(field, new_offsets, new_values, nulls)
     }
 
+    fn change_underline_null_values_for_map_array(array: &MapArray) -> 
MapArray {
+        let (field, offsets, entries, nulls, ordered) = 
array.clone().into_parts();
+        assert!(
+            !ordered,
+            "can't replace underlying null values for ordered map array as 
this can violate the ordering"
+        );
+
+        let (new_entries, new_offsets) = {
+            let concat_values = offsets
+                .windows(2)
+                .zip(nulls.as_ref().unwrap().iter())
+                .map(|(start_and_end, is_valid)| {
+                    let start = start_and_end[0].as_usize();
+                    let end = start_and_end[1].as_usize();
+                    if is_valid {
+                        return (start, end - start);
+                    }
+
+                    // If reached end, we take one less
+                    if end == entries.len() {
+                        (start, (end - start).saturating_sub(1))
+                    } else {
+                        // The keys may no longer be unique
+                        (start, end - start + 1)
+                    }
+                })
+                .map(|(start, length)| entries.slice(start, length))
+                .collect::<Vec<_>>();
+
+            let new_offsets = 
OffsetBuffer::from_lengths(concat_values.iter().map(|s| s.len()));
+
+            let new_values = {
+                let values = concat_values
+                    .iter()
+                    .map(|a| a as &dyn Array)
+                    .collect::<Vec<_>>();
+                arrow_select::concat::concat(&values).expect("should be able 
to concat")
+            };
+
+            (new_values.as_struct().clone(), new_offsets)
+        };
+
+        let new_map = MapArray::new(field, new_offsets, new_entries, nulls, 
ordered);
+
+        assert_valid_map(&new_map);
+
+        new_map
+    }
+
     fn change_underline_null_values(array: &ArrayRef) -> ArrayRef {
         if array.null_count() == 0 {
             return Arc::clone(array);
@@ -4479,6 +5054,9 @@ mod tests {
             DataType::LargeList(_) => {
                 
Arc::new(change_underline_null_values_for_list_array(array.as_list::<i64>()))
             }
+            DataType::Map(_, _) => {
+                
Arc::new(change_underline_null_values_for_map_array(array.as_map()))
+            }
             _ => {
                 Arc::clone(array)
             }
@@ -4486,7 +5064,7 @@ mod tests {
     }
 
     fn generate_column(rng: &mut (impl RngCore + Clone), len: usize) -> 
ArrayRef {
-        match rng.random_range(0..23) {
+        match rng.random_range(0..24) {
             0 => Arc::new(generate_primitive_array::<Int32Type>(rng, len, 
0.8)),
             1 => Arc::new(generate_primitive_array::<UInt32Type>(rng, len, 
0.8)),
             2 => Arc::new(generate_primitive_array::<Int64Type>(rng, len, 
0.8)),
@@ -4555,6 +5133,18 @@ mod tests {
                 })
                 .slice(500, len),
             ),
+            23 => Arc::new(generate_map(
+                rng,
+                len,
+                0.9,
+                // Need to generate all unique keys or make sure between each 
map every key is unique,
+                |rng, keys_len| {
+                    Arc::new(generate_all_unique_primitive_array::<Int64Type>(
+                        rng, keys_len,
+                    ))
+                },
+                |rng, values_len| Arc::new(generate_strings::<i32>(rng, 
values_len, 0.7)),
+            )),
             _ => unreachable!(),
         }
     }
@@ -4855,51 +5445,6 @@ mod tests {
         assert_eq!(rows.row(0).cmp(&rows.row(1)), Ordering::Less);
     }
 
-    #[test]
-    fn map_should_be_marked_as_unsupported() {
-        let map_data_type = Field::new_map(
-            "map",
-            "entries",
-            Field::new("key", DataType::Utf8, false),
-            Field::new("value", DataType::Utf8, true),
-            false,
-            true,
-        )
-        .data_type()
-        .clone();
-
-        let is_supported = 
RowConverter::supports_fields(&[SortField::new(map_data_type)]);
-
-        assert!(!is_supported, "Map should not be supported");
-    }
-
-    #[test]
-    fn should_fail_to_create_row_converter_for_unsupported_map_type() {
-        let map_data_type = Field::new_map(
-            "map",
-            "entries",
-            Field::new("key", DataType::Utf8, false),
-            Field::new("value", DataType::Utf8, true),
-            false,
-            true,
-        )
-        .data_type()
-        .clone();
-
-        let converter = RowConverter::new(vec![SortField::new(map_data_type)]);
-
-        match converter {
-            Err(ArrowError::NotYetImplemented(message)) => {
-                assert!(
-                    message.contains("Row format support not yet implemented 
for"),
-                    "Expected NotYetImplemented error for map data type, got: 
{message}",
-                );
-            }
-            Err(e) => panic!("Expected NotYetImplemented error, got: {e}"),
-            Ok(_) => panic!("Expected NotYetImplemented error for map data 
type"),
-        }
-    }
-
     #[test]
     fn test_values_buffer_smaller_when_utf8_validation_disabled() {
         fn get_values_buffer_len(col: ArrayRef) -> (usize, usize) {
@@ -5729,34 +6274,338 @@ mod tests {
         assert_eq!(&outer_struct, &back[0]);
     }
 
-    // Test Map<Null> - verify it's not supported (as per current 
implementation)
-    // https://github.com/apache/arrow-rs/issues/7879
+    // Test Map<Utf8, Null> with various combinations of nulls and empty maps
     #[test]
-    fn test_map_null_not_supported() {
-        // Map with Null values
-        let map_data_type = Field::new_map(
-            "map",
+    fn test_map_null_variations() {
+        // Map with Null values: [{a: NULL}, {}, {b: NULL, c: NULL}]
+        let keys = Arc::new(StringArray::from(vec!["a", "b", "c"])) as 
ArrayRef;
+        let null_values = Arc::new(NullArray::new(3)) as ArrayRef;
+
+        let offsets = OffsetBuffer::new(vec![0, 1, 1, 3].into());
+        let entries_fields = vec![
+            Arc::new(Field::new("keys", DataType::Utf8, false)),
+            Arc::new(Field::new("values", DataType::Null, true)),
+        ];
+        let struct_field = Arc::new(Field::new(
             "entries",
-            Field::new("key", DataType::Utf8, false),
-            Field::new("value", DataType::Null, true),
+            DataType::Struct(entries_fields.clone().into()),
             false,
-            true,
-        )
-        .data_type()
-        .clone();
+        ));
+        let entries = StructArray::new(entries_fields.into(), vec![keys, 
null_values], None);
 
-        // Currently Map is not supported by RowConverter
-        let result = RowConverter::new(vec![SortField::new(map_data_type)]);
-        assert!(
-            result.is_err(),
-            "Map should not be supported by RowConverter"
+        let map: ArrayRef = Arc::new(MapArray::new(
+            struct_field.clone(),
+            offsets,
+            entries,
+            None,
+            false,
+        ));
+
+        let converter = 
RowConverter::new(vec![SortField::new(map.data_type().clone())]).unwrap();
+        let rows = converter.convert_columns(&[Arc::clone(&map)]).unwrap();
+        let back = converter.convert_rows(&rows).unwrap();
+        assert_eq!(back.len(), 1);
+        back[0].to_data().validate_full().unwrap();
+        assert_eq!(&map, &back[0]);
+
+        // Map with Null values and null map entries: [{a: NULL}, null, {b: 
NULL, c: NULL}]
+        let keys = Arc::new(StringArray::from(vec!["a", "b", "c"])) as 
ArrayRef;
+        let null_values = Arc::new(NullArray::new(3)) as ArrayRef;
+
+        let offsets = OffsetBuffer::new(vec![0, 1, 1, 3].into());
+        let entries_fields = vec![
+            Arc::new(Field::new("keys", DataType::Utf8, false)),
+            Arc::new(Field::new("values", DataType::Null, true)),
+        ];
+        let struct_field = Arc::new(Field::new(
+            "entries",
+            DataType::Struct(entries_fields.clone().into()),
+            false,
+        ));
+        let entries = StructArray::new(entries_fields.into(), vec![keys, 
null_values], None);
+
+        let map: ArrayRef = Arc::new(MapArray::new(
+            struct_field.clone(),
+            offsets,
+            entries,
+            Some(vec![true, false, true].into()),
+            false,
+        ));
+
+        let rows = converter.convert_columns(&[Arc::clone(&map)]).unwrap();
+        let back = converter.convert_rows(&rows).unwrap();
+        assert_eq!(back.len(), 1);
+        back[0].to_data().validate_full().unwrap();
+        assert_eq!(&map, &back[0]);
+
+        // Empty map array with Null value type
+        let keys = Arc::new(StringArray::from(Vec::<&str>::new())) as ArrayRef;
+        let null_values = Arc::new(NullArray::new(0)) as ArrayRef;
+
+        let offsets = OffsetBuffer::new(vec![0i32].into());
+        let entries_fields = vec![
+            Arc::new(Field::new("keys", DataType::Utf8, false)),
+            Arc::new(Field::new("values", DataType::Null, true)),
+        ];
+        let struct_field = Arc::new(Field::new(
+            "entries",
+            DataType::Struct(entries_fields.clone().into()),
+            false,
+        ));
+        let entries = StructArray::new(entries_fields.into(), vec![keys, 
null_values], None);
+
+        let map: ArrayRef = Arc::new(MapArray::new(struct_field, offsets, 
entries, None, false));
+
+        let rows = converter.convert_columns(&[Arc::clone(&map)]).unwrap();
+        let back = converter.convert_rows(&rows).unwrap();
+        assert_eq!(back.len(), 1);
+        back[0].to_data().validate_full().unwrap();
+        assert_eq!(&map, &back[0]);
+    }
+
+    // Test Map<Utf8, Null> with descending order
+    #[test]
+    fn test_map_null_descending() {
+        // [{a: NULL}, {}, {b: NULL, c: NULL}]
+        let keys = Arc::new(StringArray::from(vec!["a", "b", "c"])) as 
ArrayRef;
+        let null_values = Arc::new(NullArray::new(3)) as ArrayRef;
+
+        let offsets = OffsetBuffer::new(vec![0, 1, 1, 3].into());
+        let entries_fields = vec![
+            Arc::new(Field::new("keys", DataType::Utf8, false)),
+            Arc::new(Field::new("values", DataType::Null, true)),
+        ];
+        let struct_field = Arc::new(Field::new(
+            "entries",
+            DataType::Struct(entries_fields.clone().into()),
+            false,
+        ));
+        let entries = StructArray::new(entries_fields.into(), vec![keys, 
null_values], None);
+
+        let map: ArrayRef = Arc::new(MapArray::new(struct_field, offsets, 
entries, None, false));
+
+        let options = SortOptions::default().with_descending(true);
+        let field = SortField::new_with_options(map.data_type().clone(), 
options);
+        let converter = RowConverter::new(vec![field]).unwrap();
+        let rows = converter.convert_columns(&[Arc::clone(&map)]).unwrap();
+        let back = converter.convert_rows(&rows).unwrap();
+        assert_eq!(back.len(), 1);
+        back[0].to_data().validate_full().unwrap();
+        assert_eq!(&map, &back[0]);
+    }
+
+    // Test Map<Null, Null> - both keys and values are Null type
+    #[test]
+    fn test_map_null_keys_and_null_values() {
+        let null_keys = Arc::new(NullArray::new(3)) as ArrayRef;
+        let null_values = Arc::new(NullArray::new(3)) as ArrayRef;
+
+        let offsets = OffsetBuffer::new(vec![0, 1, 1, 3].into());
+        let entries_fields = vec![
+            Arc::new(Field::new("keys", DataType::Null, true)),
+            Arc::new(Field::new("values", DataType::Null, true)),
+        ];
+        let struct_field = Arc::new(Field::new(
+            "entries",
+            DataType::Struct(entries_fields.clone().into()),
+            false,
+        ));
+        let entries = StructArray::new(entries_fields.into(), vec![null_keys, 
null_values], None);
+
+        let map: ArrayRef = Arc::new(MapArray::new(struct_field, offsets, 
entries, None, false));
+
+        let converter = 
RowConverter::new(vec![SortField::new(map.data_type().clone())]).unwrap();
+        let rows = converter.convert_columns(&[Arc::clone(&map)]).unwrap();
+        let back = converter.convert_rows(&rows).unwrap();
+        assert_eq!(back.len(), 1);
+        back[0].to_data().validate_full().unwrap();
+        assert_eq!(&map, &back[0]);
+    }
+
+    // Test Map<Utf8, Null> all empty maps
+    #[test]
+    fn test_map_null_all_empty() {
+        let keys = Arc::new(StringArray::from(Vec::<&str>::new())) as ArrayRef;
+        let null_values = Arc::new(NullArray::new(0)) as ArrayRef;
+
+        let offsets = OffsetBuffer::new(vec![0, 0, 0, 0].into());
+        let entries_fields = vec![
+            Arc::new(Field::new("keys", DataType::Utf8, false)),
+            Arc::new(Field::new("values", DataType::Null, true)),
+        ];
+        let struct_field = Arc::new(Field::new(
+            "entries",
+            DataType::Struct(entries_fields.clone().into()),
+            false,
+        ));
+        let entries = StructArray::new(entries_fields.into(), vec![keys, 
null_values], None);
+
+        let map: ArrayRef = Arc::new(MapArray::new(struct_field, offsets, 
entries, None, false));
+
+        let converter = 
RowConverter::new(vec![SortField::new(map.data_type().clone())]).unwrap();
+        let rows = converter.convert_columns(&[Arc::clone(&map)]).unwrap();
+
+        // All empty maps should be equal
+        assert_eq!(rows.row(0), rows.row(1));
+        assert_eq!(rows.row(1), rows.row(2));
+
+        let back = converter.convert_rows(&rows).unwrap();
+        assert_eq!(back.len(), 1);
+        back[0].to_data().validate_full().unwrap();
+        assert_eq!(&map, &back[0]);
+    }
+
+    // Test Map<Utf8, Map<Utf8, Null>> - nested map with Null leaf values
+    #[test]
+    fn test_nested_map_null() {
+        // Inner map entries: {a: NULL, b: NULL, c: NULL}
+        let inner_keys = Arc::new(StringArray::from(vec!["a", "b", "c"])) as 
ArrayRef;
+        let inner_null_values = Arc::new(NullArray::new(3)) as ArrayRef;
+
+        let inner_entries_fields = vec![
+            Arc::new(Field::new("keys", DataType::Utf8, false)),
+            Arc::new(Field::new("values", DataType::Null, true)),
+        ];
+        let inner_struct_field = Arc::new(Field::new(
+            "entries",
+            DataType::Struct(inner_entries_fields.clone().into()),
+            false,
+        ));
+        let inner_entries = StructArray::new(
+            inner_entries_fields.clone().into(),
+            vec![inner_keys, inner_null_values],
+            None,
         );
-        assert!(
-            result
-                .unwrap_err()
-                .to_string()
-                .contains("not yet implemented")
+
+        // Inner maps: [{a: NULL}, {b: NULL, c: NULL}]
+        let inner_map = Arc::new(MapArray::new(
+            inner_struct_field.clone(),
+            OffsetBuffer::new(vec![0, 1, 3].into()),
+            inner_entries,
+            None,
+            false,
+        )) as ArrayRef;
+
+        // Outer map entries
+        let outer_keys = Arc::new(StringArray::from(vec!["x", "y"])) as 
ArrayRef;
+
+        let inner_map_type = DataType::Map(inner_struct_field.clone(), false);
+        let outer_entries_fields = vec![
+            Arc::new(Field::new("keys", DataType::Utf8, false)),
+            Arc::new(Field::new("values", inner_map_type, true)),
+        ];
+        let outer_struct_field = Arc::new(Field::new(
+            "entries",
+            DataType::Struct(outer_entries_fields.clone().into()),
+            false,
+        ));
+        let outer_entries = StructArray::new(
+            outer_entries_fields.into(),
+            vec![outer_keys, inner_map],
+            None,
         );
+
+        // Outer map: [{x: {a: NULL}}, {y: {b: NULL, c: NULL}}]
+        let map: ArrayRef = Arc::new(MapArray::new(
+            outer_struct_field,
+            OffsetBuffer::new(vec![0, 1, 2].into()),
+            outer_entries,
+            None,
+            false,
+        ));
+
+        let converter = 
RowConverter::new(vec![SortField::new(map.data_type().clone())]).unwrap();
+        let rows = converter.convert_columns(&[Arc::clone(&map)]).unwrap();
+        let back = converter.convert_rows(&rows).unwrap();
+        assert_eq!(back.len(), 1);
+        back[0].to_data().validate_full().unwrap();
+        assert_eq!(&map, &back[0]);
+    }
+
+    // Test List<Map<Utf8, Null>> - list containing maps with Null values
+    #[test]
+    fn test_list_of_map_null() {
+        // Map entries: {a: NULL, b: NULL, c: NULL}
+        let keys = Arc::new(StringArray::from(vec!["a", "b", "c"])) as 
ArrayRef;
+        let null_values = Arc::new(NullArray::new(3)) as ArrayRef;
+
+        let entries_fields = vec![
+            Arc::new(Field::new("keys", DataType::Utf8, false)),
+            Arc::new(Field::new("values", DataType::Null, true)),
+        ];
+        let struct_field = Arc::new(Field::new(
+            "entries",
+            DataType::Struct(entries_fields.clone().into()),
+            false,
+        ));
+        let entries = StructArray::new(entries_fields.into(), vec![keys, 
null_values], None);
+
+        // Maps: [{a: NULL}, {}, {b: NULL, c: NULL}]
+        let map_array = Arc::new(MapArray::new(
+            struct_field.clone(),
+            OffsetBuffer::new(vec![0, 1, 1, 3].into()),
+            entries,
+            None,
+            false,
+        )) as ArrayRef;
+
+        let map_type = DataType::Map(struct_field, false);
+        // List of maps: [[{a: NULL}], [{}, {b: NULL, c: NULL}]]
+        let list: ArrayRef = Arc::new(ListArray::new(
+            Arc::new(Field::new_list_field(map_type, true)),
+            OffsetBuffer::new(vec![0, 1, 3].into()),
+            map_array,
+            None,
+        ));
+
+        let converter = 
RowConverter::new(vec![SortField::new(list.data_type().clone())]).unwrap();
+        let rows = converter.convert_columns(&[Arc::clone(&list)]).unwrap();
+        let back = converter.convert_rows(&rows).unwrap();
+        assert_eq!(&list, &back[0]);
+    }
+
+    // Test Map<Utf8, List<Null>> - map with list values containing Null
+    #[test]
+    fn test_map_of_list_null() {
+        // Inner list values: [NULL, NULL, NULL]
+        let null_array = Arc::new(NullArray::new(3)) as ArrayRef;
+        // Lists: [[NULL], [], [NULL, NULL]]
+        let list_array = Arc::new(ListArray::new(
+            Arc::new(Field::new_list_field(DataType::Null, true)),
+            OffsetBuffer::from_lengths(vec![1, 0, 2]),
+            null_array,
+            None,
+        )) as ArrayRef;
+
+        let keys = Arc::new(StringArray::from(vec!["a", "b", "c"])) as 
ArrayRef;
+
+        let list_type = list_array.data_type().clone();
+        let entries_fields = vec![
+            Arc::new(Field::new("keys", DataType::Utf8, false)),
+            Arc::new(Field::new("values", list_type, true)),
+        ];
+        let struct_field = Arc::new(Field::new(
+            "entries",
+            DataType::Struct(entries_fields.clone().into()),
+            false,
+        ));
+        let entries = StructArray::new(entries_fields.into(), vec![keys, 
list_array], None);
+
+        // Map: [{a: [NULL], b: [], c: [NULL, NULL]}]
+        let map: ArrayRef = Arc::new(MapArray::new(
+            struct_field,
+            OffsetBuffer::new(vec![0, 3].into()),
+            entries,
+            None,
+            false,
+        ));
+
+        let converter = 
RowConverter::new(vec![SortField::new(map.data_type().clone())]).unwrap();
+        let rows = converter.convert_columns(&[Arc::clone(&map)]).unwrap();
+        let back = converter.convert_rows(&rows).unwrap();
+        assert_eq!(back.len(), 1);
+        back[0].to_data().validate_full().unwrap();
+        assert_eq!(&map, &back[0]);
     }
 
     #[test]
diff --git a/arrow-row/src/list.rs b/arrow-row/src/list.rs
index f2d810d2c1..ec15192ab3 100644
--- a/arrow-row/src/list.rs
+++ b/arrow-row/src/list.rs
@@ -17,50 +17,143 @@
 
 use crate::{LengthTracker, RowConverter, Rows, SortField, fixed, 
null_sentinel};
 use arrow_array::{
-    Array, FixedSizeListArray, GenericListArray, GenericListViewArray, 
OffsetSizeTrait,
-    new_null_array,
+    Array, ArrayRef, FixedSizeListArray, GenericListArray, 
GenericListViewArray, MapArray,
+    OffsetSizeTrait, StructArray, new_null_array,
 };
 use arrow_buffer::{
     ArrowNativeType, BooleanBuffer, MutableBuffer, NullBuffer, OffsetBuffer, 
ScalarBuffer,
 };
-use arrow_schema::{ArrowError, DataType, SortOptions};
+use arrow_schema::{ArrowError, DataType, Fields, SortOptions};
 use std::{ops::Range, sync::Arc};
 
-pub fn compute_lengths<O: OffsetSizeTrait>(
+pub(crate) trait GenericListArrayOrMap: Array {
+    type Offset: OffsetSizeTrait;
+
+    fn offsets(&self) -> &[Self::Offset];
+
+    unsafe fn from_parts_unchecked(
+        data_type: DataType,
+        offsets: Vec<Self::Offset>,
+        children: Vec<ArrayRef>,
+        null_buffer: Option<NullBuffer>,
+    ) -> Self
+    where
+        Self: Sized;
+}
+
+impl<O: OffsetSizeTrait> GenericListArrayOrMap for GenericListArray<O> {
+    type Offset = O;
+
+    fn offsets(&self) -> &[Self::Offset] {
+        self.value_offsets()
+    }
+
+    unsafe fn from_parts_unchecked(
+        data_type: DataType,
+        offsets: Vec<Self::Offset>,
+        children: Vec<ArrayRef>,
+        null_buffer: Option<NullBuffer>,
+    ) -> Self
+    where
+        Self: Sized,
+    {
+        let field = match data_type {
+            DataType::List(inner_field) | DataType::LargeList(inner_field) => 
inner_field,
+            _ => unreachable!(),
+        };
+
+        let child = children
+            .into_iter()
+            .next()
+            .expect("List arrays must have exactly one child array");
+
+        // SAFETY: Caller must ensure offsets are valid and correctly 
correspond to the children and null buffer
+        // the benefit here is to avoid validating that the offsets are 
monotonically increasing
+        let offset_buffer = unsafe { 
OffsetBuffer::new_unchecked(ScalarBuffer::from(offsets)) };
+        GenericListArray::<Self::Offset>::new(field, offset_buffer, child, 
null_buffer)
+    }
+}
+
+impl GenericListArrayOrMap for MapArray {
+    type Offset = i32;
+
+    fn offsets(&self) -> &[Self::Offset] {
+        self.value_offsets()
+    }
+
+    unsafe fn from_parts_unchecked(
+        data_type: DataType,
+        offsets: Vec<Self::Offset>,
+        children: Vec<ArrayRef>,
+        null_buffer: Option<NullBuffer>,
+    ) -> Self
+    where
+        Self: Sized,
+    {
+        let DataType::Map(entries_field, ordered) = data_type else {
+            unreachable!("data type must be Map for MapArray");
+        };
+
+        assert_eq!(
+            children.len(),
+            2,
+            "Map arrays must have exactly two child arrays for keys and values"
+        );
+
+        let DataType::Struct(fields) = entries_field.data_type() else {
+            unreachable!("Map entry type must be Struct");
+        };
+
+        let entries = StructArray::new(
+            fields.clone(),
+            children,
+            // Entries StructArray cannot have NullBuffer since nulls are 
represented at the Map level
+            None,
+        );
+
+        // SAFETY: Caller must ensure offsets are valid and correctly 
correspond to the children and null buffer
+        // the benefit here is to avoid validating that the offsets are 
monotonically increasing
+        let offset_buffer = unsafe { 
OffsetBuffer::new_unchecked(ScalarBuffer::from(offsets)) };
+
+        MapArray::new(entries_field, offset_buffer, entries, null_buffer, 
ordered)
+    }
+}
+
+pub(crate) fn compute_lengths<L: GenericListArrayOrMap>(
     lengths: &mut [usize],
     rows: &Rows,
-    array: &GenericListArray<O>,
+    array: &L,
 ) {
-    let shift = array.value_offsets()[0].as_usize();
+    let shift = array.offsets()[0].as_usize();
 
     lengths
         .iter_mut()
-        .zip(array.value_offsets().windows(2))
+        .zip(array.offsets().windows(2))
         .enumerate()
         .for_each(|(idx, (length, offsets))| {
             let start = offsets[0].as_usize() - shift;
             let end = offsets[1].as_usize() - shift;
             let range = array.is_valid(idx).then_some(start..end);
-            *length += list_element_encoded_len(rows, range);
+            *length += list_like_element_encoded_len(rows, range);
         });
 }
 
-/// Encodes the provided `GenericListArray` to `out` with the provided 
`SortOptions`
+/// Encodes the provided [`GenericListArrayOrMap`] to `out` with the provided 
`SortOptions`
 ///
 /// `rows` should contain the encoded child elements
-pub fn encode<O: OffsetSizeTrait>(
+pub(crate) fn encode<L: GenericListArrayOrMap>(
     data: &mut [u8],
     offsets: &mut [usize],
     rows: &Rows,
     opts: SortOptions,
-    array: &GenericListArray<O>,
+    array: &L,
 ) {
-    let shift = array.value_offsets()[0].as_usize();
+    let shift = array.offsets()[0].as_usize();
 
     offsets
         .iter_mut()
         .skip(1)
-        .zip(array.value_offsets().windows(2))
+        .zip(array.offsets().windows(2))
         .enumerate()
         .for_each(|(idx, (offset, offsets))| {
             let start = offsets[0].as_usize() - shift;
@@ -98,19 +191,19 @@ fn encode_one(
 /// # Safety
 ///
 /// `rows` must contain valid data for the provided `converter`
-pub unsafe fn decode<O: OffsetSizeTrait>(
+pub(crate) unsafe fn decode<ListLikeImpl: GenericListArrayOrMap>(
     converter: &RowConverter,
     rows: &mut [&[u8]],
     field: &SortField,
     validate_utf8: bool,
-) -> Result<GenericListArray<O>, ArrowError> {
+) -> Result<ListLikeImpl, ArrowError> {
     let opts = field.options;
 
     let mut values_bytes = 0;
 
     let mut offset = 0;
     let mut offsets = Vec::with_capacity(rows.len() + 1);
-    offsets.push(O::usize_as(0));
+    offsets.push(ListLikeImpl::Offset::usize_as(0));
 
     for row in rows.iter_mut() {
         let mut row_offset = 0;
@@ -119,14 +212,14 @@ pub unsafe fn decode<O: OffsetSizeTrait>(
                 values_bytes += x.len();
             });
             if decoded <= 1 {
-                offsets.push(O::usize_as(offset));
+                offsets.push(ListLikeImpl::Offset::usize_as(offset));
                 break;
             }
             row_offset += decoded;
             offset += 1;
         }
     }
-    O::from_usize(offset).expect("overflow");
+    ListLikeImpl::Offset::from_usize(offset).expect("overflow");
 
     let nulls = crate::variable::decode_nulls_sentinel(rows, opts);
 
@@ -161,34 +254,66 @@ pub unsafe fn decode<O: OffsetSizeTrait>(
         })
         .collect();
 
-    let mut child = unsafe { converter.convert_raw(&mut child_rows, 
validate_utf8) }?;
-    assert_eq!(child.len(), 1);
-    let child = child.pop().unwrap();
+    let children = unsafe { converter.convert_raw(&mut child_rows, 
validate_utf8) }?;
 
     // Since RowConverter flattens certain data types (i.e. Dictionary),
     // we need to use updated data type instead of original field
-    let corrected_inner_field = match &field.data_type {
-        DataType::List(inner_field) => Arc::new(
-            inner_field
+    let corrected_type = match &field.data_type {
+        DataType::List(inner_field) => {
+            assert_eq!(children.len(), 1);
+            DataType::List(Arc::new(
+                inner_field
+                    .as_ref()
+                    .clone()
+                    .with_data_type(children[0].data_type().clone()),
+            ))
+        }
+        DataType::LargeList(inner_field) => {
+            assert_eq!(children.len(), 1);
+            DataType::LargeList(Arc::new(
+                inner_field
+                    .as_ref()
+                    .clone()
+                    .with_data_type(children[0].data_type().clone()),
+            ))
+        }
+        DataType::Map(inner_field, ordered) => {
+            let DataType::Struct(entries_field) = inner_field.data_type() else 
{
+                return Err(ArrowError::InvalidArgumentError(format!(
+                    "Expected Map entry type to be Struct, found: {}",
+                    inner_field.data_type()
+                )));
+            };
+            assert_eq!(
+                children.len(),
+                2,
+                "Map arrays must have exactly two child arrays for keys and 
values"
+            );
+            let key_field = entries_field[0]
                 .as_ref()
                 .clone()
-                .with_data_type(child.data_type().clone()),
-        ),
-        DataType::LargeList(inner_field) => Arc::new(
-            inner_field
+                .with_data_type(children[0].data_type().clone());
+            let value_field = entries_field[1]
                 .as_ref()
                 .clone()
-                .with_data_type(child.data_type().clone()),
-        ),
+                .with_data_type(children[1].data_type().clone());
+
+            let entries_fields = Fields::from(vec![key_field, value_field]);
+
+            DataType::Map(
+                Arc::new(
+                    inner_field
+                        .as_ref()
+                        .clone()
+                        .with_data_type(DataType::Struct(entries_fields)),
+                ),
+                *ordered,
+            )
+        }
         _ => unreachable!(),
     };
 
-    GenericListArray::try_new(
-        corrected_inner_field,
-        OffsetBuffer::new(offsets.into()),
-        child,
-        nulls,
-    )
+    Ok(unsafe { ListLikeImpl::from_parts_unchecked(corrected_type, offsets, 
children, nulls) })
 }
 
 pub fn compute_lengths_fixed_size_list(
@@ -305,13 +430,13 @@ pub unsafe fn decode_fixed_size_list(
     )
 }
 
-/// Computes the encoded length for a single list element given its child rows.
+/// Computes the encoded length for a single list/map element given its child 
rows.
 ///
-/// This is used by list types (List, LargeList, ListView, LargeListView) to 
determine
-/// the encoded length of a list element. For null elements, returns 1 (null 
sentinel only).
+/// This is used by list types (List, LargeList, ListView, LargeListView) and 
by Map to determine
+/// the encoded length of a list element/map entry. For null elements, returns 
1 (null sentinel only).
 /// For valid elements, returns 1 + the sum of padded lengths for each child 
row.
 #[inline]
-fn list_element_encoded_len(rows: &Rows, range: Option<Range<usize>>) -> usize 
{
+fn list_like_element_encoded_len(rows: &Rows, range: Option<Range<usize>>) -> 
usize {
     match range {
         None => 1,
         Some(range) => {
@@ -346,7 +471,7 @@ pub fn compute_lengths_list_view<O: OffsetSizeTrait>(
             };
             start..start + size
         });
-        *length += list_element_encoded_len(rows, range);
+        *length += list_like_element_encoded_len(rows, range);
     });
 }
 

Reply via email to