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

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


The following commit(s) were added to refs/heads/main by this push:
     new 37a9f27e65 GH-33032: [C#] Support fixed-size lists (#35716)
37a9f27e65 is described below

commit 37a9f27e658474a929f351c89c90159b401fb8ca
Author: Curt Hagenlocher <[email protected]>
AuthorDate: Mon Aug 21 13:23:45 2023 -0700

    GH-33032: [C#] Support fixed-size lists (#35716)
    
    ### What changes are included in this PR?
    
    Support fixed-size lists in the C# implementation.
    Adds Archery support for Lists, Structs and Fixed-size Lists in the C# 
implementation.
    
    ### Are these changes tested?
    
    Yes.
    
    ### Are there any user-facing changes?
    
    Fixed-size lists are now supported for C#.
    
    * Closes: #33032
    * Closes: #32886
    
    Authored-by: Curt Hagenlocher <[email protected]>
    Signed-off-by: David Li <[email protected]>
---
 .../Apache.Arrow/Arrays/ArrayDataConcatenator.cs   |  10 ++
 .../Apache.Arrow/Arrays/ArrayDataTypeComparer.cs   |  11 ++
 .../Arrays/ArrowArrayBuilderFactory.cs             |   2 +
 .../src/Apache.Arrow/Arrays/ArrowArrayFactory.cs   |   2 +
 csharp/src/Apache.Arrow/Arrays/DateArrayBuilder.cs |   2 +-
 .../Apache.Arrow/Arrays/DelegatingArrayBuilder.cs  |   6 +
 .../src/Apache.Arrow/Arrays/FixedSizeListArray.cs  | 190 +++++++++++++++++++++
 csharp/src/Apache.Arrow/Arrays/TimeArrayBuilder.cs |   2 +-
 csharp/src/Apache.Arrow/C/CArrowArrayImporter.cs   |  23 ++-
 csharp/src/Apache.Arrow/C/CArrowSchemaExporter.cs  |   2 +
 csharp/src/Apache.Arrow/C/CArrowSchemaImporter.cs  |  21 +++
 csharp/src/Apache.Arrow/Field.cs                   |  10 +-
 .../Apache.Arrow/Interfaces/IArrowArrayBuilder.cs  |   4 +-
 .../Apache.Arrow/Ipc/ArrowReaderImplementation.cs  |   2 +-
 csharp/src/Apache.Arrow/Ipc/ArrowStreamWriter.cs   |   8 +
 .../Apache.Arrow/Ipc/ArrowTypeFlatbufferBuilder.cs |   8 +
 csharp/src/Apache.Arrow/Ipc/MessageSerializer.cs   |  16 +-
 .../Types/{IArrowType.cs => FixedSizeListType.cs}  |  59 +++----
 csharp/src/Apache.Arrow/Types/IArrowType.cs        |   3 +-
 .../IntegrationCommand.cs                          |  80 ++++++++-
 .../test/Apache.Arrow.IntegrationTest/JsonFile.cs  |   3 +
 .../test/Apache.Arrow.Tests/ArrayBuilderTests.cs   | 126 ++++++++++++++
 .../test/Apache.Arrow.Tests/ArrayTypeComparer.cs   |  11 ++
 .../ArrowArrayConcatenatorTests.cs                 |  38 +++++
 .../test/Apache.Arrow.Tests/ArrowReaderVerifier.cs |  20 ++-
 .../CDataInterfacePythonTests.cs                   |  16 +-
 csharp/test/Apache.Arrow.Tests/TableTests.cs       |   2 +-
 csharp/test/Apache.Arrow.Tests/TestData.cs         |  28 +++
 dev/archery/archery/integration/datagen.py         |   6 +-
 docs/source/status.rst                             |   2 +-
 30 files changed, 643 insertions(+), 70 deletions(-)

diff --git a/csharp/src/Apache.Arrow/Arrays/ArrayDataConcatenator.cs 
b/csharp/src/Apache.Arrow/Arrays/ArrayDataConcatenator.cs
index 2b5e59517c..8859ecd7f0 100644
--- a/csharp/src/Apache.Arrow/Arrays/ArrayDataConcatenator.cs
+++ b/csharp/src/Apache.Arrow/Arrays/ArrayDataConcatenator.cs
@@ -48,6 +48,7 @@ namespace Apache.Arrow
             IArrowTypeVisitor<BinaryType>,
             IArrowTypeVisitor<StringType>,
             IArrowTypeVisitor<ListType>,
+            IArrowTypeVisitor<FixedSizeListType>,
             IArrowTypeVisitor<StructType>
         {
             public ArrayData Result { get; private set; }
@@ -100,6 +101,15 @@ namespace Apache.Arrow
                 Result = new ArrayData(type, _totalLength, _totalNullCount, 0, 
new ArrowBuffer[] { validityBuffer, offsetBuffer }, new[] { child });
             }
 
+            public void Visit(FixedSizeListType type)
+            {
+                CheckData(type, 1);
+                ArrowBuffer validityBuffer = ConcatenateValidityBuffer();
+                ArrayData child = Concatenate(SelectChildren(0), _allocator);
+
+                Result = new ArrayData(type, _totalLength, _totalNullCount, 0, 
new ArrowBuffer[] { validityBuffer }, new[] { child });
+            }
+
             public void Visit(StructType type)
             {
                 CheckData(type, 1);
diff --git a/csharp/src/Apache.Arrow/Arrays/ArrayDataTypeComparer.cs 
b/csharp/src/Apache.Arrow/Arrays/ArrayDataTypeComparer.cs
index 1e0524e53e..8a6bfed29a 100644
--- a/csharp/src/Apache.Arrow/Arrays/ArrayDataTypeComparer.cs
+++ b/csharp/src/Apache.Arrow/Arrays/ArrayDataTypeComparer.cs
@@ -26,6 +26,7 @@ namespace Apache.Arrow
         IArrowTypeVisitor<Time64Type>,
         IArrowTypeVisitor<FixedSizeBinaryType>,
         IArrowTypeVisitor<ListType>,
+        IArrowTypeVisitor<FixedSizeListType>,
         IArrowTypeVisitor<StructType>
     {
         private readonly IArrowType _expectedType;
@@ -102,6 +103,16 @@ namespace Apache.Arrow
             }
         }
 
+        public void Visit(FixedSizeListType actualType)
+        {
+            if (_expectedType is FixedSizeListType expectedType
+                && actualType.ListSize == expectedType.ListSize
+                && CompareNested(expectedType, actualType))
+            {
+                _dataTypeMatch = true;
+            }
+        }
+
         public void Visit(StructType actualType)
         {
             if (_expectedType is StructType expectedType
diff --git a/csharp/src/Apache.Arrow/Arrays/ArrowArrayBuilderFactory.cs 
b/csharp/src/Apache.Arrow/Arrays/ArrowArrayBuilderFactory.cs
index ef80edb838..1b972d0187 100644
--- a/csharp/src/Apache.Arrow/Arrays/ArrowArrayBuilderFactory.cs
+++ b/csharp/src/Apache.Arrow/Arrays/ArrowArrayBuilderFactory.cs
@@ -68,6 +68,8 @@ namespace Apache.Arrow
                     return new Time64Array.Builder(dataType as Time64Type);
                 case ArrowTypeId.List:
                     return new ListArray.Builder(dataType as ListType);
+                case ArrowTypeId.FixedSizeList:
+                    return new FixedSizeListArray.Builder(dataType as 
FixedSizeListType);
                 case ArrowTypeId.Decimal128:
                     return new Decimal128Array.Builder(dataType as 
Decimal128Type);
                 case ArrowTypeId.Decimal256:
diff --git a/csharp/src/Apache.Arrow/Arrays/ArrowArrayFactory.cs 
b/csharp/src/Apache.Arrow/Arrays/ArrowArrayFactory.cs
index 845cbbd3e5..f82037bff4 100644
--- a/csharp/src/Apache.Arrow/Arrays/ArrowArrayFactory.cs
+++ b/csharp/src/Apache.Arrow/Arrays/ArrowArrayFactory.cs
@@ -83,6 +83,8 @@ namespace Apache.Arrow
 #else
                     throw new NotSupportedException("Half-float arrays are not 
supported by this target framework.");
 #endif
+                case ArrowTypeId.FixedSizeList:
+                    return new FixedSizeListArray(data);
                 case ArrowTypeId.Interval:
                 case ArrowTypeId.Map:
                 default:
diff --git a/csharp/src/Apache.Arrow/Arrays/DateArrayBuilder.cs 
b/csharp/src/Apache.Arrow/Arrays/DateArrayBuilder.cs
index dcbb76930b..29246fa250 100644
--- a/csharp/src/Apache.Arrow/Arrays/DateArrayBuilder.cs
+++ b/csharp/src/Apache.Arrow/Arrays/DateArrayBuilder.cs
@@ -157,7 +157,7 @@ namespace Apache.Arrow
         /// Append a null date to the array.
         /// </summary>
         /// <returns>Returns the builder (for fluent-style 
composition).</returns>
-        public TBuilder AppendNull()
+        public override TBuilder AppendNull()
         {
             InnerBuilder.AppendNull();
             return this as TBuilder;
diff --git a/csharp/src/Apache.Arrow/Arrays/DelegatingArrayBuilder.cs 
b/csharp/src/Apache.Arrow/Arrays/DelegatingArrayBuilder.cs
index f2ab3ee13c..827b00c58f 100644
--- a/csharp/src/Apache.Arrow/Arrays/DelegatingArrayBuilder.cs
+++ b/csharp/src/Apache.Arrow/Arrays/DelegatingArrayBuilder.cs
@@ -98,5 +98,11 @@ namespace Apache.Arrow
             InnerBuilder.Clear();
             return this as TBuilder;
         }
+
+        /// <summary>
+        /// Appends a null value
+        /// </summary>
+        /// <returns>Returns the builder (for fluent-style 
composition).</returns>
+        public abstract TBuilder AppendNull();
     }
 }
diff --git a/csharp/src/Apache.Arrow/Arrays/FixedSizeListArray.cs 
b/csharp/src/Apache.Arrow/Arrays/FixedSizeListArray.cs
new file mode 100644
index 0000000000..f60daedb57
--- /dev/null
+++ b/csharp/src/Apache.Arrow/Arrays/FixedSizeListArray.cs
@@ -0,0 +1,190 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using Apache.Arrow.Memory;
+using Apache.Arrow.Types;
+
+namespace Apache.Arrow
+{
+    public class FixedSizeListArray : Array
+    {
+        public class Builder : IArrowArrayBuilder<FixedSizeListArray, Builder>
+        {
+            public IArrowArrayBuilder<IArrowArray, 
IArrowArrayBuilder<IArrowArray>> ValueBuilder { get; }
+
+            public int Length { get; protected set; }
+
+            private ArrowBuffer.BitmapBuilder ValidityBufferBuilder { get; }
+
+            public int NullCount => ValidityBufferBuilder.UnsetBitCount;
+
+            private FixedSizeListType DataType { get; }
+
+            private int ExpectedValueLength => Length * DataType.ListSize;
+
+            public Builder(IArrowType valueDataType, int listSize) : this(new 
FixedSizeListType(valueDataType, listSize))
+            {
+            }
+
+            public Builder(Field valueField, int listSize) : this(new 
FixedSizeListType(valueField, listSize))
+            {
+            }
+
+            internal Builder(FixedSizeListType dataType)
+            {
+                ValueBuilder = 
ArrowArrayBuilderFactory.Build(dataType.ValueDataType);
+                ValidityBufferBuilder = new ArrowBuffer.BitmapBuilder();
+                DataType = dataType;
+            }
+
+            /// <summary>
+            /// Start a new fixed-length list slot
+            ///
+            /// This function should be called before beginning to append 
elements to the
+            /// value builder
+            /// </summary>
+            /// <returns></returns>
+            public Builder Append()
+            {
+                ValidateChildLength();
+
+                ValidityBufferBuilder.Append(true);
+                Length++;
+
+                return this;
+            }
+
+            public Builder AppendNull()
+            {
+                ValidateChildLength();
+
+                ValidityBufferBuilder.Append(false);
+                for (int i = 0; i < DataType.ListSize; i++)
+                {
+                    ValueBuilder.AppendNull();
+                }
+                Length++;
+
+                return this;
+            }
+
+            public FixedSizeListArray Build(MemoryAllocator allocator = 
default)
+            {
+                ValidateChildLength();
+
+                int nullCount = NullCount;
+                ArrowBuffer validityBuffer = nullCount > 0
+                                        ? 
ValidityBufferBuilder.Build(allocator)
+                                        : ArrowBuffer.Empty;
+
+                return new FixedSizeListArray(DataType, Length,
+                    ValueBuilder.Build(allocator),
+                    validityBuffer, nullCount, 0);
+            }
+
+            public Builder Reserve(int capacity)
+            {
+                ValidityBufferBuilder.Reserve(capacity);
+                ValueBuilder.Reserve(DataType.ListSize * capacity);
+                return this;
+            }
+
+            public Builder Resize(int length)
+            {
+                ValidateChildLength();
+
+                ValidityBufferBuilder.Resize(length);
+                ValueBuilder.Resize(DataType.ListSize * length);
+                Length = length;
+                return this;
+            }
+
+            public Builder Clear()
+            {
+                ValueBuilder.Clear();
+                ValidityBufferBuilder.Clear();
+                Length = 0;
+                return this;
+            }
+
+            void ValidateChildLength()
+            {
+                if (ValueBuilder.Length != ExpectedValueLength)
+                {
+                    int actualLength = ValueBuilder.Length - 
ExpectedValueLength + DataType.ListSize;
+                    throw new ArgumentOutOfRangeException($"Lists of length: 
{actualLength} do not conform to the fixed size: " + DataType.ListSize);
+                }
+            }
+        }
+
+        public IArrowArray Values { get; }
+
+        public FixedSizeListArray(IArrowType dataType, int length,
+            IArrowArray values, ArrowBuffer nullBitmapBuffer,
+            int nullCount = 0, int offset = 0)
+            : this(new ArrayData(dataType, length, nullCount, offset,
+                new[] { nullBitmapBuffer }, new[] { values.Data }),
+                values)
+        {
+        }
+
+        public FixedSizeListArray(ArrayData data)
+            : this(data, ArrowArrayFactory.BuildArray(data.Children[0]))
+        {
+        }
+
+        private FixedSizeListArray(ArrayData data, IArrowArray values) : 
base(data)
+        {
+            data.EnsureBufferCount(1);
+            data.EnsureDataType(ArrowTypeId.FixedSizeList);
+            Values = values;
+        }
+
+        public override void Accept(IArrowArrayVisitor visitor) => 
Accept(this, visitor);
+
+        public IArrowArray GetSlicedValues(int index)
+        {
+            if (index < 0 || index >= Length)
+            {
+                throw new ArgumentOutOfRangeException(nameof(index));
+            }
+
+            if (IsNull(index))
+            {
+                return null;
+            }
+
+            if (!(Values is Array array))
+            {
+                return default;
+            }
+
+            index += Data.Offset;
+
+            int length = ((FixedSizeListType)Data.DataType).ListSize;
+            return array.Slice(index * length, length);
+        }
+
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing)
+            {
+                Values?.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+    }
+}
diff --git a/csharp/src/Apache.Arrow/Arrays/TimeArrayBuilder.cs 
b/csharp/src/Apache.Arrow/Arrays/TimeArrayBuilder.cs
index da93db8471..88f8aa8c45 100644
--- a/csharp/src/Apache.Arrow/Arrays/TimeArrayBuilder.cs
+++ b/csharp/src/Apache.Arrow/Arrays/TimeArrayBuilder.cs
@@ -88,7 +88,7 @@ namespace Apache.Arrow
         /// Append a null time to the array.
         /// </summary>
         /// <returns>Returns the builder (for fluent-style 
composition).</returns>
-        public TBuilder AppendNull()
+        public override TBuilder AppendNull()
         {
             InnerBuilder.AppendNull();
             return this as TBuilder;
diff --git a/csharp/src/Apache.Arrow/C/CArrowArrayImporter.cs 
b/csharp/src/Apache.Arrow/C/CArrowArrayImporter.cs
index 2f4ebed4b0..9b7bcb7abe 100644
--- a/csharp/src/Apache.Arrow/C/CArrowArrayImporter.cs
+++ b/csharp/src/Apache.Arrow/C/CArrowArrayImporter.cs
@@ -161,6 +161,10 @@ namespace Apache.Arrow.C
                         children = ProcessListChildren(cArray, 
((ListType)type).ValueDataType);
                         buffers = ImportListBuffers(cArray);
                         break;
+                    case ArrowTypeId.FixedSizeList:
+                        children = ProcessListChildren(cArray, 
((FixedSizeListType)type).ValueDataType);
+                        buffers = ImportFixedSizeListBuffers(cArray);
+                        break;
                     case ArrowTypeId.Struct:
                         children = ProcessStructChildren(cArray, 
((StructType)type).Fields);
                         buffers = new ArrowBuffer[] { 
ImportValidityBuffer(cArray) };
@@ -236,7 +240,7 @@ namespace Apache.Arrow.C
             {
                 if (cArray->n_buffers != 3)
                 {
-                    throw new InvalidOperationException("Byte arrays are 
expected to have exactly three child arrays");
+                    throw new InvalidOperationException("Byte arrays are 
expected to have exactly three buffers");
                 }
 
                 int length = checked((int)cArray->length);
@@ -256,7 +260,7 @@ namespace Apache.Arrow.C
             {
                 if (cArray->n_buffers != 2)
                 {
-                    throw new InvalidOperationException("List arrays are 
expected to have exactly two children");
+                    throw new InvalidOperationException("List arrays are 
expected to have exactly two buffers");
                 }
 
                 int length = checked((int)cArray->length);
@@ -269,11 +273,24 @@ namespace Apache.Arrow.C
                 return buffers;
             }
 
+            private ArrowBuffer[] ImportFixedSizeListBuffers(CArrowArray* 
cArray)
+            {
+                if (cArray->n_buffers != 1)
+                {
+                    throw new InvalidOperationException("Fixed-size list 
arrays are expected to have exactly one buffer");
+                }
+
+                ArrowBuffer[] buffers = new ArrowBuffer[1];
+                buffers[0] = ImportValidityBuffer(cArray);
+
+                return buffers;
+            }
+
             private ArrowBuffer[] ImportFixedWidthBuffers(CArrowArray* cArray, 
int bitWidth)
             {
                 if (cArray->n_buffers != 2)
                 {
-                    throw new InvalidOperationException("Arrays of fixed-width 
type are expected to have exactly two children");
+                    throw new InvalidOperationException("Arrays of fixed-width 
type are expected to have exactly two buffers");
                 }
 
                 // validity, data
diff --git a/csharp/src/Apache.Arrow/C/CArrowSchemaExporter.cs 
b/csharp/src/Apache.Arrow/C/CArrowSchemaExporter.cs
index 696212eda3..66142da331 100644
--- a/csharp/src/Apache.Arrow/C/CArrowSchemaExporter.cs
+++ b/csharp/src/Apache.Arrow/C/CArrowSchemaExporter.cs
@@ -167,6 +167,8 @@ namespace Apache.Arrow.C
                     return String.Format("ts{0}:{1}", 
FormatTimeUnit(timestampType.Unit), timestampType.Timezone);
                 // Nested
                 case ListType _: return "+l";
+                case FixedSizeListType fixedListType:
+                    return $"+w:{fixedListType.ListSize}";
                 case StructType _: return "+s";
                 // Dictionary
                 case DictionaryType dictionaryType:
diff --git a/csharp/src/Apache.Arrow/C/CArrowSchemaImporter.cs 
b/csharp/src/Apache.Arrow/C/CArrowSchemaImporter.cs
index ec613d59d2..2a750d5e82 100644
--- a/csharp/src/Apache.Arrow/C/CArrowSchemaImporter.cs
+++ b/csharp/src/Apache.Arrow/C/CArrowSchemaImporter.cs
@@ -200,6 +200,27 @@ namespace Apache.Arrow.C
 
                     return new StructType(childFields);
                 }
+                else if (format.StartsWith("+w:"))
+                {
+                    // Fixed-width list
+                    int width = Int32.Parse(format.Substring(3));
+
+                    if (_cSchema->n_children != 1)
+                    {
+                        throw new InvalidDataException("Expected fixed-length 
list type to have exactly one child.");
+                    }
+                    ImportedArrowSchema childSchema;
+                    if (_cSchema->GetChild(0) == null)
+                    {
+                        throw new InvalidDataException("Expected fixed-length 
list type child to be non-null.");
+                    }
+                    childSchema = new 
ImportedArrowSchema(_cSchema->GetChild(0), isRoot: false);
+
+                    Field childField = childSchema.GetAsField();
+
+                    return new FixedSizeListType(childField, width);
+                }
+
                 // TODO: Map type and large list type
 
                 // Decimals
diff --git a/csharp/src/Apache.Arrow/Field.cs b/csharp/src/Apache.Arrow/Field.cs
index 6e507b642f..562b9587bd 100644
--- a/csharp/src/Apache.Arrow/Field.cs
+++ b/csharp/src/Apache.Arrow/Field.cs
@@ -35,24 +35,24 @@ namespace Apache.Arrow
 
         public Field(string name, IArrowType dataType, bool nullable,
             IEnumerable<KeyValuePair<string, string>> metadata = default)
-            : this(name, dataType, nullable)
+            : this(name, dataType, nullable, false)
         {
             Metadata = metadata?.ToDictionary(kv => kv.Key, kv => kv.Value);
 
         }
 
         internal Field(string name, IArrowType dataType, bool nullable,
-            IReadOnlyDictionary<string, string> metadata, bool copyCollections)
-            : this(name, dataType, nullable)
+            IReadOnlyDictionary<string, string> metadata, bool 
copyCollections, bool allowBlankName)
+            : this(name, dataType, nullable, allowBlankName)
         {
             Debug.Assert(copyCollections == false, "This internal constructor 
is to not copy the collections.");
 
             Metadata = metadata;
         }
 
-        private Field(string name, IArrowType dataType, bool nullable)
+        private Field(string name, IArrowType dataType, bool nullable, bool 
allowBlankName)
         {
-            if (string.IsNullOrWhiteSpace(name))
+            if (name == null || (!allowBlankName && 
string.IsNullOrWhiteSpace(name)))
             {
                 throw new ArgumentNullException(nameof(name));
             }
diff --git a/csharp/src/Apache.Arrow/Interfaces/IArrowArrayBuilder.cs 
b/csharp/src/Apache.Arrow/Interfaces/IArrowArrayBuilder.cs
index 12f9ec1fd3..b18ff4581a 100644
--- a/csharp/src/Apache.Arrow/Interfaces/IArrowArrayBuilder.cs
+++ b/csharp/src/Apache.Arrow/Interfaces/IArrowArrayBuilder.cs
@@ -37,6 +37,7 @@ namespace Apache.Arrow
         TBuilder Reserve(int capacity);
         TBuilder Resize(int length);
         TBuilder Clear();
+        TBuilder AppendNull();
     }
 
 
@@ -47,8 +48,7 @@ namespace Apache.Arrow
         TBuilder Append(T value);
         TBuilder Append(ReadOnlySpan<T> span);
         TBuilder AppendRange(IEnumerable<T> values);
-        TBuilder AppendNull();
         TBuilder Swap(int i, int j);
         TBuilder Set(int index, T value);
     }
-}
\ No newline at end of file
+}
diff --git a/csharp/src/Apache.Arrow/Ipc/ArrowReaderImplementation.cs 
b/csharp/src/Apache.Arrow/Ipc/ArrowReaderImplementation.cs
index 6261696207..c9c1b21673 100644
--- a/csharp/src/Apache.Arrow/Ipc/ArrowReaderImplementation.cs
+++ b/csharp/src/Apache.Arrow/Ipc/ArrowReaderImplementation.cs
@@ -257,7 +257,7 @@ namespace Apache.Arrow.Ipc
             }
 
             ArrowBuffer[] arrowBuff;
-            if (field.DataType.TypeId == ArrowTypeId.Struct)
+            if (field.DataType.TypeId == ArrowTypeId.Struct || 
field.DataType.TypeId == ArrowTypeId.FixedSizeList)
             {
                 arrowBuff = new[] { nullArrowBuffer };
             }
diff --git a/csharp/src/Apache.Arrow/Ipc/ArrowStreamWriter.cs 
b/csharp/src/Apache.Arrow/Ipc/ArrowStreamWriter.cs
index ca567b67e9..a5d8db3f50 100644
--- a/csharp/src/Apache.Arrow/Ipc/ArrowStreamWriter.cs
+++ b/csharp/src/Apache.Arrow/Ipc/ArrowStreamWriter.cs
@@ -50,6 +50,7 @@ namespace Apache.Arrow.Ipc
             IArrowArrayVisitor<Time32Array>,
             IArrowArrayVisitor<Time64Array>,
             IArrowArrayVisitor<ListArray>,
+            IArrowArrayVisitor<FixedSizeListArray>,
             IArrowArrayVisitor<StringArray>,
             IArrowArrayVisitor<BinaryArray>,
             IArrowArrayVisitor<FixedSizeBinaryArray>,
@@ -111,6 +112,13 @@ namespace Apache.Arrow.Ipc
                 array.Values.Accept(this);
             }
 
+            public void Visit(FixedSizeListArray array)
+            {
+                _buffers.Add(CreateBuffer(array.NullBitmapBuffer));
+
+                array.Values.Accept(this);
+            }
+
             public void Visit(StringArray array) => Visit(array as 
BinaryArray);
 
             public void Visit(BinaryArray array)
diff --git a/csharp/src/Apache.Arrow/Ipc/ArrowTypeFlatbufferBuilder.cs 
b/csharp/src/Apache.Arrow/Ipc/ArrowTypeFlatbufferBuilder.cs
index befd87c2e0..203aa72d93 100644
--- a/csharp/src/Apache.Arrow/Ipc/ArrowTypeFlatbufferBuilder.cs
+++ b/csharp/src/Apache.Arrow/Ipc/ArrowTypeFlatbufferBuilder.cs
@@ -60,6 +60,7 @@ namespace Apache.Arrow.Ipc
             IArrowTypeVisitor<BinaryType>,
             IArrowTypeVisitor<TimestampType>,
             IArrowTypeVisitor<ListType>,
+            IArrowTypeVisitor<FixedSizeListType>,
             IArrowTypeVisitor<UnionType>,
             IArrowTypeVisitor<StructType>,
             IArrowTypeVisitor<Decimal128Type>,
@@ -110,6 +111,13 @@ namespace Apache.Arrow.Ipc
                     Flatbuf.List.EndList(Builder));
             }
 
+            public void Visit(FixedSizeListType type)
+            {
+                Result = FieldType.Build(
+                    Flatbuf.Type.FixedSizeList,
+                    Flatbuf.FixedSizeList.CreateFixedSizeList(Builder, 
type.ListSize));
+            }
+
             public void Visit(UnionType type)
             {
                 throw new NotImplementedException();
diff --git a/csharp/src/Apache.Arrow/Ipc/MessageSerializer.cs 
b/csharp/src/Apache.Arrow/Ipc/MessageSerializer.cs
index f5031fbfb5..8ca69b6116 100644
--- a/csharp/src/Apache.Arrow/Ipc/MessageSerializer.cs
+++ b/csharp/src/Apache.Arrow/Ipc/MessageSerializer.cs
@@ -59,7 +59,7 @@ namespace Apache.Arrow.Ipc
             for (int i = 0; i < schema.FieldsLength; i++)
             {
                 Flatbuf.Field field = schema.Fields(i).GetValueOrDefault();
-                fields.Add(FieldFromFlatbuffer(field, ref dictionaryMemo));
+                fields.Add(FieldFromFlatbuffer(field, ref dictionaryMemo, 
allowBlankName: false));
             }
 
             Dictionary<string, string> metadata = schema.CustomMetadataLength 
> 0 ? new Dictionary<string, string>() : null;
@@ -73,13 +73,14 @@ namespace Apache.Arrow.Ipc
             return new Schema(fields, metadata, copyCollections: false);
         }
 
-        private static Field FieldFromFlatbuffer(Flatbuf.Field flatbufField, 
ref DictionaryMemo dictionaryMemo)
+        private static Field FieldFromFlatbuffer(Flatbuf.Field flatbufField, 
ref DictionaryMemo dictionaryMemo, bool allowBlankName)
         {
+            bool allowBlankNameChild = flatbufField.ChildrenLength == 1 && 
flatbufField.TypeType == Flatbuf.Type.FixedSizeList;
             Field[] childFields = flatbufField.ChildrenLength > 0 ? new 
Field[flatbufField.ChildrenLength] : null;
             for (int i = 0; i < flatbufField.ChildrenLength; i++)
             {
                 Flatbuf.Field? childFlatbufField = flatbufField.Children(i);
-                childFields[i] = FieldFromFlatbuffer(childFlatbufField.Value, 
ref dictionaryMemo);
+                childFields[i] = FieldFromFlatbuffer(childFlatbufField.Value, 
ref dictionaryMemo, allowBlankNameChild);
             }
 
             Flatbuf.DictionaryEncoding? dictionaryEncoding = 
flatbufField.Dictionary;
@@ -103,7 +104,7 @@ namespace Apache.Arrow.Ipc
                 metadata[keyValue.Key] = keyValue.Value;
             }
 
-            var arrowField = new Field(flatbufField.Name, type, 
flatbufField.Nullable, metadata, copyCollections: false);
+            var arrowField = new Field(flatbufField.Name, type, 
flatbufField.Nullable, metadata, copyCollections: false, allowBlankName);
 
             if (dictionaryEncoding.HasValue)
             {
@@ -192,6 +193,13 @@ namespace Apache.Arrow.Ipc
                         throw new InvalidDataException($"List type must have 
exactly one child.");
                     }
                     return new Types.ListType(childFields[0]);
+                case Flatbuf.Type.FixedSizeList:
+                    if (childFields == null || childFields.Length != 1)
+                    {
+                        throw new InvalidDataException($"Fixed-size list type 
must have exactly one child.");
+                    }
+                    Flatbuf.FixedSizeList fixedSizeListMetadata = 
field.Type<Flatbuf.FixedSizeList>().Value;
+                    return new Types.FixedSizeListType(childFields[0], 
fixedSizeListMetadata.ListSize);
                 case Flatbuf.Type.Struct_:
                     Debug.Assert(childFields != null);
                     return new Types.StructType(childFields);
diff --git a/csharp/src/Apache.Arrow/Types/IArrowType.cs 
b/csharp/src/Apache.Arrow/Types/FixedSizeListType.cs
similarity index 50%
copy from csharp/src/Apache.Arrow/Types/IArrowType.cs
copy to csharp/src/Apache.Arrow/Types/FixedSizeListType.cs
index 15c9a02445..5a4c70bf3a 100644
--- a/csharp/src/Apache.Arrow/Types/IArrowType.cs
+++ b/csharp/src/Apache.Arrow/Types/FixedSizeListType.cs
@@ -13,51 +13,32 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+using System;
 
 namespace Apache.Arrow.Types
 {
-    public enum ArrowTypeId
+    public sealed class FixedSizeListType : NestedType
     {
-        Null,
-        Boolean,
-        UInt8,
-        Int8,
-        UInt16,
-        Int16,
-        UInt32,
-        Int32,
-        UInt64,
-        Int64,
-        HalfFloat,
-        Float,
-        Double,
-        String,
-        Binary,
-        FixedSizedBinary,
-        Date32,
-        Date64,
-        Timestamp,
-        Time32,
-        Time64,
-        Interval,
-        Decimal128,
-        Decimal256,
-        List,
-        Struct,
-        Union,
-        Dictionary,
-        Map
-    }
+        public override ArrowTypeId TypeId => ArrowTypeId.FixedSizeList;
+        public override string Name => "fixed_size_list";
+        public int ListSize { get; }
 
-    public interface IArrowType
-    {
-        ArrowTypeId TypeId { get; }
+        public Field ValueField => Fields[0];
+
+        public IArrowType ValueDataType => Fields[0].DataType;
+
+        public FixedSizeListType(Field valueField, int listSize)
+           : base(valueField)
+        {
+            if (listSize <= 0)
+                throw new ArgumentOutOfRangeException(nameof(listSize));
+
+            ListSize = listSize;
+        }
 
-        string Name { get; }
- 
-        void Accept(IArrowTypeVisitor visitor);
+        public FixedSizeListType(IArrowType valueDataType, int listSize)
+            : this(new Field("item", valueDataType, true), listSize) { }
 
-        bool IsFixedWidth { get; }
-    
+        public override void Accept(IArrowTypeVisitor visitor) => Accept(this, 
visitor);
     }
 }
diff --git a/csharp/src/Apache.Arrow/Types/IArrowType.cs 
b/csharp/src/Apache.Arrow/Types/IArrowType.cs
index 15c9a02445..447db47329 100644
--- a/csharp/src/Apache.Arrow/Types/IArrowType.cs
+++ b/csharp/src/Apache.Arrow/Types/IArrowType.cs
@@ -46,7 +46,8 @@ namespace Apache.Arrow.Types
         Struct,
         Union,
         Dictionary,
-        Map
+        Map,
+        FixedSizeList,
     }
 
     public interface IArrowType
diff --git a/csharp/test/Apache.Arrow.IntegrationTest/IntegrationCommand.cs 
b/csharp/test/Apache.Arrow.IntegrationTest/IntegrationCommand.cs
index 5b1ed98993..abf7451e5e 100644
--- a/csharp/test/Apache.Arrow.IntegrationTest/IntegrationCommand.cs
+++ b/csharp/test/Apache.Arrow.IntegrationTest/IntegrationCommand.cs
@@ -149,8 +149,20 @@ namespace Apache.Arrow.IntegrationTest
 
         private static void CreateField(Field.Builder builder, JsonField 
jsonField)
         {
+            Field[] children = null;
+            if (jsonField.Children?.Count > 0)
+            {
+                children = new Field[jsonField.Children.Count];
+                for (int i = 0; i < jsonField.Children.Count; i++)
+                {
+                    Field.Builder field = new Field.Builder();
+                    CreateField(field, jsonField.Children[i]);
+                    children[i] = field.Build();
+                }
+            }
+
             builder.Name(jsonField.Name)
-                .DataType(ToArrowType(jsonField.Type))
+                .DataType(ToArrowType(jsonField.Type, children))
                 .Nullable(jsonField.Nullable);
 
             if (jsonField.Metadata != null)
@@ -159,7 +171,7 @@ namespace Apache.Arrow.IntegrationTest
             }
         }
 
-        private static IArrowType ToArrowType(JsonArrowType type)
+        private static IArrowType ToArrowType(JsonArrowType type, Field[] 
children)
         {
             return type.Name switch
             {
@@ -173,6 +185,9 @@ namespace Apache.Arrow.IntegrationTest
                 "date" => ToDateArrowType(type),
                 "time" => ToTimeArrowType(type),
                 "timestamp" => ToTimestampArrowType(type),
+                "list" => ToListArrowType(type, children),
+                "fixedsizelist" => ToFixedSizeListArrowType(type, children),
+                "struct" => ToStructArrowType(type, children),
                 "null" => NullType.Default,
                 _ => throw new NotSupportedException($"JsonArrowType not 
supported: {type.Name}")
             };
@@ -251,6 +266,21 @@ namespace Apache.Arrow.IntegrationTest
             };
         }
 
+        private static IArrowType ToListArrowType(JsonArrowType type, Field[] 
children)
+        {
+            return new ListType(children[0]);
+        }
+
+        private static IArrowType ToFixedSizeListArrowType(JsonArrowType type, 
Field[] children)
+        {
+            return new FixedSizeListType(children[0], type.ListSize);
+        }
+
+        private static IArrowType ToStructArrowType(JsonArrowType type, 
Field[] children)
+        {
+            return new StructType(children);
+        }
+
         private class ArrayCreator :
             IArrowTypeVisitor<BooleanType>,
             IArrowTypeVisitor<Int8Type>,
@@ -274,10 +304,11 @@ namespace Apache.Arrow.IntegrationTest
             IArrowTypeVisitor<BinaryType>,
             IArrowTypeVisitor<FixedSizeBinaryType>,
             IArrowTypeVisitor<ListType>,
+            IArrowTypeVisitor<FixedSizeListType>,
             IArrowTypeVisitor<StructType>,
             IArrowTypeVisitor<NullType>
         {
-            private JsonFieldData JsonFieldData { get; }
+            private JsonFieldData JsonFieldData { get; set; }
             public IArrowArray Array { get; private set; }
 
             public ArrayCreator(JsonFieldData jsonFieldData)
@@ -478,12 +509,51 @@ namespace Apache.Arrow.IntegrationTest
 
             public void Visit(ListType type)
             {
-                throw new NotImplementedException();
+                ArrowBuffer validityBuffer = GetValidityBuffer(out int 
nullCount);
+                ArrowBuffer offsetBuffer = GetOffsetBuffer();
+
+                var data = JsonFieldData;
+                JsonFieldData = data.Children[0];
+                type.ValueDataType.Accept(this);
+                JsonFieldData = data;
+
+                ArrayData arrayData = new ArrayData(type, JsonFieldData.Count, 
nullCount, 0,
+                    new[] { validityBuffer, offsetBuffer }, new[] { Array.Data 
});
+                Array = new ListArray(arrayData);
+            }
+
+            public void Visit(FixedSizeListType type)
+            {
+                ArrowBuffer validityBuffer = GetValidityBuffer(out int 
nullCount);
+
+                var data = JsonFieldData;
+                JsonFieldData = data.Children[0];
+                type.ValueDataType.Accept(this);
+                JsonFieldData = data;
+
+                ArrayData arrayData = new ArrayData(type, JsonFieldData.Count, 
nullCount, 0,
+                    new[] { validityBuffer }, new[] { Array.Data });
+                Array = new FixedSizeListArray(arrayData);
             }
 
             public void Visit(StructType type)
             {
-                throw new NotImplementedException();
+                ArrowBuffer validityBuffer = GetValidityBuffer(out int 
nullCount);
+
+                ArrayData[] children = new ArrayData[type.Fields.Count];
+
+                var data = JsonFieldData;
+                for (int i = 0; i < children.Length; i++)
+                {
+                    JsonFieldData = data.Children[i];
+                    type.Fields[i].DataType.Accept(this);
+                    children[i] = Array.Data;
+                }
+                JsonFieldData = data;
+
+                ArrayData arrayData = new ArrayData(type, JsonFieldData.Count, 
nullCount, 0,
+                    new[] { validityBuffer }, children);
+                Array = new StructArray(arrayData);
             }
 
             private static byte[] ConvertHexStringToByteArray(string hexString)
diff --git a/csharp/test/Apache.Arrow.IntegrationTest/JsonFile.cs 
b/csharp/test/Apache.Arrow.IntegrationTest/JsonFile.cs
index f074afc010..f0f63d3e19 100644
--- a/csharp/test/Apache.Arrow.IntegrationTest/JsonFile.cs
+++ b/csharp/test/Apache.Arrow.IntegrationTest/JsonFile.cs
@@ -68,6 +68,9 @@ namespace Apache.Arrow.IntegrationTest
         // FixedSizeBinary fields
         public int ByteWidth { get; set; }
 
+        // FixedSizeList fields
+        public int ListSize { get; set; }
+
         [JsonExtensionData]
         public Dictionary<string, JsonElement> ExtensionData { get; set; }
     }
diff --git a/csharp/test/Apache.Arrow.Tests/ArrayBuilderTests.cs 
b/csharp/test/Apache.Arrow.Tests/ArrayBuilderTests.cs
index 2568e5e8bd..f789d6a3d3 100644
--- a/csharp/test/Apache.Arrow.Tests/ArrayBuilderTests.cs
+++ b/csharp/test/Apache.Arrow.Tests/ArrayBuilderTests.cs
@@ -203,6 +203,132 @@ namespace Apache.Arrow.Tests
                 ((Int64Array)childList3.GetSlicedValues(0)).ToList());
         }
 
+        [Fact]
+        public void FixedSizeListArrayBuilder()
+        {
+            var listBuilder = new 
FixedSizeListArray.Builder(StringType.Default, 4);
+            var valueBuilder = listBuilder.ValueBuilder as StringArray.Builder;
+            Assert.NotNull(valueBuilder);
+            listBuilder.Append();
+
+            Assert.Throws<ArgumentOutOfRangeException>(listBuilder.Append);
+
+            valueBuilder.Append("1");
+            valueBuilder.Append("2");
+            valueBuilder.Append("3");
+            valueBuilder.Append("4");
+            listBuilder.AppendNull();
+            listBuilder.Append();
+            valueBuilder.Append("5").Append("6").Append("7").Append("8");
+            listBuilder.Append();
+            
valueBuilder.Append("444").AppendNull().Append("555").Append("666");
+
+            var list = listBuilder.Build();
+
+            Assert.Equal(
+                new List<string> { "1", "2", "3", "4" },
+                ConvertStringArrayToList(list.GetSlicedValues(0) as 
StringArray));
+            Assert.Null(list.GetSlicedValues(1));
+            Assert.Equal(
+                new List<string> { "5", "6", "7", "8" },
+                ConvertStringArrayToList(list.GetSlicedValues(2) as 
StringArray));
+            Assert.Equal(
+                new List<string> { "444", null, "555", "666" },
+                ConvertStringArrayToList(list.GetSlicedValues(3) as 
StringArray));
+
+            Assert.Throws<ArgumentOutOfRangeException>(() => 
list.GetSlicedValues(-1));
+            Assert.Throws<ArgumentOutOfRangeException>(() => 
list.GetSlicedValues(4));
+
+            listBuilder.Resize(3);
+            var truncatedList = listBuilder.Build();
+
+            Assert.Equal(
+                new List<string> { "5", "6", "7", "8" },
+                ConvertStringArrayToList(truncatedList.GetSlicedValues(2) as 
StringArray));
+
+            Assert.Throws<ArgumentOutOfRangeException>(() => 
truncatedList.GetSlicedValues(-1));
+            Assert.Throws<ArgumentOutOfRangeException>(() => 
truncatedList.GetSlicedValues(3));
+
+            listBuilder.Clear();
+            var emptyList = listBuilder.Build();
+
+            Assert.Equal(0, emptyList.Length);
+
+            List<string> ConvertStringArrayToList(StringArray array)
+            {
+                var length = array.Length;
+                var resultList = new List<string>(length);
+                for (var index = 0; index < length; index++)
+                {
+                    resultList.Add(array.GetString(index));
+                }
+                return resultList;
+            }
+        }
+
+        [Fact]
+        public void FixedSizeListArrayBuilderValidityBuffer()
+        {
+            FixedSizeListArray.Builder builder = new 
FixedSizeListArray.Builder(Int64Type.Default, 2).Append();
+            ((Int64Array.Builder)builder.ValueBuilder).Append(1).Append(2);
+            FixedSizeListArray listArray = builder.AppendNull().Build();
+            Assert.False(listArray.IsValid(2));
+        }
+
+        [Fact]
+        public void NestedFixedSizeListArrayBuilder()
+        {
+            var childListType = new FixedSizeListType(Int64Type.Default, 2);
+            var parentListBuilder = new 
FixedSizeListArray.Builder(childListType, 3);
+            var childListBuilder = parentListBuilder.ValueBuilder as 
FixedSizeListArray.Builder;
+            Assert.NotNull(childListBuilder);
+            var valueBuilder = childListBuilder.ValueBuilder as 
Int64Array.Builder;
+            Assert.NotNull(valueBuilder);
+
+            parentListBuilder.Append();
+            childListBuilder.Append();
+            valueBuilder.Append(1);
+            valueBuilder.Append(2);
+            childListBuilder.Append();
+            valueBuilder.Append(3).Append(4);
+            childListBuilder.AppendNull();
+            parentListBuilder.Append();
+            childListBuilder.Append();
+            valueBuilder.Append(4).Append(5);
+            childListBuilder.Append();
+            valueBuilder.Append(6).Append(7);
+            childListBuilder.Append();
+            valueBuilder.Append(8).Append(9);
+            parentListBuilder.Append();
+            childListBuilder.Append();
+            valueBuilder.Append(10).Append(11);
+            childListBuilder.AppendNull();
+            childListBuilder.Append();
+            valueBuilder.Append(12).AppendNull();
+
+            var parentList = parentListBuilder.Build();
+
+            var childList1 = (FixedSizeListArray)parentList.GetSlicedValues(0);
+            var childList2 = (FixedSizeListArray)parentList.GetSlicedValues(1);
+            var childList3 = (FixedSizeListArray)parentList.GetSlicedValues(2);
+
+            Assert.Equal(3, childList1.Length);
+            Assert.Equal(3, childList2.Length);
+            Assert.Equal(3, childList3.Length);
+            Assert.Equal(
+                new List<long?> { 1, 2 },
+                ((Int64Array)childList1.GetSlicedValues(0)).ToList());
+            Assert.Equal(
+                new List<long?> { 3, 4 },
+                ((Int64Array)childList1.GetSlicedValues(1)).ToList());
+            Assert.Equal(
+                new List<long?> { 4, 5 },
+                ((Int64Array)childList2.GetSlicedValues(0)).ToList());
+            Assert.Equal(
+                new List<long?> { 12, null },
+                
((Int64Array)childList3.GetSlicedValues(2)).ToList(includeNulls: true));
+        }
+
         public class TimestampArrayBuilder
         {
             [Fact]
diff --git a/csharp/test/Apache.Arrow.Tests/ArrayTypeComparer.cs 
b/csharp/test/Apache.Arrow.Tests/ArrayTypeComparer.cs
index f75111b66d..77584aefb1 100644
--- a/csharp/test/Apache.Arrow.Tests/ArrayTypeComparer.cs
+++ b/csharp/test/Apache.Arrow.Tests/ArrayTypeComparer.cs
@@ -27,6 +27,7 @@ namespace Apache.Arrow.Tests
         IArrowTypeVisitor<Time64Type>,
         IArrowTypeVisitor<FixedSizeBinaryType>,
         IArrowTypeVisitor<ListType>,
+        IArrowTypeVisitor<FixedSizeListType>,
         IArrowTypeVisitor<StructType>
     {
         private readonly IArrowType _expectedType;
@@ -95,6 +96,16 @@ namespace Apache.Arrow.Tests
             CompareNested(expectedType, actualType);
         }
 
+        public void Visit(FixedSizeListType actualType)
+        {
+            Assert.IsAssignableFrom<FixedSizeListType>(_expectedType);
+            var expectedType = (FixedSizeListType)_expectedType;
+
+            Assert.Equal(expectedType.ListSize, actualType.ListSize);
+
+            CompareNested(expectedType, actualType);
+        }
+
         public void Visit(StructType actualType)
         {
             Assert.IsAssignableFrom<StructType>(_expectedType);
diff --git a/csharp/test/Apache.Arrow.Tests/ArrowArrayConcatenatorTests.cs 
b/csharp/test/Apache.Arrow.Tests/ArrowArrayConcatenatorTests.cs
index 6b3277ed57..36cffe7eb4 100644
--- a/csharp/test/Apache.Arrow.Tests/ArrowArrayConcatenatorTests.cs
+++ b/csharp/test/Apache.Arrow.Tests/ArrowArrayConcatenatorTests.cs
@@ -76,6 +76,7 @@ namespace Apache.Arrow.Tests
                         new 
Field.Builder().Name("Strings").DataType(StringType.Default).Nullable(true).Build(),
                         new 
Field.Builder().Name("Ints").DataType(Int32Type.Default).Nullable(true).Build()
                     }),
+                    new FixedSizeListType(Int32Type.Default, 1),
                 };
 
             foreach (IArrowType type in targetTypes)
@@ -117,6 +118,7 @@ namespace Apache.Arrow.Tests
             IArrowTypeVisitor<Date64Type>,
             IArrowTypeVisitor<TimestampType>,
             IArrowTypeVisitor<ListType>,
+            IArrowTypeVisitor<FixedSizeListType>,
             IArrowTypeVisitor<StructType>
         {
 
@@ -324,6 +326,42 @@ namespace Apache.Arrow.Tests
                 ExpectedArray = resultBuilder.Build();
             }
 
+            public void Visit(FixedSizeListType type)
+            {
+                FixedSizeListArray.Builder resultBuilder = new 
FixedSizeListArray.Builder(type.ValueDataType, 
type.ListSize).Reserve(_baseDataTotalElementCount);
+                //Todo : Support various types
+                Int32Array.Builder resultValueBuilder = 
(Int32Array.Builder)resultBuilder.ValueBuilder.Reserve(_baseDataTotalElementCount);
+
+                for (int i = 0; i < _baseDataListCount; i++)
+                {
+                    List<int?> dataList = _baseData[i];
+
+                    FixedSizeListArray.Builder builder = new 
FixedSizeListArray.Builder(type.ValueField, 
type.ListSize).Reserve(dataList.Count);
+                    Int32Array.Builder valueBuilder = 
(Int32Array.Builder)builder.ValueBuilder.Reserve(dataList.Count);
+
+                    foreach (int? value in dataList)
+                    {
+                        if (value.HasValue)
+                        {
+                            builder.Append();
+                            resultBuilder.Append();
+
+                            valueBuilder.Append(value.Value);
+                            resultValueBuilder.Append(value.Value);
+                        }
+                        else
+                        {
+                            builder.AppendNull();
+                            resultBuilder.AppendNull();
+                        }
+                    }
+
+                    TestTargetArrayList.Add(builder.Build());
+                }
+
+                ExpectedArray = resultBuilder.Build();
+            }
+
             public void Visit(StructType type)
             {
                 // TODO: Make data from type fields.
diff --git a/csharp/test/Apache.Arrow.Tests/ArrowReaderVerifier.cs 
b/csharp/test/Apache.Arrow.Tests/ArrowReaderVerifier.cs
index 543b446bba..e588eab51e 100644
--- a/csharp/test/Apache.Arrow.Tests/ArrowReaderVerifier.cs
+++ b/csharp/test/Apache.Arrow.Tests/ArrowReaderVerifier.cs
@@ -86,6 +86,7 @@ namespace Apache.Arrow.Tests
             IArrowArrayVisitor<Time32Array>,
             IArrowArrayVisitor<Time64Array>,
             IArrowArrayVisitor<ListArray>,
+            IArrowArrayVisitor<FixedSizeListArray>,
             IArrowArrayVisitor<StringArray>,
             IArrowArrayVisitor<FixedSizeBinaryArray>,
             IArrowArrayVisitor<BinaryArray>,
@@ -126,6 +127,7 @@ namespace Apache.Arrow.Tests
             public void Visit(Time32Array array) => CompareArrays(array);
             public void Visit(Time64Array array) => CompareArrays(array);
             public void Visit(ListArray array) => CompareArrays(array);
+            public void Visit(FixedSizeListArray array) => 
CompareArrays(array);
             public void Visit(FixedSizeBinaryArray array) => 
CompareArrays(array);
             public void Visit(Decimal128Array array) => CompareArrays(array);
             public void Visit(Decimal256Array array) => CompareArrays(array);
@@ -304,13 +306,29 @@ namespace Apache.Arrow.Tests
                 }
                 else
                 {
-                    int offsetsLength = (expectedArray.Length + 1) * 4;
+                    int offsetsLength = (expectedArray.Length + 1) * 
sizeof(int);
                     Assert.True(expectedArray.ValueOffsetsBuffer.Span.Slice(0, 
offsetsLength).SequenceEqual(actualArray.ValueOffsetsBuffer.Span.Slice(0, 
offsetsLength)));
                 }
 
                 actualArray.Values.Accept(new 
ArrayComparer(expectedArray.Values, _strictCompare));
             }
 
+            private void CompareArrays(FixedSizeListArray actualArray)
+            {
+                Assert.IsAssignableFrom<FixedSizeListArray>(_expectedArray);
+                FixedSizeListArray expectedArray = 
(FixedSizeListArray)_expectedArray;
+
+                actualArray.Data.DataType.Accept(_arrayTypeComparer);
+
+                Assert.Equal(expectedArray.Length, actualArray.Length);
+                Assert.Equal(expectedArray.NullCount, actualArray.NullCount);
+                Assert.Equal(expectedArray.Offset, actualArray.Offset);
+
+                CompareValidityBuffer(expectedArray.NullCount, 
_expectedArray.Length, expectedArray.NullBitmapBuffer, 
actualArray.NullBitmapBuffer);
+
+                actualArray.Values.Accept(new 
ArrayComparer(expectedArray.Values, _strictCompare));
+            }
+
             private void CompareValidityBuffer(int nullCount, int arrayLength, 
ArrowBuffer expectedValidityBuffer, ArrowBuffer actualValidityBuffer)
             {
                 if (_strictCompare)
diff --git a/csharp/test/Apache.Arrow.Tests/CDataInterfacePythonTests.cs 
b/csharp/test/Apache.Arrow.Tests/CDataInterfacePythonTests.cs
index 4c53b98e3d..29b1b9e7db 100644
--- a/csharp/test/Apache.Arrow.Tests/CDataInterfacePythonTests.cs
+++ b/csharp/test/Apache.Arrow.Tests/CDataInterfacePythonTests.cs
@@ -106,6 +106,8 @@ namespace Apache.Arrow.Tests
                     .Field(f => f.Name("list_string").DataType(new 
ListType(StringType.Default)).Nullable(false))
                     .Field(f => f.Name("list_list_i32").DataType(new 
ListType(new ListType(Int32Type.Default))).Nullable(false))
 
+                    .Field(f => f.Name("fixed_length_list_i64").DataType(new 
FixedSizeListType(Int64Type.Default, 10)).Nullable(true))
+
                     .Field(f => f.Name("dict_string").DataType(new 
DictionaryType(Int32Type.Default, StringType.Default, false)).Nullable(false))
                     .Field(f => f.Name("dict_string_ordered").DataType(new 
DictionaryType(Int32Type.Default, StringType.Default, true)).Nullable(false))
                     .Field(f => f.Name("list_dict_string").DataType(new 
ListType(new DictionaryType(Int32Type.Default, StringType.Default, 
false))).Nullable(false))
@@ -164,6 +166,8 @@ namespace Apache.Arrow.Tests
                 yield return pa.field("list_string", pa.list_(pa.utf8()), 
false);
                 yield return pa.field("list_list_i32", 
pa.list_(pa.list_(pa.int32())), false);
 
+                yield return pa.field("fixed_length_list_i64", 
pa.list_(pa.int64(), 10), true);
+
                 yield return pa.field("dict_string", pa.dictionary(pa.int32(), 
pa.utf8(), false), false);
                 yield return pa.field("dict_string_ordered", 
pa.dictionary(pa.int32(), pa.utf8(), true), false);
                 yield return pa.field("list_dict_string", 
pa.list_(pa.dictionary(pa.int32(), pa.utf8(), false)), false);
@@ -492,8 +496,11 @@ namespace Apache.Arrow.Tests
                             pa.array(List(1, 0, 1, 1, null)),
                             pa.array(List("foo", "bar"))
                             ),
+                        pa.FixedSizeListArray.from_arrays(
+                            pa.array(List(1, 2, 3, 4, null, 6, 7, null, null, 
null)),
+                            2),
                     }),
