Skip to content

Commit

Permalink
apacheGH-38348: [C#] Make PrimitiveArray<T> support IReadOnlyList<T?> (
Browse files Browse the repository at this point in the history
…apache#38680)

### What changes are included in this PR?

Make Arrow arrays of scalar type T implement the same semantic contract as IReadOnlyList<T?>.
Note that this PR does not include similar support for ICollection<T?>. I could add that support in this PR or a future PR.

### Are these changes tested?

This PR includes unit tests of the implemented IReadOnlyList<T?> methods.
* Closes: apache#38348

Authored-by: voidstar69 <[email protected]>
Signed-off-by: Curt Hagenlocher <[email protected]>
  • Loading branch information
voidstar69 authored and dgreiss committed Feb 17, 2024
1 parent 8f8f4df commit 0184ac0
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 8 deletions.
16 changes: 15 additions & 1 deletion csharp/src/Apache.Arrow/Arrays/BinaryArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Apache.Arrow.Memory;
using System.Collections;

namespace Apache.Arrow
{
public class BinaryArray : Array
public class BinaryArray : Array, IReadOnlyList<byte[]>
{
public class Builder : BuilderBase<BinaryArray, Builder>
{
Expand Down Expand Up @@ -366,5 +367,18 @@ public ReadOnlySpan<byte> GetBytes(int index, out bool isNull)

return ValueBuffer.Span.Slice(ValueOffsets[index], GetValueLength(index));
}

int IReadOnlyCollection<byte[]>.Count => Length;
byte[] IReadOnlyList<byte[]>.this[int index] => GetBytes(index).ToArray();

IEnumerator<byte[]> IEnumerable<byte[]>.GetEnumerator()
{
for (int index = 0; index < Length; index++)
{
yield return GetBytes(index).ToArray();
}
}

IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<byte[]>)this).GetEnumerator();
}
}
17 changes: 16 additions & 1 deletion csharp/src/Apache.Arrow/Arrays/BooleanArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@
using Apache.Arrow.Memory;
using Apache.Arrow.Types;
using System;
using System.Collections;
using System.Collections.Generic;

namespace Apache.Arrow
{
public class BooleanArray: Array
public class BooleanArray: Array, IReadOnlyList<bool?>
{
public class Builder : IArrowArrayBuilder<bool, BooleanArray, Builder>
{
Expand Down Expand Up @@ -190,5 +191,19 @@ public bool GetBoolean(int index)
? (bool?)null
: BitUtility.GetBit(ValueBuffer.Span, index + Offset);
}

int IReadOnlyCollection<bool?>.Count => Length;

bool? IReadOnlyList<bool?>.this[int index] => GetValue(index);

IEnumerator<bool?> IEnumerable<bool?>.GetEnumerator()
{
for (int index = 0; index < Length; index++)
{
yield return GetValue(index);
}
}

IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<bool?>)this).GetEnumerator();
}
}
30 changes: 29 additions & 1 deletion csharp/src/Apache.Arrow/Arrays/Date32Array.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,18 @@

using Apache.Arrow.Types;
using System;
using System.Collections.Generic;

namespace Apache.Arrow
{
/// <summary>
/// The <see cref="Date32Array"/> class holds an array of dates in the <c>Date32</c> format, where each date is
/// stored as the number of days since the dawn of (UNIX) time.
/// </summary>
public class Date32Array : PrimitiveArray<int>
public class Date32Array : PrimitiveArray<int>, IReadOnlyList<DateTime?>
#if NET6_0_OR_GREATER
, IReadOnlyList<DateOnly?>
#endif
{
private static readonly DateTime _epochDate = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Unspecified);
#if NET6_0_OR_GREATER
Expand Down Expand Up @@ -133,6 +137,30 @@ public Date32Array(ArrayData data)
? DateOnly.FromDayNumber(_epochDayNumber + value.Value)
: default(DateOnly?);
}

int IReadOnlyCollection<DateOnly?>.Count => Length;

DateOnly? IReadOnlyList<DateOnly?>.this[int index] => GetDateOnly(index);

