Array.MaxLength を超えたサイズの配列を扱えるライブラリを作ってみた

タイトルの通り，長大サイズの配列を提供するライブラリを作ったので，それに合わせて紹介記事も書いてみました。
尚，作ってみたいという動機だけで開発したため，使う機会があるかどうかや実用性については私にも分かりません。

## ライブラリ情報

- 名称：LongArrayLib
- 使用言語：C#
- Github: https://github.com/Funny-Silkie/LongArrayLib
- Nuget：未整備，需要があれば検討
- 対象バージョン：.NET7.0 & .NET8.0
- ライセンス：MIT

## 実装

`LongArray<T>` クラスがその配列クラスに該当します。
インデクサ，配列長共に64-bit符号付き整数（`long`）で定義されており，理論上は `long.MaxValue` まで要素数を設定できます。
ここで「理論上」としているのは，要素数を `long.MaxValue` （ $2^{63}-1$）とすると `byte` 配列でもメモリ上のサイズが約8 EiBになるためです。

取り回しは.NETの配列（`T[]`）に極力寄せており，検索やコピー，ソートといった一連のメソッドが用意されています。
.NETの配列と異なる要素としては，`IndexOf()` などの検索系のメソッドや `ForEach()` といった要素に変更を与えないメソッドは基本的に `LongArray<T>` に直接実装している点があります。
これはコードを書く上で一々 `Array.XXX(array)` とするより `array.XXX()` とした方がすっきりするためです。
`Resize()` や `Sort()` といった要素に変更の生じる処理は `LongArray<T>` クラスには実装されておらず，静的クラスである `LongArray` に実装されています。
また，内部で `malloc` をしている都合上， `IDisposable` を実装しています。
使い終わったら `Dispose()` を呼び出してメモリを解放する必要があります。

```csharp
using LongArrayLib;

// 長さ5の配列を生成
using var array = new LongArray<long>(5L);
// 要素を設定
for (long i = 0L; i < array.Length; i++) array[i] = -i;

// 検索などは LongArray<T> インスタンスから呼び出し
Console.WriteLine(array.IndexOf(-2L)); // 出力：2
// ソートなどは LongArray クラスから呼び出し
LongArray.Sort(array); // ソート

Console.WriteLine(array.FindLast(x => x < 0L)); // 出力：-1
```

`LongArray<T>` は.NETの配列と同じように `LongArray<T>` は読み取り専用コレクションとして実装されています。
そのため `ICollection<T>` インターフェイス経由で `Add(T)` や `Clear()` を呼び出そうとしても `NotSupportedException` がスローされます。
インデクサに関してもsetterは存在せず，getterで `ref T` を返すことで値の設定を可能にしています。

当ライブラリでは `LongArray<T>` の実装だけでなく，既存の型との互換性も考慮しており，`Span<T>` や `Memory<T>` ， `ReadOnlySequence<T>` ， `T[]` との相互変換の他，LINQには `ToLongArray<T>(IEnumerable<T>)` ， `Stream` には `Read(LongArray<T>, long, long)` や `Write(LongArray<T>, long, long)` といった拡張も定義されています。

```csharp
using LongArrayLib;
using System.Buffers;

var array = new byte[4096];
using var stream = new FileStream(..);

// LINQの拡張，IEnumerable<T> → LongArray<T>
using LongArray<byte> buffer = array.ToLongArray();

// Stream の拡張（Async版もあり）
stream.Write(buffer, 0L, buffer.Length);
stream.Read(buffer, 0L, buffer.Length);

// 型変換
Span<byte> span = buffer.AsSpan();
Memory<byte> memory = buffer.AsMemory();
ReadOnlySequence<byte> sequence = buffer.AsReadOnlySequence();
```

そしてC#12で新たに実装された[コレクション式](https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/operators/collection-expressions)にも対応しています。

```csharp
using LongArrayLib;

// コレクション式からインスタンス生成
using LongArray<int> array = [0, 1, 2, 3, 4];
Console.WriteLine(string.Join(',', array)); // 出力：0,1,2,3,4
```

## 機能一覧

`LongArray<T>` ・ `LongArray` の実装に絞っています。
これ以外に様々な拡張メソッドが記述されており，既存の型との互換性を担保しています。

### 生成

|機能名|対応する.NETの実装|備考|
|:-:|:-:|:-|
|`new LongArray<T>(long, bool)` | `new T[int]` | 第二引数を `false` にするとゼロ埋めをしない |
| `LongArray.Create<T>(params T[])` | N/A ||
| `LongArray.Create<T>(ReadOnlySpan<T>` | N/A | コレクション式対応用|

