From 71cfbbb357a130f6b5745a3565a82fbdfa092c42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20Zim=C3=A1nyi?= Date: Thu, 3 Sep 2026 12:17:44 +0200 Subject: [PATCH] Take an untyped pointer as a pointer, and call what answers nothing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An UNTYPED pointer stays untyped. MEOS states nothing about what a `void *` points at — `rtree_insert` takes whichever key the tree is created for — so the layer states nothing either and the caller hands over the pointer of the value it means, the `Ptr` every wrapped instance publishes. GoMEOS answers the same shape with `unsafe.Pointer`. A STATIC METHOD THAT ANSWERS NOTHING IS STILL A METHOD. "Neither a receiver nor a value to return" is true of such a signature and false of what it does: `Pcschema.Register` puts a schema in the process-global cache, `Pcschema.Clear` empties it, and the two kNN cursors are closed the same way. A method is called for what it does as much as for what it answers. 11 methods reach the object layer between them — `RTree.Insert`, `.Search`, `.Load` and their SP-tree twins, `MeosArray.Add` and `.Get`, `Pcschema.Register`, `.RegisterXml` and `.Clear`, and the two cursor closes — 1335 methods against 1324, 25 deferred against 36. Two tests call both kinds: an R-tree over float spans takes two of them as pointers and answers 1 for the one that overlaps, and the schema cache is cleared and then says so — by raising, which is how MEOS reports a pcid it holds no schema for. --- MEOS.NET.Tests/UntypedAndVoidTests.cs | 45 +++++++++++++++++++++++++++ MEOS.NET/Types/MeosArray.g.cs | 6 ++++ MEOS.NET/Types/Pcschema.g.cs | 9 ++++++ MEOS.NET/Types/RTree.g.cs | 19 +++++++++++ MEOS.NET/Types/SPTree.g.cs | 19 +++++++++++ tools/objectgen.py | 12 +++++-- 6 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 MEOS.NET.Tests/UntypedAndVoidTests.cs diff --git a/MEOS.NET.Tests/UntypedAndVoidTests.cs b/MEOS.NET.Tests/UntypedAndVoidTests.cs new file mode 100644 index 0000000..dade7df --- /dev/null +++ b/MEOS.NET.Tests/UntypedAndVoidTests.cs @@ -0,0 +1,45 @@ +using MEOS.NET.Enums; +using MEOS.NET.Types; + +namespace MEOS.NET.Tests +{ + /// + /// A method MEOS states nothing about the pointee of, and a method that + /// answers nothing, are both methods: the first takes the pointer of the + /// value the caller means, the second is called for what it does. + /// + [TestClass] + public class UntypedAndVoidTests : MeosTest + { + [TestMethod] + public void AnIndexTakesTheSpansItIsGivenAsPointers() + { + RTree? tree = RTree.CreateFloatspan(); + + Assert.IsNotNull(tree); + + Span first = FloatSpan.In("[1, 3]")!; + Span second = FloatSpan.In("[8, 10]")!; + + Assert.IsTrue(tree!.Insert(first.Ptr, 1)); + Assert.IsTrue(tree.Insert(second.Ptr, 2)); + + MeosArray? found = MeosArray.Create(8); + + Assert.IsNotNull(found); + Assert.AreEqual(1, tree.Search(IndexSearchOp.IndexOverlaps, second.Ptr, found!)); + } + + [TestMethod] + public void ClearingTheSchemaCacheIsAMethodThatAnswersNothing() + { + // `meos_pc_schema_clear()` answers nothing and is called for what + // it does; a cleared cache holds no schema, and MEOS says so by + // raising rather than by answering null. + Pcschema.Clear(); + + Assert.ThrowsException( + () => Pcschema.Get(1)); + } + } +} diff --git a/MEOS.NET/Types/MeosArray.g.cs b/MEOS.NET/Types/MeosArray.g.cs index 2d788b1..63e2137 100644 --- a/MEOS.NET/Types/MeosArray.g.cs +++ b/MEOS.NET/Types/MeosArray.g.cs @@ -14,6 +14,9 @@ public class MeosArray : Value { internal MeosArray(IntPtr ptr) : base(ptr) { } + public void Add(IntPtr value) + => Meos.MeosArrayAdd(this.Ptr, value); + public int Count() => Meos.MeosArrayCount(this.Ptr); @@ -23,6 +26,9 @@ public void Destroy() public void DestroyFree() => Meos.MeosArrayDestroyFree(this.Ptr); + public IntPtr Get(int n) + => Meos.MeosArrayGet(this.Ptr, n); + public void Reset() => Meos.MeosArrayReset(this.Ptr); diff --git a/MEOS.NET/Types/Pcschema.g.cs b/MEOS.NET/Types/Pcschema.g.cs index 47db74e..be37d5f 100644 --- a/MEOS.NET/Types/Pcschema.g.cs +++ b/MEOS.NET/Types/Pcschema.g.cs @@ -14,6 +14,9 @@ public class Pcschema : Value { internal Pcschema(IntPtr ptr) : base(ptr) { } + public static void Clear() + => Meos.MeosPcSchemaClear(); + public static string? Compression(uint pcid) => Meos.MeosPcSchemaCompression(pcid); @@ -23,6 +26,12 @@ internal Pcschema(IntPtr ptr) : base(ptr) { } public static int Ndims(uint pcid) => Meos.MeosPcSchemaNdims(pcid); + public static void Register(uint pcid, Pcschema schema) + => Meos.MeosPcSchemaRegister(pcid, schema.Ptr); + + public static void RegisterXml(uint pcid, Pcschema schema, string xml_text) + => Meos.MeosPcSchemaRegisterXml(pcid, schema.Ptr, xml_text); + public static int SRID(uint pcid) => Meos.MeosPcSchemaSrid(pcid); diff --git a/MEOS.NET/Types/RTree.g.cs b/MEOS.NET/Types/RTree.g.cs index 3d5b026..3ea0ab9 100644 --- a/MEOS.NET/Types/RTree.g.cs +++ b/MEOS.NET/Types/RTree.g.cs @@ -20,6 +20,9 @@ public void Free() public int Height() => Meos.RtreeHeight(this.Ptr); + public bool Insert(IntPtr box, long id) + => Meos.RtreeInsert(this.Ptr, box, id); + public bool InsertTemporal(Temporal temp, long id) => Meos.RtreeInsertTemporal(this.Ptr, temp.Ptr, id); @@ -29,12 +32,28 @@ public bool InsertTemporalSplit(Temporal temp, long id, int maxboxes) public int Join(RTree rtree2, IndexSearchOp op, MeosArray result) => Meos.RtreeJoin(this.Ptr, rtree2.Ptr, (int) op, result.Ptr); + public bool Load(IntPtr boxes, long[] ids) + { + GCHandle _ids = GCHandle.Alloc(ids, GCHandleType.Pinned); + try + { + return Meos.RtreeLoad(this.Ptr, boxes, _ids.AddrOfPinnedObject(), ids.Length); + } + finally + { + _ids.Free(); + } + } + public long MemSize() => Meos.RtreeMemSize(this.Ptr); public int NumEntries() => Meos.RtreeNumEntries(this.Ptr); + public int Search(IndexSearchOp op, IntPtr query, MeosArray result) + => Meos.RtreeSearch(this.Ptr, (int) op, query, result.Ptr); + public int SearchTemporal(IndexSearchOp op, Temporal temp, MeosArray result) => Meos.RtreeSearchTemporal(this.Ptr, (int) op, temp.Ptr, result.Ptr); diff --git a/MEOS.NET/Types/SPTree.g.cs b/MEOS.NET/Types/SPTree.g.cs index 96b211b..f10ab9a 100644 --- a/MEOS.NET/Types/SPTree.g.cs +++ b/MEOS.NET/Types/SPTree.g.cs @@ -20,6 +20,9 @@ public void Free() public int Height() => Meos.SptreeHeight(this.Ptr); + public bool Insert(IntPtr box, long id) + => Meos.SptreeInsert(this.Ptr, box, id); + public bool InsertTemporal(Temporal temp, long id) => Meos.SptreeInsertTemporal(this.Ptr, temp.Ptr, id); @@ -29,12 +32,28 @@ public bool InsertTemporalSplit(Temporal temp, long id, int maxboxes) public int Join(SPTree sptree2, IndexSearchOp op, MeosArray result) => Meos.SptreeJoin(this.Ptr, sptree2.Ptr, (int) op, result.Ptr); + public bool Load(IntPtr boxes, long[] ids) + { + GCHandle _ids = GCHandle.Alloc(ids, GCHandleType.Pinned); + try + { + return Meos.SptreeLoad(this.Ptr, boxes, _ids.AddrOfPinnedObject(), ids.Length); + } + finally + { + _ids.Free(); + } + } + public long MemSize() => Meos.SptreeMemSize(this.Ptr); public int NumEntries() => Meos.SptreeNumEntries(this.Ptr); + public int Search(IndexSearchOp op, IntPtr query, MeosArray result) + => Meos.SptreeSearch(this.Ptr, (int) op, query, result.Ptr); + public int SearchTemporal(IndexSearchOp op, Temporal temp, MeosArray result) => Meos.SptreeSearchTemporal(this.Ptr, (int) op, temp.Ptr, result.Ptr); diff --git a/tools/objectgen.py b/tools/objectgen.py index 95f5ac9..1fc3856 100644 --- a/tools/objectgen.py +++ b/tools/objectgen.py @@ -338,6 +338,8 @@ def map_return(self, f: dict, wrapper_ret: str) -> tuple[str, str] | None: return (f"{elem}?[]", f"MEOSFactory.Wrap{elem}Array($)") if wrapper_ret in ("long[]", "int[]", "double[]", "byte[]"): return (wrapper_ret, "$") + if c == "void *" and wrapper_ret == "IntPtr": + return ("IntPtr", "$") struct = self.value_struct(c) if struct and wrapper_ret == "IntPtr": return (f"{struct}?", f"MEOSConvert.ToStruct<{struct}>($)") @@ -389,6 +391,13 @@ def map_param(self, c_type: str, cs_type: str, name: str) -> tuple[str, str] | N cls = self.m.class_for_ctype(c) if cls and cs_type == "IntPtr": return (cls, f"{name}.Ptr") + # An UNTYPED pointer stays untyped: MEOS states nothing about what it + # points at, so the layer states nothing either and the caller hands + # over the pointer of the value it means — the `Ptr` every wrapped + # instance publishes. GoMEOS answers the same shape with + # `unsafe.Pointer`. + if c == "void *" and cs_type == "IntPtr": + return ("IntPtr", name) struct = self.value_struct(c) if struct and cs_type == "IntPtr": return (struct, scratch(name)) @@ -468,9 +477,6 @@ def method_for(self, cls: str, entry: dict) -> Method | None: f"{oo}: return {clean(f['returnType']['c'])} needs wrapping") return None ret_type, ret_expr = ret - if static and ret_type == "void": - self.deferred[cls].append(f"{oo}: neither a receiver nor a value to return") - return None # The length is the array's own, so it leaves the C# signature. count_of = {codegen.csharp_param_name(a["lengthFrom"]["name"]):