diff --git a/csharp/Directory.Build.props b/csharp/Directory.Build.props index 66c8acf5003cf..01d079c4744fc 100644 --- a/csharp/Directory.Build.props +++ b/csharp/Directory.Build.props @@ -34,7 +34,7 @@ true - 8.0 + latest true $(CSharpDir)ApacheArrow.snk diff --git a/csharp/README.md b/csharp/README.md index 79415837713b1..649e8a722574c 100644 --- a/csharp/README.md +++ b/csharp/README.md @@ -79,7 +79,7 @@ for currently available features. - Int8, Int16, Int32, Int64 - UInt8, UInt16, UInt32, UInt64 -- Float, Double +- Float, Double, Half-float (.NET 6+) - Binary (variable-length) - String (utf-8) - Null @@ -126,12 +126,10 @@ for currently available features. - Dictionary Encoding - Types - Tensor - - Table - Arrays - Union - Dense - Sparse - - Half-Float - Array Operations - Equality / Comparison - Casting diff --git a/csharp/src/Apache.Arrow/Apache.Arrow.csproj b/csharp/src/Apache.Arrow/Apache.Arrow.csproj index 51f188ae87be8..c57c8e48b9494 100644 --- a/csharp/src/Apache.Arrow/Apache.Arrow.csproj +++ b/csharp/src/Apache.Arrow/Apache.Arrow.csproj @@ -1,7 +1,7 @@ - netstandard1.3;netstandard2.0;netcoreapp3.1 + netstandard1.3;netstandard2.0;netcoreapp3.1;net6.0 true $(DefineConstants);UNSAFE_BYTEBUFFER;BYTEBUFFER_NO_BOUNDS_CHECK;ENABLE_SPAN_T @@ -41,4 +41,7 @@ + + + diff --git a/csharp/src/Apache.Arrow/Arrays/ArrowArrayBuilderFactory.cs b/csharp/src/Apache.Arrow/Arrays/ArrowArrayBuilderFactory.cs index 618ac0212ea5a..ef80edb838162 100644 --- a/csharp/src/Apache.Arrow/Arrays/ArrowArrayBuilderFactory.cs +++ b/csharp/src/Apache.Arrow/Arrays/ArrowArrayBuilderFactory.cs @@ -42,6 +42,12 @@ internal static IArrowArrayBuilder> return new UInt64Array.Builder(); case ArrowTypeId.Int64: return new Int64Array.Builder(); + case ArrowTypeId.HalfFloat: +#if NET5_0_OR_GREATER + return new HalfFloatArray.Builder(); +#else + throw new NotSupportedException("Half-float arrays are not supported by this target framework."); +#endif case ArrowTypeId.Float: return new FloatArray.Builder(); case ArrowTypeId.Double: @@ -70,7 +76,6 @@ internal static IArrowArrayBuilder> case ArrowTypeId.Union: case ArrowTypeId.Dictionary: case ArrowTypeId.FixedSizedBinary: - case ArrowTypeId.HalfFloat: case ArrowTypeId.Interval: case ArrowTypeId.Map: default: diff --git a/csharp/src/Apache.Arrow/Arrays/ArrowArrayFactory.cs b/csharp/src/Apache.Arrow/Arrays/ArrowArrayFactory.cs index 819540a46a10b..319dcab17e75c 100644 --- a/csharp/src/Apache.Arrow/Arrays/ArrowArrayFactory.cs +++ b/csharp/src/Apache.Arrow/Arrays/ArrowArrayFactory.cs @@ -76,6 +76,11 @@ public static IArrowArray BuildArray(ArrayData data) case ArrowTypeId.Dictionary: return new DictionaryArray(data); case ArrowTypeId.HalfFloat: +#if NET5_0_OR_GREATER + return new HalfFloatArray(data); +#else + throw new NotSupportedException("Half-float arrays are not supported by this target framework."); +#endif case ArrowTypeId.Interval: case ArrowTypeId.Map: default: diff --git a/csharp/src/Apache.Arrow/Arrays/HalfFloatArray.cs b/csharp/src/Apache.Arrow/Arrays/HalfFloatArray.cs new file mode 100644 index 0000000000000..072629e0e8ab8 --- /dev/null +++ b/csharp/src/Apache.Arrow/Arrays/HalfFloatArray.cs @@ -0,0 +1,46 @@ +// 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 Apache.Arrow.Types; +using System; + +namespace Apache.Arrow +{ + public class HalfFloatArray : PrimitiveArray + { + public class Builder : PrimitiveArrayBuilder + { + protected override HalfFloatArray Build( + ArrowBuffer valueBuffer, ArrowBuffer nullBitmapBuffer, + int length, int nullCount, int offset) => + new HalfFloatArray(valueBuffer, nullBitmapBuffer, length, nullCount, offset); + } + + public HalfFloatArray( + ArrowBuffer valueBuffer, ArrowBuffer nullBitmapBuffer, + int length, int nullCount, int offset) + : this(new ArrayData(HalfFloatType.Default, length, nullCount, offset, + new[] { nullBitmapBuffer, valueBuffer })) + { } + + public HalfFloatArray(ArrayData data) + : base(data) + { + data.EnsureDataType(ArrowTypeId.HalfFloat); + } + + public override void Accept(IArrowArrayVisitor visitor) => Accept(this, visitor); + } +} diff --git a/csharp/src/Apache.Arrow/Ipc/ArrowStreamWriter.cs b/csharp/src/Apache.Arrow/Ipc/ArrowStreamWriter.cs index e0d241da80608..d9cafc06bffba 100644 --- a/csharp/src/Apache.Arrow/Ipc/ArrowStreamWriter.cs +++ b/csharp/src/Apache.Arrow/Ipc/ArrowStreamWriter.cs @@ -38,6 +38,9 @@ internal class ArrowRecordBatchFlatBufferBuilder : IArrowArrayVisitor, IArrowArrayVisitor, IArrowArrayVisitor, +#if NET5_0_OR_GREATER + IArrowArrayVisitor, +#endif IArrowArrayVisitor, IArrowArrayVisitor, IArrowArrayVisitor, @@ -87,6 +90,9 @@ public ArrowRecordBatchFlatBufferBuilder() public void Visit(UInt16Array array) => CreateBuffers(array); public void Visit(UInt32Array array) => CreateBuffers(array); public void Visit(UInt64Array array) => CreateBuffers(array); +#if NET5_0_OR_GREATER + public void Visit(HalfFloatArray array) => CreateBuffers(array); +#endif public void Visit(FloatArray array) => CreateBuffers(array); public void Visit(DoubleArray array) => CreateBuffers(array); public void Visit(TimestampArray array) => CreateBuffers(array); diff --git a/csharp/src/Apache.Arrow/RecordBatch.Builder.cs b/csharp/src/Apache.Arrow/RecordBatch.Builder.cs index ec77967aab7cf..b5d5ec9ea0bbf 100644 --- a/csharp/src/Apache.Arrow/RecordBatch.Builder.cs +++ b/csharp/src/Apache.Arrow/RecordBatch.Builder.cs @@ -42,6 +42,9 @@ internal ArrayBuilder(MemoryAllocator allocator) public UInt16Array UInt16(Action action) => Build(new UInt16Array.Builder(), action); public UInt32Array UInt32(Action action) => Build(new UInt32Array.Builder(), action); public UInt64Array UInt64(Action action) => Build(new UInt64Array.Builder(), action); +#if NET5_0_OR_GREATER + public HalfFloatArray HalfFloat(Action action) => Build(new HalfFloatArray.Builder(), action); +#endif public FloatArray Float(Action action) => Build(new FloatArray.Builder(), action); public DoubleArray Double(Action action) => Build(new DoubleArray.Builder(), action); public Decimal128Array Decimal128(Decimal128Type type, Action action) => diff --git a/csharp/test/Apache.Arrow.Tests/ArrayBuilderTests.cs b/csharp/test/Apache.Arrow.Tests/ArrayBuilderTests.cs index 0c58617ad1752..8c90f780720f8 100644 --- a/csharp/test/Apache.Arrow.Tests/ArrayBuilderTests.cs +++ b/csharp/test/Apache.Arrow.Tests/ArrayBuilderTests.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Numerics; using Xunit; namespace Apache.Arrow.Tests @@ -28,36 +29,49 @@ public class ArrayBuilderTests [Fact] public void PrimitiveArrayBuildersProduceExpectedArray() { - TestArrayBuilder(x => x.Append(10).Append(20).Append(30)); - TestArrayBuilder(x => x.Append(10).Append(20).Append(30)); - TestArrayBuilder(x => x.Append(10).Append(20).Append(30)); - TestArrayBuilder(x => x.Append(10).Append(20).Append(30)); - TestArrayBuilder(x => x.Append(10).Append(20).Append(30)); - TestArrayBuilder(x => x.Append(10).Append(20).Append(30)); - TestArrayBuilder(x => x.Append(10).Append(20).Append(30)); - TestArrayBuilder(x => x.Append(10).Append(20).Append(30)); - TestArrayBuilder(x => x.Append(10).Append(20).Append(30)); - TestArrayBuilder(x => x.Append(10).Append(20).Append(30)); - TestArrayBuilder(x => x.Append(10).Append(20).Append(30)); - TestArrayBuilder(x => x.Append(10).Append(20).Append(30)); + Test(); + Test(); + Test(); + Test(); + Test(); + Test(); + Test(); + Test(); + Test(); + Test(); + Test(); + Test(); + Test(); + + static void Test() + where T : struct, INumber + where TArray : PrimitiveArray + where TBuilder : PrimitiveArrayBuilder, new() => + TestArrayBuilder(x => x.Append(T.CreateChecked(10)).Append(T.CreateChecked(20)).Append(T.CreateChecked(30))); } [Fact] public void PrimitiveArrayBuildersProduceExpectedArrayWithNulls() { - TestArrayBuilder(x => x.Append(123).AppendNull().AppendNull().Append(127), 4, 2, 0x09); - TestArrayBuilder(x => x.Append(123).AppendNull().AppendNull().Append(456), 4, 2, 0x09); - TestArrayBuilder(x => x.Append(123).AppendNull().AppendNull().Append(456), 4, 2, 0x09); - TestArrayBuilder(x => x.Append(123).AppendNull().AppendNull().Append(456), 4, 2, 0x09); - TestArrayBuilder(x => x.Append(123).AppendNull().AppendNull().Append(127), 4, 2, 0x09); - TestArrayBuilder(x => x.Append(123).AppendNull().AppendNull().Append(456), 4, 2, 0x09); - TestArrayBuilder(x => x.Append(123).AppendNull().AppendNull().Append(456), 4, 2, 0x09); - TestArrayBuilder(x => x.Append(123).AppendNull().AppendNull().Append(456), 4, 2, 0x09); - TestArrayBuilder(x => x.Append(123).AppendNull().AppendNull().Append(456), 4, 2, 0x09); - TestArrayBuilder(x => x.Append(123).AppendNull().AppendNull().Append(456), 4, 2, 0x09); - TestArrayBuilder(x => x.Append(123).AppendNull().AppendNull().Append(456), 4, 2, 0x09); - TestArrayBuilder(x => x.Append(123).AppendNull().AppendNull().Append(456), 4, 2, 0x09); - TestArrayBuilder(x => x.Append(123).AppendNull().AppendNull().Append(456), 4, 2, 0x09); + Test(); + Test(); + Test(); + Test(); + Test(); + Test(); + Test(); + Test(); + Test(); + Test(); + Test(); + Test(); + Test(); + + static void Test() + where T : struct, INumber + where TArray : PrimitiveArray + where TBuilder : PrimitiveArrayBuilder, new() => + TestArrayBuilder(x => x.Append(T.CreateChecked(123)).AppendNull().AppendNull().Append(T.CreateChecked(127)), 4, 2, 0x09); } [Fact] @@ -138,7 +152,7 @@ List ConvertStringArrayToList(StringArray array) [Fact] public void ListArrayBuilderValidityBuffer() { - ListArray listArray = new ListArray.Builder(Int64Type.Default).Append().AppendNull().Build(); + ListArray listArray = new ListArray.Builder(Int64Type.Default).Append().AppendNull().Build(); Assert.False(listArray.IsValid(2)); } diff --git a/csharp/test/Apache.Arrow.Tests/ArrowArrayTests.cs b/csharp/test/Apache.Arrow.Tests/ArrowArrayTests.cs index 1e3d4b12f56d3..af3e0f80e6473 100644 --- a/csharp/test/Apache.Arrow.Tests/ArrowArrayTests.cs +++ b/csharp/test/Apache.Arrow.Tests/ArrowArrayTests.cs @@ -14,6 +14,7 @@ // limitations under the License. using System; +using System.Numerics; using Xunit; namespace Apache.Arrow.Tests @@ -95,40 +96,54 @@ void TestIsValid(ArrowBuffer valueBuf, ArrowBuffer nullBitmapBuf, int length, in [Fact] public void SliceArray() { - TestSlice(x => x.Append(10).Append(20).Append(30)); - TestSlice(x => x.Append(10).Append(20).Append(30)); - TestSlice(x => x.Append(10).Append(20).Append(30)); - TestSlice(x => x.Append(10).Append(20).Append(30)); - TestSlice(x => x.Append(10).Append(20).Append(30)); - TestSlice(x => x.Append(10).Append(20).Append(30)); - TestSlice(x => x.Append(10).Append(20).Append(30)); - TestSlice(x => x.Append(10).Append(20).Append(30)); - TestSlice(x => x.Append(10).Append(20).Append(30)); - TestSlice(x => x.Append(10).Append(20).Append(30)); + TestNumberSlice(); + TestNumberSlice(); + TestNumberSlice(); + TestNumberSlice(); + TestNumberSlice(); + TestNumberSlice(); + TestNumberSlice(); + TestNumberSlice(); + TestNumberSlice(); + TestNumberSlice(); + TestNumberSlice(); TestSlice(x => x.Append(new DateTime(2019, 1, 1)).Append(new DateTime(2019, 1, 2)).Append(new DateTime(2019, 1, 3))); TestSlice(x => x.Append(new DateTime(2019, 1, 1)).Append(new DateTime(2019, 1, 2)).Append(new DateTime(2019, 1, 3))); - TestSlice(x => x.Append(10).Append(20).Append(30)); - TestSlice(x => x.Append(10).Append(20).Append(30)); + TestNumberSlice(); + TestNumberSlice(); TestSlice(x => x.Append("10").Append("20").Append("30")); + + static void TestNumberSlice() + where T : struct, INumber + where TArray : PrimitiveArray + where TBuilder : PrimitiveArrayBuilder, new() => + TestSlice(x => x.Append(T.CreateChecked(10)).Append(T.CreateChecked(20)).Append(T.CreateChecked(30))); } [Fact] public void SlicePrimitiveArrayWithNulls() { - TestSlice(x => x.Append(10).Append(20).AppendNull().Append(30)); - TestSlice(x => x.Append(10).AppendNull().Append(20).AppendNull().Append(30)); - TestSlice(x => x.Append(10).Append(20).AppendNull().Append(30)); - TestSlice(x => x.Append(10).Append(20).AppendNull().Append(30)); - TestSlice(x => x.Append(10).Append(20).Append(30).AppendNull()); - TestSlice(x => x.Append(10).Append(20).AppendNull().AppendNull().Append(30)); - TestSlice(x => x.Append(10).Append(20).AppendNull().Append(30)); - TestSlice(x => x.Append(10).Append(20).AppendNull().Append(30)); - TestSlice(x => x.AppendNull().Append(10).Append(20).AppendNull().Append(30)); - TestSlice(x => x.Append(10).Append(20).AppendNull().Append(30)); + TestNumberSlice(); + TestNumberSlice(); + TestNumberSlice(); + TestNumberSlice(); + TestNumberSlice(); + TestNumberSlice(); + TestNumberSlice(); + TestNumberSlice(); + TestNumberSlice(); + TestNumberSlice(); + TestNumberSlice(); TestSlice(x => x.Append(new DateTime(2019, 1, 1)).Append(new DateTime(2019, 1, 2)).AppendNull().Append(new DateTime(2019, 1, 3))); TestSlice(x => x.Append(new DateTime(2019, 1, 1)).Append(new DateTime(2019, 1, 2)).AppendNull().Append(new DateTime(2019, 1, 3))); - TestSlice(x => x.Append(10).AppendNull().Append(30)); - TestSlice(x => x.Append(10).AppendNull().Append(30)); + TestNumberSlice(); + TestNumberSlice(); + + static void TestNumberSlice() + where T : struct, INumber + where TArray : PrimitiveArray + where TBuilder : PrimitiveArrayBuilder, new() => + TestSlice(x => x.AppendNull().Append(T.CreateChecked(10)).Append(T.CreateChecked(20)).AppendNull().Append(T.CreateChecked(30))); } [Fact] @@ -183,6 +198,7 @@ private class ArraySliceValidator : IArrowArrayVisitor, IArrowArrayVisitor, IArrowArrayVisitor, + IArrowArrayVisitor, IArrowArrayVisitor, IArrowArrayVisitor, IArrowArrayVisitor, @@ -224,6 +240,7 @@ public void Visit(Date64Array array) public void Visit(Time32Array array) => ValidateArrays(array); public void Visit(Time64Array array) => ValidateArrays(array); + public void Visit(HalfFloatArray array) => ValidateArrays(array); public void Visit(FloatArray array) => ValidateArrays(array); public void Visit(DoubleArray array) => ValidateArrays(array); public void Visit(StringArray array) => ValidateArrays(array); diff --git a/csharp/test/Apache.Arrow.Tests/ArrowReaderVerifier.cs b/csharp/test/Apache.Arrow.Tests/ArrowReaderVerifier.cs index 4a15bc7117649..8fde77d930779 100644 --- a/csharp/test/Apache.Arrow.Tests/ArrowReaderVerifier.cs +++ b/csharp/test/Apache.Arrow.Tests/ArrowReaderVerifier.cs @@ -74,6 +74,7 @@ private class ArrayComparer : IArrowArrayVisitor, IArrowArrayVisitor, IArrowArrayVisitor, + IArrowArrayVisitor, IArrowArrayVisitor, IArrowArrayVisitor, IArrowArrayVisitor, @@ -110,6 +111,7 @@ public ArrayComparer(IArrowArray expectedArray, bool strictCompare) public void Visit(UInt16Array array) => CompareArrays(array); public void Visit(UInt32Array array) => CompareArrays(array); public void Visit(UInt64Array array) => CompareArrays(array); + public void Visit(HalfFloatArray array) => CompareArrays(array); public void Visit(FloatArray array) => CompareArrays(array); public void Visit(DoubleArray array) => CompareArrays(array); public void Visit(BooleanArray array) => CompareArrays(array); diff --git a/docs/source/status.rst b/docs/source/status.rst index 73c025a8b7493..3ca82ac3e3bb1 100644 --- a/docs/source/status.rst +++ b/docs/source/status.rst @@ -40,7 +40,7 @@ Data Types +-------------------+-------+-------+-------+------------+-------+-------+-------+ | UInt8/16/32/64 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +-------------------+-------+-------+-------+------------+-------+-------+-------+ -| Float16 | | | ✓ | | | ✓ | ✓ | +| Float16 | | | ✓ | | ✓ (1)| ✓ | ✓ | +-------------------+-------+-------+-------+------------+-------+-------+-------+ | Float32/64 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +-------------------+-------+-------+-------+------------+-------+-------+-------+ @@ -92,7 +92,7 @@ Data Types | Data type | C++ | Java | Go | JavaScript | C# | Rust | Julia | | (special) | | | | | | | | +===================+=======+=======+=======+============+=======+=======+=======+ -| Dictionary | ✓ | ✓ (1) | ✓ | ✓ (1) | ✓ (1) | ✓ (1) | ✓ | +| Dictionary | ✓ | ✓ (2) | ✓ | ✓ (2) | ✓ (2) | ✓ (2) | ✓ | +-------------------+-------+-------+-------+------------+-------+-------+-------+ | Extension | ✓ | ✓ | ✓ | | | ✓ | ✓ | +-------------------+-------+-------+-------+------------+-------+-------+-------+ @@ -101,7 +101,8 @@ Data Types Notes: -* \(1) Nested dictionaries not supported +* \(1) Float16 support in C# is only available when targeting .NET 6+. +* \(2) Nested dictionaries not supported .. seealso:: The :ref:`format_columnar` specification.