### 列挙

|機能名|対応する.NETの実装|備考|
|:-:|:-:|:-|
| `LongArray<T>.GetEnumerator()`| `T[].GetEnumerator()` | `LongArray<T>.Enumerator` は `IEnumerator<T>` を実装 |
| `LongArray<T>.GetSpanEnumerator(int)` | N/A | `ReadOnly<T>` に分割して列挙，`foreach` に入れて回すことも可能|
| `LongArray<T>.ForEach(Action<T>) | `Array.ForEach<T>(Action<T>)`||
| `LongArray<T>.ForEachChunk<TArg>(ReadOnlySpanAction<T, TArg>, TArg, int)` | N/A | `ReadOnly<T>` に分割して処理|

### 取得

|機能名|対応する.NETの実装|備考|
|:-:|:-:|:-|
| `LongArray<T>.this[long]` | `T[int]` | `ref T` を返す |
| `LongArray<T>.this[int]` | `T[int]` | `ref T` を返す |
| `LongArray<T>.this[Index]` | `T[Index]` | `ref T` を返す |
| `LongArray<T>.this[Range]` | `T[Range]` | `LongArray<T>` を返す |
| `LongArray<T>.GetRange(long, long)` | N/A | `List<T>.GetRange(int, int)` と同じ取り回し|

### コピー

|機能名|対応する.NETの実装|備考|
|:-:|:-:|:-|
| `LongArray<T>.CopyTo(T[], int)` | `T[].CopyTo(Array, int)` ||
| `LongArray<T>.CopyTo(LongArray<T>, int)` | `T[].CopyTo(Array, int)` ||
| `LongArray<T>.CopyTo(Memory<T>)` | `MemoryExtensions.CopyTo(this T[], Memory<T>)` ||
| `LongArray<T>.CopyTo(Span<T>)` | `MemoryExtensions.CopyTo(this T[], Span<T>)` ||
| `LongArray.Copy<T>(LongArray<T>, LongArray<T>, long)` | `Array.Copy(Array, Array, int)` ||
| `LongArray.Copy<T>(T[], LongArray<T>, int)` | `Array.Copy(Array, Array, int)` | .NET配列 → `LongArray<T>`へのコピー |
| `LongArray.Copy<T>(LongArray<T>, T[], int)` | `Array.Copy(Array, Array, int)` | `LongArray<T>` → .NET配列へのコピー |
| `LongArray.Copy<T>(LongArray<T>, long, LongArray<T>, long, long)` | `Array.Copy(Array, int, Array, int, int)` ||
| `LongArray.Copy<T>(T[], int, LongArray<T>, long, int)` | `Array.Copy(Array, int, Array, int, int)` | .NET配列 → `LongArray<T>`へのコピー |
| `LongArray.Copy<T>(LongArray<T>, long, T[], int, int)` | `Array.Copy(Array, int, Array, int, int)` | `LongArray<T>` → .NET配列へのコピー|
| `LongArray<T>.Clone()` | `T[].Clone()` ||

### 検索

|機能名|対応する.NETの実装|備考|
|:-:|:-:|:-|
| `LongArray<T>.Contains(T)` | `Array.Contains(T)` ||
| `LongArray<T>.IndexOf(T)` | `Array.IndexOf(T)` ||
| `LongArray<T>.IndexOf(T, long)` | `Array.IndexOf(T), int` ||
| `LongArray<T>.IndexOf(T, long, long)` | `Array.IndexOf(T), int, int` ||
| `LongArray<T>.LastIndexOf(T)` | `Array.LastIndexOf(T)` ||
| `LongArray<T>.LastIndexOf(T, long)` | `Array.LastIndexOf(T), int` ||
| `LongArray<T>.LastIndexOf(T, long, long)` | `Array.LastIndexOf(T), int, int` ||
| `LongArray<T>.Exists(Predicate<T>)` | `Array.Exists(Predicate<T>)` ||
| `LongArray<T>.TrueForAll(Predicate<T>)` | `Array.TrueForAll(Predicate<T>)` ||
| `LongArray<T>.Find(Predicate<T>)` | `Array.Find(Predicate<T>)` ||
| `LongArray<T>.FindIndex(Predicate<T>)` | `Array.FindIndex(Predicate<T>)` ||
| `LongArray<T>.FindIndex(Predicate<T>, long)` | `Array.FindIndex(Predicate<T>, int)` ||
| `LongArray<T>.FindIndex(Predicate<T>, long, long)` | `Array.FindIndex(Predicate<T>, int, int)` ||
| `LongArray<T>.FindLast(Predicate<T>)` | `Array.FindLast(Predicate<T>)` ||
| `LongArray<T>.FindLastIndex(Predicate<T>)` | `Array.FindLastIndex(Predicate<T>)` ||
| `LongArray<T>.FindLastIndex(Predicate<T>, long)` | `Array.FindLastIndex(Predicate<T>, int)` ||
| `LongArray<T>.FindLastIndex(Predicate<T>, long, long)` | `Array.FindLastIndex(Predicate<T>, int, int)` ||
| `LongArray<T>.FindAll(Predicate<T>)` | `Array.FindAll(Predicate<T>)` ||
| `LongArray<T>.BinarySearch(T)` | `Array.BinarySearch(T)` ||
| `LongArray<T>.BinarySearch(T, IComparer<T>?)` | `Array.BinarySearch(T, IComparer<T>?)` ||
| `LongArray<T>.BinarySearch(long, long, T)` | `Array.BinarySearch(long, long, T)` ||
| `LongArray<T>.BinarySearch(long, long, T, IComparer<T>?)` | `Array.BinarySearch(long, long, T, IComparer<T>?)` ||

### 編集

|機能名|対応する.NETの実装|備考|
|:-:|:-:|:-|
| `LongArray.Resize<T>(ref LongArray<T>? long)` | `Array.Resize<T>(ref T[]?, int)` ||
| `LongArray.Clear<T>(LongArray<T>)` | `Array.Clear<T>(T[])` ||
| `LongArray.Clear<T>(LongArray<T>, long, long)` | `Array.Clear<T>(T[], int, int)` ||
| `LongArray.Fill<T>(LongArray<T>, T)` | `Array.Fill<T>(T[], T)` ||
| `LongArray.Fill<T>(LongArray<T>, T, long, long)` | `Array.Fill<T>(T[], T, int, int)` ||

### ソート

|機能名|対応する.NETの実装|備考|
|:-:|:-:|:-|
| `LongArray.Sort<T>(LongArray<T>)` | `Array.Sort<T>(T[])` ||
| `LongArray.Sort<T>(LongArray<T>, IComparer<T>?)` | `Array.Sort<T>(T[], IComparer<T>?)` ||
| `LongArray.Sort<T>(LongArray<T>, long, long)` | `Array.Sort<T>(T[], int, int)` ||
| `LongArray.Sort<T>(LongArray<T>, long, long, IComparer<T>?)` | `Array.Sort<T>(T[], int, int, IComparer<T>?)` ||
| `LongArray.Sort<T>(LongArray<T>, Comparison<T>)` | `Array.Sort<T>(T[], Comparison<T>)` ||
| `LongArray.Sort<TKey, TValue>(LongArray<TKey>, LongArray<TValue>?)` | `Array.Sort<TKey>(TKey[], TValue[]?)` ||
| `LongArray.Sort<TKey, TValue>(LongArray<TKey>, LongArray<TValue>?, IComparer<TKey>?)` | `Array.Sort<TKey>(TKey[], TValue[]?, IComparer<TKey>?)` ||
| `LongArray.Sort<TKey, TValue>(LongArray<TKey>, LongArray<TValue>?, long, long)` | `Array.Sort<TKey>(TKey[], TValue[]?, int, int)` ||
| `LongArray.Sort<TKey, TValue>(LongArray<TKey>, LongArray<TValue>?, long, long, IComparer<TKey>?)` | `Array.Sort<TKey>(TKey[], TValue[]?, int, int, IComparer<TKey>?)` ||
| `LongArray.Reverse<T>(LongArray<T>)` | `Array.Reverse<T>(T[])` ||
| `LongArray.Reverse<T>(LongArray<T>, long, long)` | `Array.Reverse<T>(T[], int, int)` ||

### 変換

|機能名|対応する.NETの実装|備考|
|:-:|:-:|:-|
| `LongArray<T>.ConvertAll<TOut>(Converter<T, TOut>)` | `Array.ConvertAll<TIn, TOut>(Converter<TIn, TOut>)` ||
| `LongArray<T>.ToArray()` | N/A | `T[]` に変換 |
| `LongArray<T>.AsPointer()` | N/A | `void*` に変換 |