diff --git a/MEOS.NET.Tests/CallerAllocatedArrayTests.cs b/MEOS.NET.Tests/CallerAllocatedArrayTests.cs
new file mode 100644
index 0000000..f1ea7a5
--- /dev/null
+++ b/MEOS.NET.Tests/CallerAllocatedArrayTests.cs
@@ -0,0 +1,39 @@
+using System.Runtime.InteropServices;
+
+using MEOS.NET.Functions;
+using MEOS.NET.Types;
+
+namespace MEOS.NET.Tests
+{
+ ///
+ /// The `each` family fills an array the CALLER allocates, so the wrapper
+ /// takes that array rather than making one: MEOS writes one pointer per
+ /// member of the object, and storage for fewer of them is written past.
+ ///
+ [TestClass]
+ public class CallerAllocatedArrayTests : MeosTest
+ {
+ [TestMethod]
+ public void EachFillsTheArrayTheCallerGivesIt()
+ {
+ Jsonb jb = Jsonb.In("{\"a\": 1, \"b\": \"two\"}")!;
+ IntPtr values = Marshal.AllocHGlobal(IntPtr.Size * 8);
+ try
+ {
+ IntPtr[] keys = Meos.JsonbEach(jb.Ptr, values);
+
+ Assert.AreEqual(2, keys.Length);
+ Assert.AreEqual("a", Meos.TextOut(keys[0]));
+ Assert.AreEqual("b", Meos.TextOut(keys[1]));
+ Assert.AreEqual("1",
+ Meos.JsonbOut(Marshal.ReadIntPtr(values, 0)));
+ Assert.AreEqual("\"two\"",
+ Meos.JsonbOut(Marshal.ReadIntPtr(values, IntPtr.Size)));
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(values);
+ }
+ }
+ }
+}
diff --git a/MEOS.NET.Tests/OutParameterTests.cs b/MEOS.NET.Tests/OutParameterTests.cs
index c80285d..a96d16d 100644
--- a/MEOS.NET.Tests/OutParameterTests.cs
+++ b/MEOS.NET.Tests/OutParameterTests.cs
@@ -49,5 +49,34 @@ public void ATemporalFloatAnswersItsValueAtAMoment()
Assert.IsNull(
temp.ValueAtTimestamptz(new DateTime(2024, 12, 9, 0, 0, 0, DateTimeKind.Utc), true));
}
+
+ [TestMethod]
+ public void ANearestNeighbourStepAnswersTheEntryAndItsDistance()
+ {
+ RTree tree = RTree.CreateStbox()!;
+ STBox near = STBox.In("STBOX X((1,1),(2,2))")!;
+ STBox far = STBox.In("STBOX X((9,9),(10,10))")!;
+ Assert.IsTrue(tree.Insert(near.Ptr, 1));
+ Assert.IsTrue(tree.Insert(far.Ptr, 2));
+
+ STBox query = STBox.In("STBOX X((0,0),(0,0))")!;
+ RTreeNNCursor walk = RTreeNNCursor.Open(tree, query.Ptr)!;
+ try
+ {
+ (long Id, double Distance)? nearest = walk.Next();
+ (long Id, double Distance)? next = walk.Next();
+
+ Assert.IsNotNull(nearest);
+ Assert.IsNotNull(next);
+ Assert.AreEqual(1L, nearest!.Value.Id);
+ Assert.AreEqual(2L, next!.Value.Id);
+ Assert.IsTrue(next.Value.Distance > nearest.Value.Distance);
+ Assert.IsNull(walk.Next());
+ }
+ finally
+ {
+ walk.Close();
+ }
+ }
}
}
diff --git a/MEOS.NET/Functions/Meos.meos_json.g.cs b/MEOS.NET/Functions/Meos.meos_json.g.cs
index 27542e7..81fd440 100644
--- a/MEOS.NET/Functions/Meos.meos_json.g.cs
+++ b/MEOS.NET/Functions/Meos.meos_json.g.cs
@@ -105,52 +105,34 @@ public static IntPtr[] JsonArrayElementsText(IntPtr js)
public static int JsonArrayLength(IntPtr js)
=> SafeExecution(() => Native.JsonArrayLength(js));
- public static (IntPtr[], IntPtr[]) JsonEach(IntPtr js)
+ public static IntPtr[] JsonEach(IntPtr js, IntPtr values)
{
- IntPtr _out_values = Marshal.AllocHGlobal(IntPtr.Size);
- IntPtr _count_count = Marshal.AllocHGlobal(sizeof(int));
+ IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
try
{
- IntPtr _resultPtr = SafeExecution(() => Native.JsonEach(js, _out_values, _count_count));
- int _n = Marshal.ReadInt32(_count_count);
- IntPtr[] _resultArr = new IntPtr[_n];
- for (int _i = 0; _i < _n; _i++)
- { _resultArr[_i] = Marshal.ReadIntPtr(_resultPtr, _i * IntPtr.Size); }
- IntPtr __out_values_arr = Marshal.ReadIntPtr(_out_values);
- IntPtr[] __out_values_out = new IntPtr[_n];
+ IntPtr _p = SafeExecution(() => Native.JsonEach(js, values, _cnt));
+ int _n = Marshal.ReadInt32(_cnt);
+ IntPtr[] _out = new IntPtr[_n];
for (int _i = 0; _i < _n; _i++)
- { __out_values_out[_i] = Marshal.ReadIntPtr(__out_values_arr, _i * IntPtr.Size); }
- return (_resultArr, __out_values_out);
- }
- finally
- {
- Marshal.FreeHGlobal(_out_values);
- Marshal.FreeHGlobal(_count_count);
+ { _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
+ return _out;
}
+ finally { Marshal.FreeHGlobal(_cnt); }
}
- public static (IntPtr[], IntPtr[]) JsonEachText(IntPtr js)
+ public static IntPtr[] JsonEachText(IntPtr js, IntPtr values)
{
- IntPtr _out_values = Marshal.AllocHGlobal(IntPtr.Size);
- IntPtr _count_count = Marshal.AllocHGlobal(sizeof(int));
+ IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
try
{
- IntPtr _resultPtr = SafeExecution(() => Native.JsonEachText(js, _out_values, _count_count));
- int _n = Marshal.ReadInt32(_count_count);
- IntPtr[] _resultArr = new IntPtr[_n];
- for (int _i = 0; _i < _n; _i++)
- { _resultArr[_i] = Marshal.ReadIntPtr(_resultPtr, _i * IntPtr.Size); }
- IntPtr __out_values_arr = Marshal.ReadIntPtr(_out_values);
- IntPtr[] __out_values_out = new IntPtr[_n];
+ IntPtr _p = SafeExecution(() => Native.JsonEachText(js, values, _cnt));
+ int _n = Marshal.ReadInt32(_cnt);
+ IntPtr[] _out = new IntPtr[_n];
for (int _i = 0; _i < _n; _i++)
- { __out_values_out[_i] = Marshal.ReadIntPtr(__out_values_arr, _i * IntPtr.Size); }
- return (_resultArr, __out_values_out);
- }
- finally
- {
- Marshal.FreeHGlobal(_out_values);
- Marshal.FreeHGlobal(_count_count);
+ { _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
+ return _out;
}
+ finally { Marshal.FreeHGlobal(_cnt); }
}
public static IntPtr JsonExtractPath(IntPtr js, IntPtr path_elems, int path_len)
@@ -228,52 +210,34 @@ public static bool JsonbContained(IntPtr jb1, IntPtr jb2)
public static bool JsonbContains(IntPtr jb1, IntPtr jb2)
=> SafeExecution(() => Native.JsonbContains(jb1, jb2));
- public static (IntPtr[], IntPtr[]) JsonbEach(IntPtr jb)
+ public static IntPtr[] JsonbEach(IntPtr jb, IntPtr values)
{
- IntPtr _out_values = Marshal.AllocHGlobal(IntPtr.Size);
- IntPtr _count_count = Marshal.AllocHGlobal(sizeof(int));
+ IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
try
{
- IntPtr _resultPtr = SafeExecution(() => Native.JsonbEach(jb, _out_values, _count_count));
- int _n = Marshal.ReadInt32(_count_count);
- IntPtr[] _resultArr = new IntPtr[_n];
- for (int _i = 0; _i < _n; _i++)
- { _resultArr[_i] = Marshal.ReadIntPtr(_resultPtr, _i * IntPtr.Size); }
- IntPtr __out_values_arr = Marshal.ReadIntPtr(_out_values);
- IntPtr[] __out_values_out = new IntPtr[_n];
+ IntPtr _p = SafeExecution(() => Native.JsonbEach(jb, values, _cnt));
+ int _n = Marshal.ReadInt32(_cnt);
+ IntPtr[] _out = new IntPtr[_n];
for (int _i = 0; _i < _n; _i++)
- { __out_values_out[_i] = Marshal.ReadIntPtr(__out_values_arr, _i * IntPtr.Size); }
- return (_resultArr, __out_values_out);
- }
- finally
- {
- Marshal.FreeHGlobal(_out_values);
- Marshal.FreeHGlobal(_count_count);
+ { _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
+ return _out;
}
+ finally { Marshal.FreeHGlobal(_cnt); }
}
- public static (IntPtr[], IntPtr[]) JsonbEachText(IntPtr jb)
+ public static IntPtr[] JsonbEachText(IntPtr jb, IntPtr values)
{
- IntPtr _out_values = Marshal.AllocHGlobal(IntPtr.Size);
- IntPtr _count_count = Marshal.AllocHGlobal(sizeof(int));
+ IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
try
{
- IntPtr _resultPtr = SafeExecution(() => Native.JsonbEachText(jb, _out_values, _count_count));
- int _n = Marshal.ReadInt32(_count_count);
- IntPtr[] _resultArr = new IntPtr[_n];
- for (int _i = 0; _i < _n; _i++)
- { _resultArr[_i] = Marshal.ReadIntPtr(_resultPtr, _i * IntPtr.Size); }
- IntPtr __out_values_arr = Marshal.ReadIntPtr(_out_values);
- IntPtr[] __out_values_out = new IntPtr[_n];
+ IntPtr _p = SafeExecution(() => Native.JsonbEachText(jb, values, _cnt));
+ int _n = Marshal.ReadInt32(_cnt);
+ IntPtr[] _out = new IntPtr[_n];
for (int _i = 0; _i < _n; _i++)
- { __out_values_out[_i] = Marshal.ReadIntPtr(__out_values_arr, _i * IntPtr.Size); }
- return (_resultArr, __out_values_out);
- }
- finally
- {
- Marshal.FreeHGlobal(_out_values);
- Marshal.FreeHGlobal(_count_count);
+ { _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
+ return _out;
}
+ finally { Marshal.FreeHGlobal(_cnt); }
}
public static bool JsonbExists(IntPtr jb, IntPtr key)
diff --git a/MEOS.NET/Functions/Meos.pgtypes.g.cs b/MEOS.NET/Functions/Meos.pgtypes.g.cs
index 5dd3553..5992523 100644
--- a/MEOS.NET/Functions/Meos.pgtypes.g.cs
+++ b/MEOS.NET/Functions/Meos.pgtypes.g.cs
@@ -534,52 +534,34 @@ public static IntPtr[] PgJsonArrayElementsText(IntPtr js)
public static int PgJsonArrayLength(IntPtr js)
=> SafeExecution(() => Native.PgJsonArrayLength(js));
- public static (IntPtr[], IntPtr[]) PgJsonEach(IntPtr js)
+ public static IntPtr[] PgJsonEach(IntPtr js, IntPtr values)
{
- IntPtr _out_values = Marshal.AllocHGlobal(IntPtr.Size);
- IntPtr _count_count = Marshal.AllocHGlobal(sizeof(int));
+ IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
try
{
- IntPtr _resultPtr = SafeExecution(() => Native.PgJsonEach(js, _out_values, _count_count));
- int _n = Marshal.ReadInt32(_count_count);
- IntPtr[] _resultArr = new IntPtr[_n];
- for (int _i = 0; _i < _n; _i++)
- { _resultArr[_i] = Marshal.ReadIntPtr(_resultPtr, _i * IntPtr.Size); }
- IntPtr __out_values_arr = Marshal.ReadIntPtr(_out_values);
- IntPtr[] __out_values_out = new IntPtr[_n];
+ IntPtr _p = SafeExecution(() => Native.PgJsonEach(js, values, _cnt));
+ int _n = Marshal.ReadInt32(_cnt);
+ IntPtr[] _out = new IntPtr[_n];
for (int _i = 0; _i < _n; _i++)
- { __out_values_out[_i] = Marshal.ReadIntPtr(__out_values_arr, _i * IntPtr.Size); }
- return (_resultArr, __out_values_out);
- }
- finally
- {
- Marshal.FreeHGlobal(_out_values);
- Marshal.FreeHGlobal(_count_count);
+ { _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
+ return _out;
}
+ finally { Marshal.FreeHGlobal(_cnt); }
}
- public static (IntPtr[], IntPtr[]) PgJsonEachText(IntPtr js)
+ public static IntPtr[] PgJsonEachText(IntPtr js, IntPtr values)
{
- IntPtr _out_values = Marshal.AllocHGlobal(IntPtr.Size);
- IntPtr _count_count = Marshal.AllocHGlobal(sizeof(int));
+ IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
try
{
- IntPtr _resultPtr = SafeExecution(() => Native.PgJsonEachText(js, _out_values, _count_count));
- int _n = Marshal.ReadInt32(_count_count);
- IntPtr[] _resultArr = new IntPtr[_n];
- for (int _i = 0; _i < _n; _i++)
- { _resultArr[_i] = Marshal.ReadIntPtr(_resultPtr, _i * IntPtr.Size); }
- IntPtr __out_values_arr = Marshal.ReadIntPtr(_out_values);
- IntPtr[] __out_values_out = new IntPtr[_n];
+ IntPtr _p = SafeExecution(() => Native.PgJsonEachText(js, values, _cnt));
+ int _n = Marshal.ReadInt32(_cnt);
+ IntPtr[] _out = new IntPtr[_n];
for (int _i = 0; _i < _n; _i++)
- { __out_values_out[_i] = Marshal.ReadIntPtr(__out_values_arr, _i * IntPtr.Size); }
- return (_resultArr, __out_values_out);
- }
- finally
- {
- Marshal.FreeHGlobal(_out_values);
- Marshal.FreeHGlobal(_count_count);
+ { _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
+ return _out;
}
+ finally { Marshal.FreeHGlobal(_cnt); }
}
public static IntPtr[] PgJsonObjectKeys(IntPtr js)
@@ -642,52 +624,34 @@ public static bool PgJsonbContained(IntPtr jb1, IntPtr jb2)
public static bool PgJsonbContains(IntPtr jb1, IntPtr jb2)
=> SafeExecution(() => Native.PgJsonbContains(jb1, jb2));
- public static (IntPtr[], IntPtr[]) PgJsonbEach(IntPtr jb)
+ public static IntPtr[] PgJsonbEach(IntPtr jb, IntPtr values)
{
- IntPtr _out_values = Marshal.AllocHGlobal(IntPtr.Size);
- IntPtr _count_count = Marshal.AllocHGlobal(sizeof(int));
+ IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
try
{
- IntPtr _resultPtr = SafeExecution(() => Native.PgJsonbEach(jb, _out_values, _count_count));
- int _n = Marshal.ReadInt32(_count_count);
- IntPtr[] _resultArr = new IntPtr[_n];
- for (int _i = 0; _i < _n; _i++)
- { _resultArr[_i] = Marshal.ReadIntPtr(_resultPtr, _i * IntPtr.Size); }
- IntPtr __out_values_arr = Marshal.ReadIntPtr(_out_values);
- IntPtr[] __out_values_out = new IntPtr[_n];
+ IntPtr _p = SafeExecution(() => Native.PgJsonbEach(jb, values, _cnt));
+ int _n = Marshal.ReadInt32(_cnt);
+ IntPtr[] _out = new IntPtr[_n];
for (int _i = 0; _i < _n; _i++)
- { __out_values_out[_i] = Marshal.ReadIntPtr(__out_values_arr, _i * IntPtr.Size); }
- return (_resultArr, __out_values_out);
- }
- finally
- {
- Marshal.FreeHGlobal(_out_values);
- Marshal.FreeHGlobal(_count_count);
+ { _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
+ return _out;
}
+ finally { Marshal.FreeHGlobal(_cnt); }
}
- public static (IntPtr[], IntPtr[]) PgJsonbEachText(IntPtr jb)
+ public static IntPtr[] PgJsonbEachText(IntPtr jb, IntPtr values)
{
- IntPtr _out_values = Marshal.AllocHGlobal(IntPtr.Size);
- IntPtr _count_count = Marshal.AllocHGlobal(sizeof(int));
+ IntPtr _cnt = Marshal.AllocHGlobal(sizeof(int));
try
{
- IntPtr _resultPtr = SafeExecution(() => Native.PgJsonbEachText(jb, _out_values, _count_count));
- int _n = Marshal.ReadInt32(_count_count);
- IntPtr[] _resultArr = new IntPtr[_n];
- for (int _i = 0; _i < _n; _i++)
- { _resultArr[_i] = Marshal.ReadIntPtr(_resultPtr, _i * IntPtr.Size); }
- IntPtr __out_values_arr = Marshal.ReadIntPtr(_out_values);
- IntPtr[] __out_values_out = new IntPtr[_n];
+ IntPtr _p = SafeExecution(() => Native.PgJsonbEachText(jb, values, _cnt));
+ int _n = Marshal.ReadInt32(_cnt);
+ IntPtr[] _out = new IntPtr[_n];
for (int _i = 0; _i < _n; _i++)
- { __out_values_out[_i] = Marshal.ReadIntPtr(__out_values_arr, _i * IntPtr.Size); }
- return (_resultArr, __out_values_out);
- }
- finally
- {
- Marshal.FreeHGlobal(_out_values);
- Marshal.FreeHGlobal(_count_count);
+ { _out[_i] = Marshal.ReadIntPtr(_p, _i * IntPtr.Size); }
+ return _out;
}
+ finally { Marshal.FreeHGlobal(_cnt); }
}
public static bool PgJsonbEq(IntPtr jb1, IntPtr jb2)
diff --git a/MEOS.NET/Types/Geometry.g.cs b/MEOS.NET/Types/Geometry.g.cs
index 41b26c9..66ec8f2 100644
--- a/MEOS.NET/Types/Geometry.g.cs
+++ b/MEOS.NET/Types/Geometry.g.cs
@@ -89,6 +89,21 @@ public double Length()
public double MaxDistance2d(Geo gs2)
=> Meos.GeomMaxDistance2d(this.Ptr, gs2.Ptr);
+ public (Geo?, double) MinBoundingRadius()
+ {
+ IntPtr _radius = Marshal.AllocHGlobal(8);
+ try
+ {
+ var _answered = Meos.GeomMinBoundingRadius(this.Ptr, _radius);
+
+ return (MEOSFactory.WrapGeo(_answered), Marshal.PtrToStructure(_radius));
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(_radius);
+ }
+ }
+
public Geo? OrientedEnvelope()
=> MEOSFactory.WrapGeo(Meos.GeomOrientedEnvelope(this.Ptr));
diff --git a/MEOS.NET/Types/Jsonb.g.cs b/MEOS.NET/Types/Jsonb.g.cs
index d08e44c..210a745 100644
--- a/MEOS.NET/Types/Jsonb.g.cs
+++ b/MEOS.NET/Types/Jsonb.g.cs
@@ -86,20 +86,6 @@ public bool Contained(Jsonb jb2)
}
}
- public (Text?[], Jsonb?[]) Each()
- {
- var _answered = Meos.JsonbEach(this.Ptr);
-
- return (MEOSFactory.WrapTextArray(_answered.Item1), MEOSFactory.WrapJsonbArray(_answered.Item2));
- }
-
- public (Text?[], Text?[]) EachText()
- {
- var _answered = Meos.JsonbEachText(this.Ptr);
-
- return (MEOSFactory.WrapTextArray(_answered.Item1), MEOSFactory.WrapTextArray(_answered.Item2));
- }
-
public bool Exists(Text key)
=> Meos.JsonbExists(this.Ptr, key.Ptr);
diff --git a/MEOS.NET/Types/RTreeNNCursor.g.cs b/MEOS.NET/Types/RTreeNNCursor.g.cs
index 0c638b4..06e88f1 100644
--- a/MEOS.NET/Types/RTreeNNCursor.g.cs
+++ b/MEOS.NET/Types/RTreeNNCursor.g.cs
@@ -17,6 +17,26 @@ internal RTreeNNCursor(IntPtr ptr) : base(ptr) { }
public void Close()
=> Meos.RtreeNnCursorClose(this.Ptr);
+ public (long, double)? Next()
+ {
+ IntPtr _id_out = Marshal.AllocHGlobal(8);
+ IntPtr _dist_out = Marshal.AllocHGlobal(8);
+ try
+ {
+ if (!Meos.RtreeNnCursorNext(this.Ptr, _id_out, _dist_out))
+ {
+ return null;
+ }
+
+ return (Marshal.ReadInt64(_id_out), Marshal.PtrToStructure(_dist_out));
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(_id_out);
+ Marshal.FreeHGlobal(_dist_out);
+ }
+ }
+
public static RTreeNNCursor? Open(RTree rtree, IntPtr query)
=> MEOSFactory.WrapRTreeNNCursor(Meos.RtreeNnCursorOpen(rtree.Ptr, query));
diff --git a/MEOS.NET/Types/SPNNCursor.g.cs b/MEOS.NET/Types/SPNNCursor.g.cs
index 6ad4c3a..48859a7 100644
--- a/MEOS.NET/Types/SPNNCursor.g.cs
+++ b/MEOS.NET/Types/SPNNCursor.g.cs
@@ -17,6 +17,26 @@ internal SPNNCursor(IntPtr ptr) : base(ptr) { }
public void Close()
=> Meos.SptreeNnCursorClose(this.Ptr);
+ public (long, double)? Next()
+ {
+ IntPtr _id_out = Marshal.AllocHGlobal(8);
+ IntPtr _dist_out = Marshal.AllocHGlobal(8);
+ try
+ {
+ if (!Meos.SptreeNnCursorNext(this.Ptr, _id_out, _dist_out))
+ {
+ return null;
+ }
+
+ return (Marshal.ReadInt64(_id_out), Marshal.PtrToStructure(_dist_out));
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(_id_out);
+ Marshal.FreeHGlobal(_dist_out);
+ }
+ }
+
public static SPNNCursor? Open(SPTree sptree, IntPtr query)
=> MEOSFactory.WrapSPNNCursor(Meos.SptreeNnCursorOpen(sptree.Ptr, query));
diff --git a/tools/objectgen.py b/tools/objectgen.py
index 5763f01..7bcd57d 100644
--- a/tools/objectgen.py
+++ b/tools/objectgen.py
@@ -275,7 +275,7 @@ class Method:
def __init__(self, name: str, ret: str, params: list[tuple[str, str]],
body: str, static: bool, arrays: list[tuple[str, str]] | None = None,
- out_param: tuple[str, int, str] | None = None,
+ out_params: list[tuple[str, int, str]] | None = None,
structs: list[tuple[str, str]] | None = None,
scalar_arrays: list[str] | None = None,
length_out: str | None = None, byte_buffer: bool = False):
@@ -286,8 +286,9 @@ def __init__(self, name: str, ret: str, params: list[tuple[str, str]],
self.static = static
# (parameter name, element class) for each counted array the method takes.
self.arrays = arrays or []
- # (parameter name, bytes, reader expression) for a value out-parameter.
- self.out_param = out_param
+ # (parameter name, bytes, reader expression) for each value MEOS states
+ # through an out-parameter.
+ self.out_params = out_params or []
# (parameter name, struct type) for each struct argument passed by value.
self.structs = structs or []
# The caller's own arrays of scalars, pinned across the call.
@@ -300,7 +301,7 @@ def __init__(self, name: str, ret: str, params: list[tuple[str, str]],
def needs_a_body(self) -> bool:
"""Whether the call needs anything allocated, pinned or read around it."""
return bool(self.structs or self.arrays or self.scalar_arrays
- or self.out_param or self.length_out
+ or self.out_params or self.length_out
or (self.ret.startswith("(") and self.ret.endswith(")")))
@@ -508,21 +509,40 @@ def method_for(self, cls: str, entry: dict) -> Method | None:
out_params = [o for o in out_params
if codegen.csharp_param_name(o) != length_out]
- # A `bool` return with one value out-parameter is MEOS saying whether the
- # value exists: the method answers the value, or nothing.
- result_out = None
- if clean(f["returnType"]["c"]) == "bool" and len(out_params) == 1:
+ # An out-parameter is a value MEOS answers, so every one of them is read
+ # back and none appears in the C# signature. A `bool` return alongside
+ # them is MEOS saying whether the values exist rather than an answer of
+ # its own, so the method answers them or nothing; any other return is an
+ # answer and stands beside them.
+ # An out-parameter the flat wrapper already answers — the array it fills
+ # and that array's count — is gone from the wrapper's own signature, and
+ # what the wrapper answers is not the object layer's to read again.
+ taken = {pname for _t, pname in params}
+ out_params = [o for o in out_params
+ if codegen.csharp_param_name(o) in taken]
+ # A by-pointer count says the call answers a NUMBER of things, so an
+ # out-parameter beside one holds as many as it states — `jsonb_each`
+ # fills its `Jsonb **values` with one pointer per member of the object.
+ # Reading such a parameter as a single value allocates room for one and
+ # takes what MEOS wrote first for the whole answer.
+ if any(p["name"] == "count" and clean(p["cType"]) == "int *"
+ for p in f.get("params", [])):
+ out_params = []
+
+ # And a pointer to a MEOS value is the value itself, handed over for the
+ # call to fill — `rtree_search(..., MeosArray *result)` takes the array
+ # it appends to. What MEOS WRITES THROUGH the pointer is a scalar, or a
+ # value whose address it states (`T **`); those are its answers, and the
+ # rest stay ordinary arguments.
+ result_outs = []
+ for name in out_params:
pointee = clean(next(
(p["cType"] for p in f.get("params", [])
- if p["name"] == out_params[0]), ""))
+ if p["name"] == name), ""))
reader = OUT_PARAM_READERS.get(pointee) or self.wrapped_out_reader(pointee)
if reader:
- result_out = (codegen.csharp_param_name(out_params[0]), reader)
- else:
- self.deferred[cls].append(
- f"{oo}: the value out-parameter {out_params[0]} is a "
- f"{pointee}, which has no reader")
- return None
+ result_outs.append((codegen.csharp_param_name(name), reader))
+ answers_existence = clean(f["returnType"]["c"]) == "bool" and result_outs
if length_out and clean(f["returnType"]["c"]) == "uint8_t *":
ret = ("byte[]?", "$")
@@ -539,16 +559,24 @@ def method_for(self, cls: str, entry: dict) -> Method | None:
codegen.csharp_param_name(a["param"])
for a in input_arrays}
- if result_out is not None:
- ret_type = f"{result_out[1][0]}?"
+ if answers_existence:
+ # The `bool` is not an answer: what MEOS wrote is.
+ answered = ", ".join(r[0] for _n, r in result_outs)
+ ret_type = (f"{answered}?" if len(result_outs) == 1
+ else f"({answered})?")
ret_expr = "$"
+ elif result_outs:
+ # The return is an answer of its own and the out-parameters stand
+ # beside it, in the order MEOS declares them.
+ ret_type = ", ".join([ret_type] + [r[0] for _n, r in result_outs])
+ ret_type = f"({ret_type})"
sig: list[tuple[str, str]] = []
arrays: list[tuple[str, str]] = []
scalar_arrays: list[str] = []
structs: list[tuple[str, str]] = []
for cs_type, pname in params:
- if result_out is not None and pname == result_out[0]:
+ if any(pname == n for n, _r in result_outs):
args.append(scratch(pname))
continue
if pname == length_out:
@@ -590,12 +618,17 @@ def method_for(self, cls: str, entry: dict) -> Method | None:
structs.append((pname, mapped[0]))
call = f"Meos.{codegen.public_name(fname)}({', '.join(args)})"
- out = None
- if result_out is not None:
- name, (_, size, reader) = result_out
- out = (name, size, reader.format(scratch(name)))
- return Method(pascal(oo), ret_type, sig, ret_expr.replace("$", call), static,
- arrays, out, structs, scalar_arrays, length_out,
+ outs = [(name, size, reader.format(scratch(name)))
+ for name, (_t, size, reader) in result_outs]
+ body = ret_expr.replace("$", call)
+ if result_outs and not answers_existence:
+ # The call is made once and its own answer read beside the values it
+ # wrote, so the template carries the two halves apart.
+ read = ", ".join([ret_expr.replace("$", "_answered")]
+ + [reader for _n, _s, reader in outs])
+ body = f"{call}|>({read})"
+ return Method(pascal(oo), ret_type, sig, body, static,
+ arrays, outs, structs, scalar_arrays, length_out,
ret_type == "byte[]?")
def inherited_names(self, cls: str) -> set[tuple]:
@@ -693,8 +726,7 @@ def call_body(self, method: Method) -> list[str]:
name = scratch(method.length_out)
setup.append(f" IntPtr {name} = Marshal.AllocHGlobal(sizeof(long));")
teardown.append(f" Marshal.FreeHGlobal({name});")
- if method.out_param:
- name, size, _reader = method.out_param
+ for name, size, _reader in method.out_params:
setup.append(
f" IntPtr {scratch(name)} = Marshal.AllocHGlobal({size});")
teardown.append(f" Marshal.FreeHGlobal({scratch(name)});")
@@ -712,17 +744,18 @@ def call_body(self, method: Method) -> list[str]:
def answer(self, method: Method) -> list[str]:
"""What the body does with the call, once everything is in place."""
- if method.out_param:
- # MEOS answers `false` where the value does not exist and leaves the
- # out-parameter untouched, so the method answers null there.
- _name, _size, reader = method.out_param
+ if method.out_params and "|>" not in method.body:
+ # MEOS answers `false` where the values do not exist and leaves the
+ # out-parameters untouched, so the method answers null there.
+ read = ", ".join(reader for _n, _s, reader in method.out_params)
+ answered = read if len(method.out_params) == 1 else f"({read})"
return [
f" if (!{method.body})",
" {",
" return null;",
" }",
"",
- f" return {reader};",
+ f" return {answered};",
]
if method.byte_buffer:
# A byte array does not carry its own length, so what MEOS wrote