Skip to content

Commit 905ecce

Browse files
committed
fix: int64 indexing migration batch 6 - overflow checks and loop counters
- NdArrayToJaggedArray.cs: Add overflow checks for managed array limits, explicit (int) casts with validation before allocation, loop comparisons against .Length instead of shape[x] - NDArray.matrix_power.cs: Add overflow check for np.eye dimension - NDArray.Indexing.Masking.cs: Fix loop counter int→long for mask.size iteration - Update INT64_MIGRATION_PROGRESS.md with session 5 fixes and audit results (np.load.cs, np.save.cs confirmed as valid exceptions)
1 parent 4bcd5f9 commit 905ecce

4 files changed

Lines changed: 128 additions & 57 deletions

File tree

docs/INT64_MIGRATION_PROGRESS.md

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,56 @@ This document tracks the progress of migrating recent commits to comply with the
1818

1919
---
2020

21+
## Completed Fixes (Session 5)
22+
23+
### 39. NDArray.Indexing.Masking.cs - Additional loop counter fix
24+
25+
**Location**: `src/NumSharp.Core/Selection/NDArray.Indexing.Masking.cs`
26+
27+
**Changes**:
28+
- `int destIdx = 0; for (int srcIdx = 0; srcIdx < mask.size; srcIdx++)``long destIdx = 0; for (long srcIdx = 0; ...)`
29+
30+
### 40. NdArrayToJaggedArray.cs - Overflow checks for managed array limits
31+
32+
**Location**: `src/NumSharp.Core/Casting/NdArrayToJaggedArray.cs`
33+
34+
**Changes**:
35+
- Added overflow checks for all cases (2D through 6D)
36+
- Added explicit `(int)shape[x]` casts with checks before managed array allocation
37+
- Changed loop comparisons from `shape[x]` to `ret.Length`, `ret[i].Length`, etc. (managed array lengths are int)
38+
- Example: `if (shape[0] > int.MaxValue || shape[1] > int.MaxValue) throw new InvalidOperationException(...)`
39+
- This is a valid exception per guide: managed arrays limited to int indices
40+
41+
### 41. NDArray.matrix_power.cs - Overflow check
42+
43+
**Location**: `src/NumSharp.Core/LinearAlgebra/NDArray.matrix_power.cs`
44+
45+
**Changes**:
46+
- Added overflow check: `if (product.shape[0] > int.MaxValue) throw new OverflowException(...)`
47+
- `np.eye` requires int parameter
48+
49+
---
50+
51+
## Audited - Valid Exceptions (No Changes Needed)
52+
53+
### np.load.cs / np.save.cs
54+
55+
**Status**: Valid Exception - No changes needed
56+
57+
**Reason**: These files interface with:
58+
1. The `.npy` file format which uses int32 for shape values
59+
2. Managed `Array` class which uses int indices (GetLength returns int)
60+
61+
Per the developer guide "Valid Exceptions" section: "Managed Array Allocation: .NET arrays limited to int indexing"
62+
63+
### Shape.cs explicit operators
64+
65+
**Status**: Already correctly implemented
66+
67+
The `explicit operator int[](Shape shape)` and `explicit operator int(Shape shape)` already have proper overflow checks with `OverflowException`.
68+
69+
---
70+
2171
## Completed Fixes (Session 4)
2272

2373
### 32. Default.All.cs - Loop counter fix
@@ -321,15 +371,20 @@ Tests that use `np.arange()` followed by `GetInt32()` need to be updated:
321371

322372
## Remaining Work
323373

324-
### Medium Priority - Not Yet Audited
374+
### Medium Priority - Audited (Valid Exceptions)
375+
376+
| File | Status |
377+
|------|--------|
378+
| `np.load.cs` | ✅ Valid exception - managed Array API + .npy format |
379+
| `np.save.cs` | ✅ Valid exception - managed Array API + .npy format |
380+
| `NdArray.ReShape.cs` | ✅ Has both `int[]` and `long[]` overloads (convenience) |
381+
| `NDArray`1.ReShape.cs` | ✅ Same - convenience overloads |
382+
383+
### Low Priority - API Compatibility
325384

326385
| File | Issue |
327386
|------|-------|
328-
| `np.load.cs` | `int[] shape` declarations |
329-
| `np.save.cs` | `int[] shape` declarations |
330-
| `NDArray.cs` | `int[] indices` parameters (may keep for API compat) |
331-
| `NdArray.ReShape.cs` | `int[] shape` parameters |
332-
| `NDArray`1.ReShape.cs` | `int[] shape` parameters |
387+
| `NDArray.cs` | `int[] indices` parameters - keep for API compat, delegates to long[] |
333388

334389
### User Request: Random Functions
335390

src/NumSharp.Core/Casting/NdArrayToJaggedArray.cs

Lines changed: 58 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -36,34 +36,40 @@ public Array ToJaggedArray<T>() where T : unmanaged
3636