-                    new[] { "col1", "col2", "col3", "col4", "col5", "col6", 
"col7" });
+                    new[] { "col1", "col2", "col3", "col4", "col5", "col6", 
"col7", "col8" });
 
                 dynamic batch = table.to_batches()[0];
 
@@ -554,6 +561,13 @@ namespace Apache.Arrow.Tests
             Assert.Equal(2, col7b.Length);
             Assert.Equal("foo", col7b.GetString(0));
             Assert.Equal("bar", col7b.GetString(1));
+
+            FixedSizeListArray col8 = 
(FixedSizeListArray)recordBatch.Column("col8");
+            Assert.Equal(5, col8.Length);
+            Int64Array col8a = (Int64Array)col8.Values;
+            Assert.Equal(new long[] { 1, 2, 3, 4, 0, 6, 7, 0, 0, 0 }, 
col8a.Values.ToArray());
+            Assert.True(col8a.IsValid(3));
+            Assert.False(col8a.IsValid(9));
         }
 
         [SkippableFact]
diff --git a/csharp/test/Apache.Arrow.Tests/TableTests.cs 
b/csharp/test/Apache.Arrow.Tests/TableTests.cs
index 45a14cc256..b4c4b1faed 100644
--- a/csharp/test/Apache.Arrow.Tests/TableTests.cs
+++ b/csharp/test/Apache.Arrow.Tests/TableTests.cs
@@ -60,7 +60,7 @@ namespace Apache.Arrow.Tests
 
             Table table1 = Table.TableFromRecordBatches(recordBatch1.Schema, 
recordBatches);
             Assert.Equal(20, table1.RowCount);