IEnumerator<DateOnly?> IEnumerable<DateOnly?>.GetEnumerator()
{
for (int index = 0; index < Length; index++)
{
yield return GetDateOnly(index);
};
}
#endif

int IReadOnlyCollection<DateTime?>.Count => Length;

DateTime? IReadOnlyList<DateTime?>.this[int index] => GetDateTime(index);

IEnumerator<DateTime?> IEnumerable<DateTime?>.GetEnumerator()
{
for (int index = 0; index < Length; index++)
{
yield return GetDateTime(index);
};
}
}
}
32 changes: 30 additions & 2 deletions csharp/src/Apache.Arrow/Arrays/Date64Array.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

using Apache.Arrow.Types;
using System;
using System.Collections.Generic;

namespace Apache.Arrow
{
Expand All @@ -23,7 +24,10 @@ namespace Apache.Arrow
/// stored as the number of milliseconds since the dawn of (UNIX) time, excluding leap seconds, in multiples of
/// 86400000.
/// </summary>
public class Date64Array: PrimitiveArray<long>
public class Date64Array : PrimitiveArray<long>, IReadOnlyList<DateTime?>
#if NET6_0_OR_GREATER
, IReadOnlyList<DateOnly?>
#endif
{
private const long MillisecondsPerDay = 86400000;

Expand All @@ -39,7 +43,7 @@ public Date64Array(
/// </summary>
public class Builder : DateArrayBuilder<long, Date64Array, Builder>
{
private class DateBuilder: PrimitiveArrayBuilder<long, Date64Array, DateBuilder>
private class DateBuilder : PrimitiveArrayBuilder<long, Date64Array, DateBuilder>
{
protected override Date64Array Build(
ArrowBuffer valueBuffer, ArrowBuffer nullBitmapBuffer,
Expand Down Expand Up @@ -135,6 +139,30 @@ public Date64Array(ArrayData data)
? DateOnly.FromDateTime(DateTimeOffset.FromUnixTimeMilliseconds(value.Value).UtcDateTime)
: default(DateOnly?);
}

int IReadOnlyCollection<DateOnly?>.Count => Length;

DateOnly? IReadOnlyList<DateOnly?>.this[int index] => GetDateOnly(index);

IEnumerator<DateOnly?> IEnumerable<DateOnly?>.GetEnumerator()
{
for (int index = 0; index < Length; index++)
{
yield return GetDateOnly(index);
};
}
#endif

int IReadOnlyCollection<DateTime?>.Count => Length;

DateTime? IReadOnlyList<DateTime?>.this[int index] => GetDateTime(index);

IEnumerator<DateTime?> IEnumerable<DateTime?>.GetEnumerator()
{
for (int index = 0; index < Length; index++)
{
yield return GetDateTime(index);
};
}
}
}
22 changes: 21 additions & 1 deletion csharp/src/Apache.Arrow/Arrays/PrimitiveArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@
// limitations under the License.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

namespace Apache.Arrow
{
public abstract class PrimitiveArray<T> : Array
public abstract class PrimitiveArray<T> : Array, IReadOnlyList<T?>
where T : struct
{
protected PrimitiveArray(ArrayData data)
Expand Down Expand Up @@ -66,5 +67,24 @@ protected PrimitiveArray(ArrayData data)

return list;
}

int IReadOnlyCollection<T?>.Count => Length;
T? IReadOnlyList<T?>.this[int index] => GetValue(index);

IEnumerator<T?> IEnumerable<T?>.GetEnumerator()
{
for (int index = 0; index < Length; index++)
{
yield return IsValid(index) ? Values[index] : null;
}
}

IEnumerator IEnumerable.GetEnumerator()
{
for (int index = 0; index < Length; index++)
{
yield return IsValid(index) ? Values[index] : null;
}
}
}
}
17 changes: 16 additions & 1 deletion csharp/src/Apache.Arrow/Arrays/StringArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@

using Apache.Arrow.Types;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace Apache.Arrow
{
public class StringArray: BinaryArray
public class StringArray: BinaryArray, IReadOnlyList<string>
{
public static readonly Encoding DefaultEncoding = Encoding.UTF8;

Expand Down Expand Up @@ -91,5 +92,19 @@ public string GetString(int index, Encoding encoding = default)
return encoding.GetString(data, bytes.Length);
}
}

int IReadOnlyCollection<string>.Count => Length;

string IReadOnlyList<string>.this[int index] => GetString(index);

IEnumerator<string> IEnumerable<string>.GetEnumerator()
{
for (int index = 0; index < Length; index++)
{
yield return GetString(index);
};
}

IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<string>)this).GetEnumerator();
}
}
16 changes: 16 additions & 0 deletions csharp/src/Apache.Arrow/Arrays/Time32Array.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

