Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions MEOS.NET.Tests/ScalarArrayTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using MEOS.NET.Types;

namespace MEOS.NET.Tests
{
/// <summary>
/// An array of scalars is read at the scalar's own width. Reading one as an
/// array of pointers walks it eight bytes at a step, which runs off the end
/// of a bool array and answers the values' own bytes as addresses.
/// </summary>
[TestClass]
public class ScalarArrayTests : MeosTest
{
[TestMethod]
public void ATemporalBooleanAnswersItsValuesAsBooleans()
{
TBool temp = (TBool)TBool.In(
"{true@2024-12-06, false@2024-12-07, true@2024-12-08}")!;

bool[] values = temp.Values();

Assert.AreEqual(2, values.Length);
CollectionAssert.AreEquivalent(new[] { false, true }, values);
}

[TestMethod]
public void ATemporalFloatAnswersItsValuesAsDoubles()
{
TFloat temp = (TFloat)TFloat.In(
"{1.5@2024-12-06, 2.5@2024-12-07}")!;

double[] values = temp.Values();

Assert.AreEqual(2, values.Length);
CollectionAssert.AreEquivalent(new[] { 1.5, 2.5 }, values);
}
}
}
6 changes: 3 additions & 3 deletions MEOS.NET/Functions/Meos.meos.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2905,16 +2905,16 @@ public static bool TboolValueAtTimestamptz(IntPtr temp, long t, bool strict, Int
public static bool TboolValueN(IntPtr temp, int n, IntPtr result)
=> SafeExecution<bool>(() => Native.TboolValueN(temp, n, result));

public static IntPtr[] TboolValues(IntPtr temp)
public static bool[] TboolValues(IntPtr temp)
{
IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
try
{
IntPtr _p = SafeExecution<IntPtr>(() => Native.TboolValues(temp, _cnt));
int _n = Marshal.ReadInt32(_cnt);
IntPtr[] _out = new IntPtr[_n];
bool[] _out = new bool[_n];
for (int _i = 0; _i < _n; _i++)
{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
{ _out[_i] = Marshal.ReadByte(_p, _i) != 0; }
return _out;
}
finally { Marshal.FreeHGlobal(_cnt); }
Expand Down
6 changes: 3 additions & 3 deletions MEOS.NET/Functions/Meos.meos_geo.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1736,16 +1736,16 @@ public static int[] GeoClusterKmeans(IntPtr geoms, uint ngeoms, uint k)
finally { Marshal.FreeHGlobal(_cnt); }
}

public static IntPtr[] GeoClusterDbscan(IntPtr geoms, uint ngeoms, double tolerance, int minpoints)
public static uint[] GeoClusterDbscan(IntPtr geoms, uint ngeoms, double tolerance, int minpoints)
{
IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
try
{
IntPtr _p = SafeExecution<IntPtr>(() => Native.GeoClusterDbscan(geoms, ngeoms, tolerance, minpoints, _cnt));
int _n = Marshal.ReadInt32(_cnt);
IntPtr[] _out = new IntPtr[_n];
uint[] _out = new uint[_n];
for (int _i = 0; _i < _n; _i++)
{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
{ _out[_i] = (uint) Marshal.ReadInt32(_p, _i * 4); }
return _out;
}
finally { Marshal.FreeHGlobal(_cnt); }
Expand Down
6 changes: 3 additions & 3 deletions MEOS.NET/Functions/Meos.meos_h3.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,16 +135,16 @@ public static ulong Th3indexEndValue(IntPtr temp)
public static bool Th3indexValueN(IntPtr temp, int n, IntPtr result)
=> SafeExecution<bool>(() => Native.Th3indexValueN(temp, n, result));

public static IntPtr[] Th3indexValues(IntPtr temp)
public static ulong[] Th3indexValues(IntPtr temp)
{
IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
try
{
IntPtr _p = SafeExecution<IntPtr>(() => Native.Th3indexValues(temp, _cnt));
int _n = Marshal.ReadInt32(_cnt);
IntPtr[] _out = new IntPtr[_n];
ulong[] _out = new ulong[_n];
for (int _i = 0; _i < _n; _i++)
{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
{ _out[_i] = (ulong) Marshal.ReadInt64(_p, _i * 8); }
return _out;
}
finally { Marshal.FreeHGlobal(_cnt); }
Expand Down
18 changes: 9 additions & 9 deletions MEOS.NET/Functions/Meos.meos_json.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -462,16 +462,16 @@ public static IntPtr JsonbsetDelete(IntPtr set, IntPtr key)
public static IntPtr JsonbsetDeleteArray(IntPtr set, IntPtr keys, int count)
=> SafeExecution<IntPtr>(() => Native.JsonbsetDeleteArray(set, keys, count));

public static IntPtr[] JsonbsetExists(IntPtr set, IntPtr key)
public static bool[] JsonbsetExists(IntPtr set, IntPtr key)
{
IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
try
{
IntPtr _p = SafeExecution<IntPtr>(() => Native.JsonbsetExists(set, key, _cnt));
int _n = Marshal.ReadInt32(_cnt);
IntPtr[] _out = new IntPtr[_n];
bool[] _out = new bool[_n];
for (int _i = 0; _i < _n; _i++)
{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
{ _out[_i] = Marshal.ReadByte(_p, _i) != 0; }
return _out;
}
finally { Marshal.FreeHGlobal(_cnt); }
Expand Down Expand Up @@ -513,31 +513,31 @@ public static IntPtr JsonbsetExtractPath(IntPtr set, IntPtr path_elems, int path
public static IntPtr JsonbsetInsert(IntPtr set, IntPtr path_elems, int path_len, IntPtr newjb, bool after)
=> SafeExecution<IntPtr>(() => Native.JsonbsetInsert(set, path_elems, path_len, newjb, after));

public static IntPtr[] JsonbsetPathExists(IntPtr set, IntPtr jp, IntPtr vars, bool silent, bool tz)
public static bool[] JsonbsetPathExists(IntPtr set, IntPtr jp, IntPtr vars, bool silent, bool tz)
{
IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
try
{
IntPtr _p = SafeExecution<IntPtr>(() => Native.JsonbsetPathExists(set, jp, vars, silent, tz, _cnt));
int _n = Marshal.ReadInt32(_cnt);
IntPtr[] _out = new IntPtr[_n];
bool[] _out = new bool[_n];
for (int _i = 0; _i < _n; _i++)
{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
{ _out[_i] = Marshal.ReadByte(_p, _i) != 0; }
return _out;
}
finally { Marshal.FreeHGlobal(_cnt); }
}

public static IntPtr[] JsonbsetPathMatch(IntPtr set, IntPtr jp, IntPtr vars, bool silent, bool tz)
public static bool[] JsonbsetPathMatch(IntPtr set, IntPtr jp, IntPtr vars, bool silent, bool tz)
{
IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
try
{
IntPtr _p = SafeExecution<IntPtr>(() => Native.JsonbsetPathMatch(set, jp, vars, silent, tz, _cnt));
int _n = Marshal.ReadInt32(_cnt);
IntPtr[] _out = new IntPtr[_n];
bool[] _out = new bool[_n];
for (int _i = 0; _i < _n; _i++)
{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
{ _out[_i] = Marshal.ReadByte(_p, _i) != 0; }
return _out;
}
finally { Marshal.FreeHGlobal(_cnt); }
Expand Down
18 changes: 9 additions & 9 deletions MEOS.NET/Functions/Meos.meos_quadbin.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,16 @@ public static uint QuadbinGetResolution(ulong cell)
public static ulong QuadbinCellToParent(ulong cell, uint parent_resolution)
=> SafeExecution<ulong>(() => Native.QuadbinCellToParent(cell, parent_resolution));

public static IntPtr[] QuadbinCellToChildren(ulong cell, uint children_resolution)
public static ulong[] QuadbinCellToChildren(ulong cell, uint children_resolution)
{
IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
try
{
IntPtr _p = SafeExecution<IntPtr>(() => Native.QuadbinCellToChildren(cell, children_resolution, _cnt));
int _n = Marshal.ReadInt32(_cnt);
IntPtr[] _out = new IntPtr[_n];
ulong[] _out = new ulong[_n];
for (int _i = 0; _i < _n; _i++)
{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
{ _out[_i] = (ulong) Marshal.ReadInt64(_p, _i * 8); }
return _out;
}
finally { Marshal.FreeHGlobal(_cnt); }
Expand All @@ -75,16 +75,16 @@ public static IntPtr[] QuadbinCellToChildren(ulong cell, uint children_resolutio
public static ulong QuadbinCellSibling(ulong cell, string direction)
=> SafeExecution<ulong>(() => Native.QuadbinCellSibling(cell, direction));

public static IntPtr[] QuadbinKRing(ulong cell, int k)
public static ulong[] QuadbinKRing(ulong cell, int k)
{
IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
try
{
IntPtr _p = SafeExecution<IntPtr>(() => Native.QuadbinKRing(cell, k, _cnt));
int _n = Marshal.ReadInt32(_cnt);
IntPtr[] _out = new IntPtr[_n];
ulong[] _out = new ulong[_n];
for (int _i = 0; _i < _n; _i++)
{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
{ _out[_i] = (ulong) Marshal.ReadInt64(_p, _i * 8); }
return _out;
}
finally { Marshal.FreeHGlobal(_cnt); }
Expand Down Expand Up @@ -162,16 +162,16 @@ public static ulong TquadbinEndValue(IntPtr temp)
public static bool TquadbinValueN(IntPtr temp, int n, IntPtr result)
=> SafeExecution<bool>(() => Native.TquadbinValueN(temp, n, result));

public static IntPtr[] TquadbinValues(IntPtr temp)
public static ulong[] TquadbinValues(IntPtr temp)
{
IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
try
{
IntPtr _p = SafeExecution<IntPtr>(() => Native.TquadbinValues(temp, _cnt));
int _n = Marshal.ReadInt32(_cnt);
IntPtr[] _out = new IntPtr[_n];
ulong[] _out = new ulong[_n];
for (int _i = 0; _i < _n; _i++)
{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
{ _out[_i] = (ulong) Marshal.ReadInt64(_p, _i * 8); }
return _out;
}
finally { Marshal.FreeHGlobal(_cnt); }
Expand Down
6 changes: 3 additions & 3 deletions MEOS.NET/Functions/Meos.meos_raster.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,16 +147,16 @@ public static IntPtr RasterTileValue(IntPtr traj, IntPtr rq)
public static IntPtr RasterTileValueArray(IntPtr traj, IntPtr rqarr, int count)
=> SafeExecution<IntPtr>(() => Native.RasterTileValueArray(traj, rqarr, count));

public static IntPtr[] TrajectoryQuadbins(IntPtr traj, uint zoom)
public static ulong[] TrajectoryQuadbins(IntPtr traj, uint zoom)
{
IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
try
{
IntPtr _p = SafeExecution<IntPtr>(() => Native.TrajectoryQuadbins(traj, zoom, _cnt));
int _n = Marshal.ReadInt32(_cnt);
IntPtr[] _out = new IntPtr[_n];
ulong[] _out = new ulong[_n];
for (int _i = 0; _i < _n; _i++)
{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
{ _out[_i] = (ulong) Marshal.ReadInt64(_p, _i * 8); }
return _out;
}
finally { Marshal.FreeHGlobal(_cnt); }
Expand Down
19 changes: 19 additions & 0 deletions MEOS.NET/Types/Geo.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,25 @@ public ulong ToS2cellCell(int level)
public STBox? TstzspanToStbox(Span s)
=> MEOSFactory.WrapSTBox(Meos.GeoTstzspanToStbox(this.Ptr, s.Ptr));

public static uint[] ClusterDbscan(Geo[] geoms, double tolerance, int minpoints)
{
IntPtr[] _geomsValues = new IntPtr[geoms.Length];
for (int i = 0; i < geoms.Length; i++)
{
_geomsValues[i] = geoms[i].Ptr;
}

GCHandle _geoms = GCHandle.Alloc(_geomsValues, GCHandleType.Pinned);
try
{
return Meos.GeoClusterDbscan(_geoms.AddrOfPinnedObject(), (uint) geoms.Length, tolerance, minpoints);
}
finally
{
_geoms.Free();
}
}

public static Geo?[] ClusterIntersecting(Geo[] geoms)
{
IntPtr[] _geomsValues = new IntPtr[geoms.Length];
Expand Down
3 changes: 3 additions & 0 deletions MEOS.NET/Types/TBool.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ public bool StartValue()
}
}

public bool[] Values()
=> Meos.TboolValues(this.Ptr);

public SpanSet? WhenTrue()
=> MEOSFactory.WrapSpanSet(Meos.TboolWhenTrue(this.Ptr));

Expand Down
33 changes: 33 additions & 0 deletions tools/codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,20 @@ def gen_external_functions(funcs: list[dict]) -> str:
"short": "short",
}

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


def _strip_const_stars(c_type: str) -> tuple[str, int]:
s = c_type.replace("const ", "").strip()
Expand Down Expand Up @@ -462,6 +476,8 @@ def _csharp_array_element(c_type: str, canonical: str) -> tuple[str, str]:
return (elem, "Marshal.Copy")
if base == "uint8_t":
return ("byte", "ByteBuffer")
if elem in _SCALAR_ELEM_READER:
return (elem, f"ScalarArray:{elem}")
if stars == 1 and base in STRUCTS:
# A single pointer to a catalog struct is an array of struct VALUES, not
# of pointers: element i sits at the struct's own stride, and reading it
Expand Down Expand Up @@ -567,6 +583,12 @@ def _emit_outputs_wrapper(f: dict) -> list[str]:
stride = ret_strategy.split(":", 1)[1]
lines.append(" for (int _i = 0; _i < _n; _i++)")
lines.append(f" {{ _resultArr[_i] = IntPtr.Add(_resultPtr, _i * {stride}); }}")
elif ret_strategy.startswith("ScalarArray:"):
size, reader = _SCALAR_ELEM_READER[ret_strategy.split(":", 1)[1]]
offset = "_i" if size == 1 else f"_i * {size}"
lines.append(" for (int _i = 0; _i < _n; _i++)")
lines.append(f" {{ _resultArr[_i] = "
f"{reader.format('_resultPtr', offset)}; }}")
else:
lines.append(" for (int _i = 0; _i < _n; _i++)")
lines.append(" { _resultArr[_i] = Marshal.ReadIntPtr(_resultPtr, _i * IntPtr.Size); }")
Expand All @@ -577,6 +599,12 @@ def _emit_outputs_wrapper(f: dict) -> list[str]:
stride = strategy.split(":", 1)[1]
lines.append(f" for (int _i = 0; _i < _n; _i++)")
lines.append(f" {{ _{local}_out[_i] = IntPtr.Add(_{local}_arr, _i * {stride}); }}")
elif strategy.startswith("ScalarArray:"):
size, reader = _SCALAR_ELEM_READER[strategy.split(":", 1)[1]]
offset = "_i" if size == 1 else f"_i * {size}"
lines.append(f" for (int _i = 0; _i < _n; _i++)")
lines.append(f" {{ _{local}_out[_i] = "
f"{reader.format(f'_{local}_arr', offset)}; }}")
elif elem == "IntPtr":
lines.append(f" for (int _i = 0; _i < _n; _i++)")
lines.append(f" {{ _{local}_out[_i] = Marshal.ReadIntPtr(_{local}_arr, _i * IntPtr.Size); }}")
Expand Down Expand Up @@ -623,6 +651,11 @@ def _copy(indent: str) -> list[str]:
stride = strategy.split(":", 1)[1]
out.append(f"{indent}for (int _i = 0; _i < _n; _i++)")
out.append(f"{indent}{{ _out[_i] = IntPtr.Add(_p, _i * {stride}); }}")
elif strategy.startswith("ScalarArray:"):
size, reader = _SCALAR_ELEM_READER[strategy.split(":", 1)[1]]
offset = "_i" if size == 1 else f"_i * {size}"
out.append(f"{indent}for (int _i = 0; _i < _n; _i++)")
out.append(f"{indent}{{ _out[_i] = {reader.format('_p', offset)}; }}")
else:
out.append(f"{indent}for (int _i = 0; _i < _n; _i++)")
out.append(f"{indent}{{ _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }}")
Expand Down
4 changes: 3 additions & 1 deletion tools/objectgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,9 @@ def map_return(self, f: dict, wrapper_ret: str) -> tuple[str, str] | None:
elem = self.m.class_for_ctype(c[:-1] if c.endswith(" **") else c)
if elem:
return (f"{elem}?[]", f"MEOSFactory.Wrap{elem}Array($)")
if wrapper_ret in ("long[]", "int[]", "double[]", "byte[]"):
# An array of scalars is handed on as it stands, whichever scalar it is
# — the wrapper has already read it at the element's own width.
if wrapper_ret.endswith("[]") and is_scalar(wrapper_ret[:-2]):
return (wrapper_ret, "$")
if c == "void *" and wrapper_ret == "IntPtr":
return ("IntPtr", "$")
Expand Down
Loading