Skip to content

Commit bf3fec9

Browse files
Merge pull request #35 from estebanzimanyi/fix/scalar-array-element-width
Read an array of scalars at the scalar's own width
2 parents 977217a + 5051074 commit bf3fec9

11 files changed

Lines changed: 125 additions & 31 deletions

File tree

‎MEOS.NET.Tests/ScalarArrayTests.cs‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using MEOS.NET.Types;
2+
3+
namespace MEOS.NET.Tests
4+
{
5+
/// <summary>
6+
/// An array of scalars is read at the scalar's own width. Reading one as an
7+
/// array of pointers walks it eight bytes at a step, which runs off the end
8+
/// of a bool array and answers the values' own bytes as addresses.
9+
/// </summary>
10+
[TestClass]
11+
public class ScalarArrayTests : MeosTest
12+
{
13+
[TestMethod]
14+
public void ATemporalBooleanAnswersItsValuesAsBooleans()
15+
{
16+
TBool temp = (TBool)TBool.In(
17+
"{true@2024-12-06, false@2024-12-07, true@2024-12-08}")!;
18+
19+
bool[] values = temp.Values();
20+
21+
Assert.AreEqual(2, values.Length);
22+
CollectionAssert.AreEquivalent(new[] { false, true }, values);
23+
}
24+
25+
[TestMethod]
26+
public void ATemporalFloatAnswersItsValuesAsDoubles()
27+
{
28+
TFloat temp = (TFloat)TFloat.In(
29+
"{1.5@2024-12-06, 2.5@2024-12-07}")!;
30+
31+
double[] values = temp.Values();
32+
33+
Assert.AreEqual(2, values.Length);
34+
CollectionAssert.AreEquivalent(new[] { 1.5, 2.5 }, values);
35+
}
36+
}
37+
}