-            Assert.Equal(23, table1.ColumnCount);
+            Assert.Equal(24, table1.ColumnCount);
 
             FixedSizeBinaryType type = new FixedSizeBinaryType(17);
             Field newField1 = new Field(type.Name, type, false);
diff --git a/csharp/test/Apache.Arrow.Tests/TestData.cs 
b/csharp/test/Apache.Arrow.Tests/TestData.cs
index 96c6fafee2..41507311f6 100644
--- a/csharp/test/Apache.Arrow.Tests/TestData.cs
+++ b/csharp/test/Apache.Arrow.Tests/TestData.cs
@@ -59,6 +59,7 @@ namespace Apache.Arrow.Tests
                 {
                     builder.Field(CreateField(new 
DictionaryType(Int32Type.Default, StringType.Default, false), i));
                     builder.Field(CreateField(new FixedSizeBinaryType(16), i));
+                    builder.Field(CreateField(new 
FixedSizeListType(Int32Type.Default, 3), i));
                 }
 
                 //builder.Field(CreateField(HalfFloatType.Default));
@@ -122,6 +123,7 @@ namespace Apache.Arrow.Tests
             IArrowTypeVisitor<TimestampType>,
             IArrowTypeVisitor<StringType>,
             IArrowTypeVisitor<ListType>,