using Apache.Arrow.Types;
using System;
using System.Collections.Generic;
using System.IO;

namespace Apache.Arrow
Expand All @@ -24,6 +25,9 @@ namespace Apache.Arrow
/// stored as the number of seconds/ milliseconds (depending on the Time32Type) since midnight.
/// </summary>
public class Time32Array : PrimitiveArray<int>
#if NET6_0_OR_GREATER
, IReadOnlyList<TimeOnly?>
#endif
{
/// <summary>
/// The <see cref="Builder"/> class can be used to fluently build <see cref="Time32Array"/> objects.
Expand Down Expand Up @@ -155,6 +159,18 @@ public Time32Array(ArrayData data)
_ => throw new InvalidDataException($"Unsupported time unit for Time32Type: {unit}")
};
}

int IReadOnlyCollection<TimeOnly?>.Count => Length;

TimeOnly? IReadOnlyList<TimeOnly?>.this[int index] => GetTime(index);

IEnumerator<TimeOnly?> IEnumerable<TimeOnly?>.GetEnumerator()
{
for (int index = 0; index < Length; index++)
{
yield return GetTime(index);
};
}
#endif
}
}
16 changes: 16 additions & 0 deletions csharp/src/Apache.Arrow/Arrays/Time64Array.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

using Apache.Arrow.Types;
using System;
using System.Collections.Generic;
using System.IO;

namespace Apache.Arrow
Expand All @@ -24,6 +25,9 @@ namespace Apache.Arrow
/// stored as the number of microseconds/nanoseconds (depending on the Time64Type) since midnight.
/// </summary>
public class Time64Array : PrimitiveArray<long>
#if NET6_0_OR_GREATER
, IReadOnlyList<TimeOnly?>
#endif
{
/// <summary>
/// The <see cref="Builder"/> class can be used to fluently build <see cref="Time64Array"/> objects.
Expand Down Expand Up @@ -146,6 +150,18 @@ public Time64Array(ArrayData data)

return new TimeOnly(((Time64Type)Data.DataType).Unit.ConvertToTicks(value.Value));
}

int IReadOnlyCollection<TimeOnly?>.Count => Length;

TimeOnly? IReadOnlyList<TimeOnly?>.this[int index] => GetTime(index);

IEnumerator<TimeOnly?> IEnumerable<TimeOnly?>.GetEnumerator()
{
for (int index = 0; index < Length; index++)
{
yield return GetTime(index);
};
}
#endif
}
}
14 changes: 13 additions & 1 deletion csharp/src/Apache.Arrow/Arrays/TimestampArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@

using Apache.Arrow.Types;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

namespace Apache.Arrow
{
public class TimestampArray: PrimitiveArray<long>
public class TimestampArray : PrimitiveArray<long>, IReadOnlyList<DateTimeOffset?>
{
private static readonly DateTimeOffset s_epoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, 0, TimeSpan.Zero);

Expand Down Expand Up @@ -145,5 +146,16 @@ public DateTimeOffset GetTimestampUnchecked(int index)
return GetTimestampUnchecked(index);
}

int IReadOnlyCollection<DateTimeOffset?>.Count => Length;

DateTimeOffset? IReadOnlyList<DateTimeOffset?>.this[int index] => GetTimestamp(index);

IEnumerator<DateTimeOffset?> IEnumerable<DateTimeOffset?>.GetEnumerator()
{
for (int index = 0; index < Length; index++)
{
yield return GetTimestamp(index);
};
}
}
}
Loading

0 comments on commit 0184ac0

Please sign in to comment.