‎MEOS.NET/Functions/Meos.meos.g.cs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2905,16 +2905,16 @@ public static bool TboolValueAtTimestamptz(IntPtr temp, long t, bool strict, Int
29052905
public static bool TboolValueN(IntPtr temp, int n, IntPtr result)
29062906
=> SafeExecution<bool>(() => Native.TboolValueN(temp, n, result));
29072907

2908-
public static IntPtr[] TboolValues(IntPtr temp)
2908+
public static bool[] TboolValues(IntPtr temp)
29092909
{
29102910
IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
29112911
try
29122912
{
29132913
IntPtr _p = SafeExecution<IntPtr>(() => Native.TboolValues(temp, _cnt));
29142914
int _n = Marshal.ReadInt32(_cnt);
2915-
IntPtr[] _out = new IntPtr[_n];
2915+
bool[] _out = new bool[_n];
29162916
for (int _i = 0; _i < _n; _i++)
2917-
{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
2917+
{ _out[_i] = Marshal.ReadByte(_p, _i) != 0; }
29182918
return _out;
29192919
}
29202920
finally { Marshal.FreeHGlobal(_cnt); }

‎MEOS.NET/Functions/Meos.meos_geo.g.cs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1736,16 +1736,16 @@ public static int[] GeoClusterKmeans(IntPtr geoms, uint ngeoms, uint k)
17361736
finally { Marshal.FreeHGlobal(_cnt); }
17371737
}
17381738

1739-
public static IntPtr[] GeoClusterDbscan(IntPtr geoms, uint ngeoms, double tolerance, int minpoints)
1739+
public static uint[] GeoClusterDbscan(IntPtr geoms, uint ngeoms, double tolerance, int minpoints)
17401740
{
17411741
IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
17421742
try
17431743
{
17441744
IntPtr _p = SafeExecution<IntPtr>(() => Native.GeoClusterDbscan(geoms, ngeoms, tolerance, minpoints, _cnt));
17451745
int _n = Marshal.ReadInt32(_cnt);
1746-
IntPtr[] _out = new IntPtr[_n];
1746+
uint[] _out = new uint[_n];
17471747
for (int _i = 0; _i < _n; _i++)
1748-
{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
1748+
{ _out[_i] = (uint) Marshal.ReadInt32(_p, _i * 4); }
17491749
return _out;
17501750
}
17511751
finally { Marshal.FreeHGlobal(_cnt); }

‎MEOS.NET/Functions/Meos.meos_h3.g.cs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,16 +135,16 @@ public static ulong Th3indexEndValue(IntPtr temp)
135135
public static bool Th3indexValueN(IntPtr temp, int n, IntPtr result)
136136
=> SafeExecution<bool>(() => Native.Th3indexValueN(temp, n, result));
137137

138-
public static IntPtr[] Th3indexValues(IntPtr temp)
138+
public static ulong[] Th3indexValues(IntPtr temp)
139139
{
140140
IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
141141
try
142142
{
143143
IntPtr _p = SafeExecution<IntPtr>(() => Native.Th3indexValues(temp, _cnt));
144144
int _n = Marshal.ReadInt32(_cnt);
145-
IntPtr[] _out = new IntPtr[_n];
145+
ulong[] _out = new ulong[_n];
146146
for (int _i = 0; _i < _n; _i++)
147-
{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
147+
{ _out[_i] = (ulong) Marshal.ReadInt64(_p, _i * 8); }
148148
return _out;
149149
}
150150
finally { Marshal.FreeHGlobal(_cnt); }

‎MEOS.NET/Functions/Meos.meos_json.g.cs‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -462,16 +462,16 @@ public static IntPtr JsonbsetDelete(IntPtr set, IntPtr key)
462462
public static IntPtr JsonbsetDeleteArray(IntPtr set, IntPtr keys, int count)
463463
=> SafeExecution<IntPtr>(() => Native.JsonbsetDeleteArray(set, keys, count));
464464

465-
public static IntPtr[] JsonbsetExists(IntPtr set, IntPtr key)
465+
public static bool[] JsonbsetExists(IntPtr set, IntPtr key)
466466
{
467467
IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
468468
try
469469
{
470470
IntPtr _p = SafeExecution<IntPtr>(() => Native.JsonbsetExists(set, key, _cnt));
471471
int _n = Marshal.ReadInt32(_cnt);
472-
IntPtr[] _out = new IntPtr[_n];
472+
bool[] _out = new bool[_n];
473473
for (int _i = 0; _i < _n; _i++)
474-
{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
474+
{ _out[_i] = Marshal.ReadByte(_p, _i) != 0; }
475475
return _out;
476476
}
477477
finally { Marshal.FreeHGlobal(_cnt); }
@@ -513,31 +513,31 @@ public static IntPtr JsonbsetExtractPath(IntPtr set, IntPtr path_elems, int path
513513
public static IntPtr JsonbsetInsert(IntPtr set, IntPtr path_elems, int path_len, IntPtr newjb, bool after)
514514
=> SafeExecution<IntPtr>(() => Native.JsonbsetInsert(set, path_elems, path_len, newjb, after));
515515

516-
public static IntPtr[] JsonbsetPathExists(IntPtr set, IntPtr jp, IntPtr vars, bool silent, bool tz)
516+
public static bool[] JsonbsetPathExists(IntPtr set, IntPtr jp, IntPtr vars, bool silent, bool tz)
517517
{
518518
IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
519519
try
520520
{
521521
IntPtr _p = SafeExecution<IntPtr>(() => Native.JsonbsetPathExists(set, jp, vars, silent, tz, _cnt));
522522
int _n = Marshal.ReadInt32(_cnt);
523-
IntPtr[] _out = new IntPtr[_n];
523+
bool[] _out = new bool[_n];
524524
for (int _i = 0; _i < _n; _i++)
525-
{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
525+
{ _out[_i] = Marshal.ReadByte(_p, _i) != 0; }
526526
return _out;
527527
}
528528
finally { Marshal.FreeHGlobal(_cnt); }
529529
}
530530

531-
public static IntPtr[] JsonbsetPathMatch(IntPtr set, IntPtr jp, IntPtr vars, bool silent, bool tz)
531+
public static bool[] JsonbsetPathMatch(IntPtr set, IntPtr jp, IntPtr vars, bool silent, bool tz)
532532
{
533533
IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
534534
try
535535
{
536536
IntPtr _p = SafeExecution<IntPtr>(() => Native.JsonbsetPathMatch(set, jp, vars, silent, tz, _cnt));
537537
int _n = Marshal.ReadInt32(_cnt);
538-
IntPtr[] _out = new IntPtr[_n];
538+
bool[] _out = new bool[_n];
539539
for (int _i = 0; _i < _n; _i++)
540-
{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
540+
{ _out[_i] = Marshal.ReadByte(_p, _i) != 0; }
541541
return _out;
542542
}
543543
finally { Marshal.FreeHGlobal(_cnt); }

‎MEOS.NET/Functions/Meos.meos_quadbin.g.cs‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,16 @@ public static uint QuadbinGetResolution(ulong cell)
5757
public static ulong QuadbinCellToParent(ulong cell, uint parent_resolution)
5858
=> SafeExecution<ulong>(() => Native.QuadbinCellToParent(cell, parent_resolution));
5959

60-
public static IntPtr[] QuadbinCellToChildren(ulong cell, uint children_resolution)
60+
public static ulong[] QuadbinCellToChildren(ulong cell, uint children_resolution)
6161
{
6262
IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
6363
try
6464
{
6565
IntPtr _p = SafeExecution<IntPtr>(() => Native.QuadbinCellToChildren(cell, children_resolution, _cnt));
6666
int _n = Marshal.ReadInt32(_cnt);
67-
IntPtr[] _out = new IntPtr[_n];
67+
ulong[] _out = new ulong[_n];
6868
for (int _i = 0; _i < _n; _i++)
69-
{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
69+
{ _out[_i] = (ulong) Marshal.ReadInt64(_p, _i * 8); }
7070
return _out;
7171
}
7272
finally { Marshal.FreeHGlobal(_cnt); }
@@ -75,16 +75,16 @@ public static IntPtr[] QuadbinCellToChildren(ulong cell, uint children_resolutio
7575
public static ulong QuadbinCellSibling(ulong cell, string direction)
7676
=> SafeExecution<ulong>(() => Native.QuadbinCellSibling(cell, direction));
7777

78-
public static IntPtr[] QuadbinKRing(ulong cell, int k)
78+
public static ulong[] QuadbinKRing(ulong cell, int k)
7979
{
8080
IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
8181
try
8282
{
8383
IntPtr _p = SafeExecution<IntPtr>(() => Native.QuadbinKRing(cell, k, _cnt));
8484
int _n = Marshal.ReadInt32(_cnt);
85-
IntPtr[] _out = new IntPtr[_n];
85+
ulong[] _out = new ulong[_n];
8686
for (int _i = 0; _i < _n; _i++)
87-
{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
87+
{ _out[_i] = (ulong) Marshal.ReadInt64(_p, _i * 8); }
8888
return _out;
8989
}
9090
finally { Marshal.FreeHGlobal(_cnt); }
@@ -162,16 +162,16 @@ public static ulong TquadbinEndValue(IntPtr temp)
162162
public static bool TquadbinValueN(IntPtr temp, int n, IntPtr result)
163163
=> SafeExecution<bool>(() => Native.TquadbinValueN(temp, n, result));
164164

165-
public static IntPtr[] TquadbinValues(IntPtr temp)
165+
public static ulong[] TquadbinValues(IntPtr temp)
166166
{
167167
IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
168168
try
169169
{
170170
IntPtr _p = SafeExecution<IntPtr>(() => Native.TquadbinValues(temp, _cnt));
171171
int _n = Marshal.ReadInt32(_cnt);
172-
IntPtr[] _out = new IntPtr[_n];
172+
ulong[] _out = new ulong[_n];
173173
for (int _i = 0; _i < _n; _i++)
174-
{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
174+
{ _out[_i] = (ulong) Marshal.ReadInt64(_p, _i * 8); }
175175
return _out;
176176
}
177177
finally { Marshal.FreeHGlobal(_cnt); }

‎MEOS.NET/Functions/Meos.meos_raster.g.cs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,16 +147,16 @@ public static IntPtr RasterTileValue(IntPtr traj, IntPtr rq)
147147
public static IntPtr RasterTileValueArray(IntPtr traj, IntPtr rqarr, int count)
148148
=> SafeExecution<IntPtr>(() => Native.RasterTileValueArray(traj, rqarr, count));
149149

150-
public static IntPtr[] TrajectoryQuadbins(IntPtr traj, uint zoom)
150+
public static ulong[] TrajectoryQuadbins(IntPtr traj, uint zoom)
151151
{
152152
IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
153153
try
154154
{
155155
IntPtr _p = SafeExecution<IntPtr>(() => Native.TrajectoryQuadbins(traj, zoom, _cnt));
156156
int _n = Marshal.ReadInt32(_cnt);
157-
IntPtr[] _out = new IntPtr[_n];
157+
ulong[] _out = new ulong[_n];
158158
for (int _i = 0; _i < _n; _i++)
159-
{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
159+
{ _out[_i] = (ulong) Marshal.ReadInt64(_p, _i * 8); }
160160
return _out;
161161
}
162162
finally { Marshal.FreeHGlobal(_cnt); }

‎MEOS.NET/Types/Geo.g.cs‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,25 @@ public ulong ToS2cellCell(int level)
114114
public STBox? TstzspanToStbox(Span s)
115115
=> MEOSFactory.WrapSTBox(Meos.GeoTstzspanToStbox(this.Ptr, s.Ptr));
116116

117+
public static uint[] ClusterDbscan(Geo[] geoms, double tolerance, int minpoints)
118+
{
119+
IntPtr[] _geomsValues = new IntPtr[geoms.Length];
120+
for (int i = 0; i < geoms.Length; i++)
121+
{
122+
_geomsValues[i] = geoms[i].Ptr;
123+
}
124+
125+
GCHandle _geoms = GCHandle.Alloc(_geomsValues, GCHandleType.Pinned);
126+
try
127+
{
128+
return Meos.GeoClusterDbscan(_geoms.AddrOfPinnedObject(), (uint) geoms.Length, tolerance, minpoints);
129+
}
130+
finally
131+
{
132+
_geoms.Free();
133+
}
134+
}
135+
117136
public static Geo?[] ClusterIntersecting(Geo[] geoms)
118137
{
119138
IntPtr[] _geomsValues = new IntPtr[geoms.Length];

‎MEOS.NET/Types/TBool.g.cs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ public bool StartValue()
7272
}
7373
}
7474

75+
public bool[] Values()
76+
=> Meos.TboolValues(this.Ptr);
77+
7578
public SpanSet? WhenTrue()
7679
=> MEOSFactory.WrapSpanSet(Meos.TboolWhenTrue(this.Ptr));
7780

‎tools/codegen.py‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,20 @@ def gen_external_functions(funcs: list[dict]) -> str:
433433
"short": "short",
434434
}
435435

436+
# How to read one element of a scalar array `Marshal.Copy` has no overload for.
437+
# `{0}` is the array's base pointer and `{1}` the byte offset of the element.
438+
# Reading such an array as an array of POINTERS walks it at eight bytes a step,
439+
# which runs off the end of a `bool` array and answers addresses that are the
440+
# values' own bytes.
441+
_SCALAR_ELEM_READER: dict[str, tuple[int, str]] = {
442+
"bool": (1, "Marshal.ReadByte({0}, {1}) != 0"),
443+
"sbyte": (1, "(sbyte) Marshal.ReadByte({0}, {1})"),
444+
"ushort": (2, "(ushort) Marshal.ReadInt16({0}, {1})"),
445+
"uint": (4, "(uint) Marshal.ReadInt32({0}, {1})"),
446+
"ulong": (8, "(ulong) Marshal.ReadInt64({0}, {1})"),
447+
"float": (4, "BitConverter.Int32BitsToSingle(Marshal.ReadInt32({0}, {1}))"),
448+
}
449+
436450

437451
def _strip_const_stars(c_type: str) -> tuple[str, int]:
438452
s = c_type.replace("const ", "").strip()
@@ -462,6 +476,8 @@ def _csharp_array_element(c_type: str, canonical: str) -> tuple[str, str]:
462476
return (elem, "Marshal.Copy")
463477
if base == "uint8_t":
464478
return ("byte", "ByteBuffer")
479+
if elem in _SCALAR_ELEM_READER:
480+
return (elem, f"ScalarArray:{elem}")
465481
if stars == 1 and base in STRUCTS:
466482
# A single pointer to a catalog struct is an array of struct VALUES, not
467483
# of pointers: element i sits at the struct's own stride, and reading it
@@ -567,6 +583,12 @@ def _emit_outputs_wrapper(f: dict) -> list[str]:
567583
stride = ret_strategy.split(":", 1)[1]
568584
lines.append(" for (int _i = 0; _i < _n; _i++)")
569585
lines.append(f" {{ _resultArr[_i] = IntPtr.Add(_resultPtr, _i * {stride}); }}")
586+
elif ret_strategy.startswith("ScalarArray:"):
587+
size, reader = _SCALAR_ELEM_READER[ret_strategy.split(":", 1)[1]]
588+
offset = "_i" if size == 1 else f"_i * {size}"
589+
lines.append(" for (int _i = 0; _i < _n; _i++)")
590+
lines.append(f" {{ _resultArr[_i] = "
591+
f"{reader.format('_resultPtr', offset)}; }}")
570592
else:
571593
lines.append(" for (int _i = 0; _i < _n; _i++)")
572594
lines.append(" { _resultArr[_i] = Marshal.ReadIntPtr(_resultPtr, _i * IntPtr.Size); }")
@@ -577,6 +599,12 @@ def _emit_outputs_wrapper(f: dict) -> list[str]:
577599
stride = strategy.split(":", 1)[1]
578600
lines.append(f" for (int _i = 0; _i < _n; _i++)")
579601
lines.append(f" {{ _{local}_out[_i] = IntPtr.Add(_{local}_arr, _i * {stride}); }}")
602+
elif strategy.startswith("ScalarArray:"):
603+
size, reader = _SCALAR_ELEM_READER[strategy.split(":", 1)[1]]
604+
offset = "_i" if size == 1 else f"_i * {size}"
605+
lines.append(f" for (int _i = 0; _i < _n; _i++)")
606+
lines.append(f" {{ _{local}_out[_i] = "
607+
f"{reader.format(f'_{local}_arr', offset)}; }}")
580608
elif elem == "IntPtr":
581609
lines.append(f" for (int _i = 0; _i < _n; _i++)")
582610
lines.append(f" {{ _{local}_out[_i] = Marshal.ReadIntPtr(_{local}_arr, _i * IntPtr.Size); }}")
@@ -623,6 +651,11 @@ def _copy(indent: str) -> list[str]:
623651
stride = strategy.split(":", 1)[1]
624652
out.append(f"{indent}for (int _i = 0; _i < _n; _i++)")
625653
out.append(f"{indent}{{ _out[_i] = IntPtr.Add(_p, _i * {stride}); }}")
654+
elif strategy.startswith("ScalarArray:"):
655+
size, reader = _SCALAR_ELEM_READER[strategy.split(":", 1)[1]]
656+
offset = "_i" if size == 1 else f"_i * {size}"
657+
out.append(f"{indent}for (int _i = 0; _i < _n; _i++)")
658+
out.append(f"{indent}{{ _out[_i] = {reader.format('_p', offset)}; }}")
626659
else:
627660
out.append(f"{indent}for (int _i = 0; _i < _n; _i++)")
628661
out.append(f"{indent}{{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }}")

0 commit comments

Comments
 (0)