3737
case 2:
3838
{
39-
T[][] ret = new T[shape[0]][];
39+
// Managed arrays limited to int indices
40+
if (shape[0] > int.MaxValue || shape[1] > int.MaxValue)
41+
throw new InvalidOperationException("Shape dimensions exceed managed array limits for jagged array conversion.");
42+
43+
T[][] ret = new T[(int)shape[0]][];
4044
for (int i = 0; i < ret.Length; i++)
41-
ret[i] = new T[shape[1]];
45+
ret[i] = new T[(int)shape[1]];
4246

4347
for (int i = 0; i < ret.Length; i++)
4448
for (int j = 0; j < ret[0].Length; j++)
4549
ret[i][j] = GetAtIndex<T>(shape.GetOffset(i, j));
4650

4751
return ret;
48-
49-
break;
5052
}
5153

5254
case 3:
5355
{
54-
T[][][] ret = new T[shape[0]][][];
56+
// Managed arrays limited to int indices
57+
if (shape[0] > int.MaxValue || shape[1] > int.MaxValue || shape[2] > int.MaxValue)
58+
throw new InvalidOperationException("Shape dimensions exceed managed array limits for jagged array conversion.");
59+
60+
T[][][] ret = new T[(int)shape[0]][][];
5561
for (int i = 0; i < ret.Length; i++)
5662
{
57-
ret[i] = new T[shape[1]][];
63+
ret[i] = new T[(int)shape[1]][];
5864
for (int j = 0; j < ret[i].Length; j++)
59-
ret[i][j] = new T[shape[2]];
65+
ret[i][j] = new T[(int)shape[2]];
6066
}
6167

62-
for (int i = 0; i < shape[0]; i++)
68+
for (int i = 0; i < ret.Length; i++)
6369
{
64-
for (int j = 0; j < shape[1]; j++)
70+
for (int j = 0; j < ret[i].Length; j++)
6571
{
66-
for (int k = 0; k < shape[2]; k++)
72+
for (int k = 0; k < ret[i][j].Length; k++)
6773
{
6874
ret[i][j][k] = (T)GetValue(i, j, k);
6975
}
@@ -75,27 +81,31 @@ public Array ToJaggedArray<T>() where T : unmanaged
7581

7682
case 4:
7783
{
78-
T[][][][] ret = new T[shape[0]][][][];
84+
// Managed arrays limited to int indices
85+
if (shape[0] > int.MaxValue || shape[1] > int.MaxValue || shape[2] > int.MaxValue || shape[3] > int.MaxValue)
86+
throw new InvalidOperationException("Shape dimensions exceed managed array limits for jagged array conversion.");
87+
88+
T[][][][] ret = new T[(int)shape[0]][][][];
7989
for (int i = 0; i < ret.Length; i++)
8090
{
81-
ret[i] = new T[shape[1]][][];
91+
ret[i] = new T[(int)shape[1]][][];
8292
for (int j = 0; j < ret[i].Length; j++)
8393
{
84-
ret[i][j] = new T[shape[2]][];
85-
for (int n = 0; n < ret[i].Length; n++)
94+
ret[i][j] = new T[(int)shape[2]][];
95+
for (int n = 0; n < ret[i][j].Length; n++)
8696
{
87-
ret[i][j][n] = new T[shape[3]];
97+
ret[i][j][n] = new T[(int)shape[3]];
8898
}
8999
}
90100
}
91101

92-
for (int i = 0; i < shape[0]; i++)
102+
for (int i = 0; i < ret.Length; i++)
93103
{
94-
for (int j = 0; j < shape[1]; j++)
104+
for (int j = 0; j < ret[i].Length; j++)
95105
{
96-
for (int k = 0; k < shape[2]; k++)
106+
for (int k = 0; k < ret[i][j].Length; k++)
97107
{
98-
for (int l = 0; l < shape[3]; l++)
108+
for (int l = 0; l < ret[i][j][k].Length; l++)
99109
{
100110
ret[i][j][k][l] = (T)GetValue(i, j, k, l);
101111
}
@@ -108,33 +118,38 @@ public Array ToJaggedArray<T>() where T : unmanaged
108118

109119
case 5:
110120
{
111-
T[][][][][] ret = new T[shape[0]][][][][];
121+
// Managed arrays limited to int indices
122+
if (shape[0] > int.MaxValue || shape[1] > int.MaxValue || shape[2] > int.MaxValue ||
123+
shape[3] > int.MaxValue || shape[4] > int.MaxValue)
124+
throw new InvalidOperationException("Shape dimensions exceed managed array limits for jagged array conversion.");
125+
126+
T[][][][][] ret = new T[(int)shape[0]][][][][];
112127
for (int i = 0; i < ret.Length; i++)
113128
{
114-
ret[i] = new T[shape[1]][][][];
129+
ret[i] = new T[(int)shape[1]][][][];
115130
for (int j = 0; j < ret[i].Length; j++)
116131
{
117-
ret[i][j] = new T[shape[2]][][];
118-
for (int n = 0; n < ret[i].Length; n++)
132+
ret[i][j] = new T[(int)shape[2]][][];
133+
for (int n = 0; n < ret[i][j].Length; n++)
119134
{
120-
ret[i][j][n] = new T[shape[3]][];
121-
for (int k = 0; k < ret[i].Length; k++)
135+
ret[i][j][n] = new T[(int)shape[3]][];
136+
for (int k = 0; k < ret[i][j][n].Length; k++)
122137
{
123-
ret[i][j][n][k] = new T[shape[4]];
138+
ret[i][j][n][k] = new T[(int)shape[4]];
124139
}
125140
}
126141
}
127142
}
128143

129-
for (int i = 0; i < shape[0]; i++)
144+
for (int i = 0; i < ret.Length; i++)
130145
{
131-
for (int j = 0; j < shape[1]; j++)
146+
for (int j = 0; j < ret[i].Length; j++)
132147
{
133-
for (int k = 0; k < shape[2]; k++)
148+
for (int k = 0; k < ret[i][j].Length; k++)
134149
{
135-
for (int l = 0; l < shape[3]; l++)
150+
for (int l = 0; l < ret[i][j][k].Length; l++)
136151
{
137-
for (int m = 0; m < shape[4]; m++)
152+
for (int m = 0; m < ret[i][j][k][l].Length; m++)
138153
{
139154
ret[i][j][k][l][m] = (T)GetValue(i, j, k, l, m);
140155
}
@@ -148,30 +163,26 @@ public Array ToJaggedArray<T>() where T : unmanaged
148163

149164
case 6:
150165
{
151-
T[][][] ret = new T[shape[0]][][];
166+
// NOTE: This case appears buggy - creates 3D array for 6D input
167+
// Managed arrays limited to int indices
168+
if (shape[0] > int.MaxValue || shape[1] > int.MaxValue || shape[2] > int.MaxValue)
169+
throw new InvalidOperationException("Shape dimensions exceed managed array limits for jagged array conversion.");
170+
171+
T[][][] ret = new T[(int)shape[0]][][];
152172
for (int i = 0; i < ret.Length; i++)
153173
{
154-
ret[i] = new T[shape[1]][];
174+
ret[i] = new T[(int)shape[1]][];
155175
for (int jdx = 0; jdx < ret[i].Length; jdx++)
156-
ret[i][jdx] = new T[shape[2]];
176+
ret[i][jdx] = new T[(int)shape[2]];
157177
}
158178

159-
for (int i = 0; i < shape[0]; i++)
179+
for (int i = 0; i < ret.Length; i++)
160180
{
161-
for (int j = 0; j < shape[1]; j++)
181+
for (int j = 0; j < ret[i].Length; j++)
162182
{
163-
for (int k = 0; k < shape[2]; k++)
183+
for (int k = 0; k < ret[i][j].Length; k++)
164184
{
165-
for (int l = 0; l < shape[3]; l++)
166-
{
167-
for (int m = 0; m < shape[4]; m++)
168-
{
169-
for (int n = 0; n < shape[5]; n++)
170-
{
171-
ret[i][j][k] = (T)GetValue(i, j, k);
172-
}
173-
}
174-
}
185+
ret[i][j][k] = (T)GetValue(i, j, k);
175186
}
176187
}
177188
}

src/NumSharp.Core/LinearAlgebra/NDArray.matrix_power.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,13 @@ public NDArray matrix_power(int power)
1414
for (int idx = 2; idx <= power; idx++)
1515
product = TensorEngine.Dot(product, this);
1616

17-
// Matrix dimensions are typically small; np.eye expects int
18-
product = (power == 0) ? np.eye((int)product.shape[0]) : product;
17+
// np.eye expects int; matrix dimensions typically small but check anyway
18+
if (power == 0)
19+
{
20+
if (product.shape[0] > int.MaxValue)
21+
throw new OverflowException($"Matrix dimension {product.shape[0]} exceeds int.MaxValue for np.eye");
22+
product = np.eye((int)product.shape[0]);
23+
}
1924

2025
return product;
2126
}

src/NumSharp.Core/Selection/NDArray.Indexing.Masking.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,8 @@ private NDArray BooleanMaskAxis0(NDArray<bool> mask)
259259
var result = new NDArray(this.dtype, new Shape(resultShape));
260260

261261
// Copy selected slices
262-
int destIdx = 0;
263-
for (int srcIdx = 0; srcIdx < mask.size; srcIdx++)
262+
long destIdx = 0;
263+
for (long srcIdx = 0; srcIdx < mask.size; srcIdx++)
264264
{
265265
if (mask.GetBoolean(srcIdx))
266266
{

0 commit comments

Comments
 (0)