+            IArrowTypeVisitor<FixedSizeListType>,
             IArrowTypeVisitor<StructType>,
             IArrowTypeVisitor<Decimal128Type>,
             IArrowTypeVisitor<Decimal256Type>,
@@ -270,6 +272,32 @@ namespace Apache.Arrow.Tests
                 Array = builder.Build();
             }
 
+            public void Visit(FixedSizeListType type)
+            {
+                var builder = new FixedSizeListArray.Builder(type.ValueField, 
type.ListSize).Reserve(Length);
+
+                //Todo : Support various types
+                var valueBuilder = (Int32Array.Builder)builder.ValueBuilder;
+
+                for (var i = 0; i < Length; i++)
+                {
+                    if (type.Fields[0].IsNullable && (i % 3) == 0)
+                    {
+                        builder.AppendNull();
+                    }
+                    else
+                    {
+                        builder.Append();
+                        for (var j = 0; j < type.ListSize; j++)
+                        {
+                            valueBuilder.Append(i * type.ListSize + j);
+                        }
+                    }
+                }
+
+                Array = builder.Build();
+            }
+
             public void Visit(StructType type)
             {
                 IArrowArray[] childArrays = new IArrowArray[type.Fields.Count];
diff --git a/dev/archery/archery/integration/datagen.py 
b/dev/archery/archery/integration/datagen.py
index 11cb02a9f4..99aeda5248 100644
--- a/dev/archery/archery/integration/datagen.py
+++ b/dev/archery/archery/integration/datagen.py
@@ -1702,11 +1702,9 @@ def get_generated_json_files(tempdir=None):
         .skip_category('Java')   # TODO(ARROW-8715)
         .skip_category('JS'),     # TODO(ARROW-8716)
 
-        generate_nested_case()
-        .skip_category('C#'),
+        generate_nested_case(),
 
-        generate_recursive_nested_case()
-        .skip_category('C#'),
+        generate_recursive_nested_case(),
 
         generate_nested_large_offsets_case()
         .skip_category('C#')
diff --git a/docs/source/status.rst b/docs/source/status.rst
index fbbe1cede9..6d7f674932 100644
--- a/docs/source/status.rst
+++ b/docs/source/status.rst
@@ -73,7 +73,7 @@ Data Types
 | Data type         | C++   | Java  | Go    | JavaScript | C#    | Rust  | 
Julia | Swift |
 | (nested)          |       |       |       |            |       |       |     
  |       |
 
+===================+=======+=======+=======+============+=======+=======+=======+=======+
-| Fixed Size List   | ✓     | ✓     | ✓     | ✓          |       |  ✓    | ✓   
  |       |
+| Fixed Size List   | ✓     | ✓     | ✓     | ✓          |  ✓    |  ✓    | ✓   
  |       |
 
+-------------------+-------+-------+-------+------------+-------+-------+-------+-------+
 | List              | ✓     | ✓     | ✓     | ✓          |  ✓    |  ✓    | ✓   
  |       |
 
+-------------------+-------+-------+-------+------------+-------+-------+-------+-------+

Reply via email to