From 32e0d0d0374a73b9f856e423aac2266ea7844d2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20Zim=C3=A1nyi?= Date: Thu, 3 Sep 2026 09:51:55 +0200 Subject: [PATCH] Hand on whatever the wrapper already states as a value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The object layer kept its own account of which C types are values — one enum named in a branch, and a set of C spellings beside it — while the flat surface had already resolved every one of them from the catalog. So a method taking the `nullHandleType` the catalog names, or answering an `S2CellId`, a `float8` or an `Oid`, was deferred to that surface for a type sitting two lines away in it. The wrapper's own C# type is the answer: a scalar it resolved through the catalog's typedefs, a struct it carries by value, or an enum the catalog names, in which case the method takes and answers that enum rather than its number. 18 methods reach the object layer that way — `TJsonb.ArrayElement` and its ten jsonb siblings taking a `NullHandleType`, `Raquet.Make` a `MeosPixType`, `Geo.ToS2cellCell` answering a `ulong`, `TPoint.AsMvtgeom` an `MvtGeom` and `TGeo.SpaceSplit` a `SpaceSplit` — 1264 methods against 1246, 31 deferred against 49. Three tests read the three kinds back: a temporal jsonb given the enum the catalog names, a geometry answering its S2 cell as the number MEOS states, and a temporal point answering the struct it fills. --- MEOS.NET.Tests/CatalogValueTypeTests.cs | 49 +++++++++++++++++++ MEOS.NET/Functions/Meos.Native.g.cs | 18 +++++++ .../Functions/Meos.meos_internal_geo.g.cs | 15 ++++++ MEOS.NET/Types/Geo.g.cs | 3 ++ MEOS.NET/Types/Jsonb.g.cs | 6 +++ MEOS.NET/Types/Raquet.g.cs | 19 +++++++ MEOS.NET/Types/TGeo.g.cs | 17 +++++++ MEOS.NET/Types/TJsonb.g.cs | 37 ++++++++++++++ MEOS.NET/Types/TPoint.g.cs | 3 ++ MEOS.NET/Types/Text.g.cs | 6 +++ tools/objectgen.py | 34 ++++++++----- 11 files changed, 195 insertions(+), 12 deletions(-) create mode 100644 MEOS.NET.Tests/CatalogValueTypeTests.cs diff --git a/MEOS.NET.Tests/CatalogValueTypeTests.cs b/MEOS.NET.Tests/CatalogValueTypeTests.cs new file mode 100644 index 0000000..0f4ceab --- /dev/null +++ b/MEOS.NET.Tests/CatalogValueTypeTests.cs @@ -0,0 +1,49 @@ +using MEOS.NET.Enums; +using MEOS.NET.Structures; +using MEOS.NET.Types; + +namespace MEOS.NET.Tests +{ + /// + /// Whatever the wrapper already states as a C# value — an enum the catalog + /// names, a scalar behind a MEOS typedef, a struct carried by value — the + /// object layer hands on as that value. + /// + [TestClass] + public class CatalogValueTypeTests : MeosTest + { + [TestMethod] + public void AnEnumArgumentIsTheEnumTheCatalogNames() + { + TJsonb temp = (TJsonb)TJsonb.In( + "[{\"a\": [1, 2]}@2024-12-06, {\"a\": [1, 2]}@2024-12-08]")!; + + Temporal? first = temp.ArrayElement(0, true, NullHandleType.NullJsonNull); + + Assert.IsNotNull(first); + } + + [TestMethod] + public void AScalarBehindATypedefComesBackAsThatScalar() + { + Geo point = Geo.FromText("POINT(1 1)", 4326)!; + + ulong cell = point.ToS2cellCell(10); + + Assert.AreNotEqual(0UL, cell); + } + + [TestMethod] + public void AStructReturnedByValueComesBackAsTheStruct() + { + TPoint trip = (TPoint)TGeomPoint.In( + "[POINT(0 0)@2024-12-06, POINT(2 2)@2024-12-08]")!; + STBox bounds = STBox.In("STBOX X((-1,-1),(3,3))")!; + + MvtGeom mvt = trip.AsMvtgeom(bounds, 4096, 0, true); + + Assert.AreNotEqual(IntPtr.Zero, mvt.geom); + Assert.IsTrue(mvt.count > 0); + } + } +} diff --git a/MEOS.NET/Functions/Meos.Native.g.cs b/MEOS.NET/Functions/Meos.Native.g.cs index fea9327..6b22b0d 100644 --- a/MEOS.NET/Functions/Meos.Native.g.cs +++ b/MEOS.NET/Functions/Meos.Native.g.cs @@ -9213,6 +9213,24 @@ private static partial class Native [LibraryImport(DllPath, EntryPoint = "geo_edge_ctx_make", StringMarshalling = StringMarshalling.Utf8)] internal static partial IntPtr GeoEdgeCtxMake(IntPtr gs); + [LibraryImport(DllPath, EntryPoint = "geo_is_planar_linear", StringMarshalling = StringMarshalling.Utf8)] + [return: MarshalAs(UnmanagedType.U1)] + internal static partial bool GeoIsPlanarLinear(IntPtr gs); + + [LibraryImport(DllPath, EntryPoint = "geo_is_point_set", StringMarshalling = StringMarshalling.Utf8)] + [return: MarshalAs(UnmanagedType.U1)] + internal static partial bool GeoIsPointSet(IntPtr gs); + + [LibraryImport(DllPath, EntryPoint = "geo_meos_supported", StringMarshalling = StringMarshalling.Utf8)] + [return: MarshalAs(UnmanagedType.U1)] + internal static partial bool GeoMeosSupported(IntPtr gs); + + [LibraryImport(DllPath, EntryPoint = "geo_points_covered", StringMarshalling = StringMarshalling.Utf8)] + internal static partial IntPtr GeoPointsCovered(IntPtr pts, IntPtr gs, [MarshalAs(UnmanagedType.U1)] bool covered); + + [LibraryImport(DllPath, EntryPoint = "geo_clip_linear_geom", StringMarshalling = StringMarshalling.Utf8)] + internal static partial IntPtr GeoClipLinearGeom(IntPtr line, IntPtr gs, [MarshalAs(UnmanagedType.U1)] bool inside); + [LibraryImport(DllPath, EntryPoint = "geo_edge_ctx_free", StringMarshalling = StringMarshalling.Utf8)] internal static partial void GeoEdgeCtxFree(IntPtr ctx); diff --git a/MEOS.NET/Functions/Meos.meos_internal_geo.g.cs b/MEOS.NET/Functions/Meos.meos_internal_geo.g.cs index 04d0ff3..e204ad7 100644 --- a/MEOS.NET/Functions/Meos.meos_internal_geo.g.cs +++ b/MEOS.NET/Functions/Meos.meos_internal_geo.g.cs @@ -252,6 +252,21 @@ public static IntPtr TgeoseqsetRestrictStbox(IntPtr ss, IntPtr box, bool border_ public static IntPtr GeoEdgeCtxMake(IntPtr gs) => SafeExecution(() => Native.GeoEdgeCtxMake(gs)); + public static bool GeoIsPlanarLinear(IntPtr gs) + => SafeExecution(() => Native.GeoIsPlanarLinear(gs)); + + public static bool GeoIsPointSet(IntPtr gs) + => SafeExecution(() => Native.GeoIsPointSet(gs)); + + public static bool GeoMeosSupported(IntPtr gs) + => SafeExecution(() => Native.GeoMeosSupported(gs)); + + public static IntPtr GeoPointsCovered(IntPtr pts, IntPtr gs, bool covered) + => SafeExecution(() => Native.GeoPointsCovered(pts, gs, covered)); + + public static IntPtr GeoClipLinearGeom(IntPtr line, IntPtr gs, bool inside) + => SafeExecution(() => Native.GeoClipLinearGeom(line, gs, inside)); + public static void GeoEdgeCtxFree(IntPtr ctx) => SafeExecution(() => Native.GeoEdgeCtxFree(ctx)); diff --git a/MEOS.NET/Types/Geo.g.cs b/MEOS.NET/Types/Geo.g.cs index 0a1b42e..6993294 100644 --- a/MEOS.NET/Types/Geo.g.cs +++ b/MEOS.NET/Types/Geo.g.cs @@ -96,6 +96,9 @@ public ulong ToH3indexCell(int resolution) public ulong ToQuadbinCell(int resolution) => Meos.GeoToQuadbinCell(this.Ptr, resolution); + public ulong ToS2cellCell(int level) + => Meos.GeoToS2cellCell(this.Ptr, level); + public Set? ToSet() => MEOSFactory.WrapSet(Meos.GeoToSet(this.Ptr)); diff --git a/MEOS.NET/Types/Jsonb.g.cs b/MEOS.NET/Types/Jsonb.g.cs index 3f390e7..b38b1a1 100644 --- a/MEOS.NET/Types/Jsonb.g.cs +++ b/MEOS.NET/Types/Jsonb.g.cs @@ -233,6 +233,12 @@ public bool ToBool() public string ToCstring() => Meos.JsonbToCstring(this.Ptr); + public float ToFloat4() + => Meos.JsonbToFloat4(this.Ptr); + + public double ToFloat8() + => Meos.JsonbToFloat8(this.Ptr); + public short ToInt16() => Meos.JsonbToInt16(this.Ptr); diff --git a/MEOS.NET/Types/Raquet.g.cs b/MEOS.NET/Types/Raquet.g.cs index eb367d6..f159d41 100644 --- a/MEOS.NET/Types/Raquet.g.cs +++ b/MEOS.NET/Types/Raquet.g.cs @@ -143,6 +143,25 @@ public int Width() public static Raquet? In(string str) => MEOSFactory.WrapRaquet(Meos.RaquetIn(str)); + public static Raquet? Make(ulong quadbin, int width, int height, MeosPixType pixtype, double nodata, bool has_nodata, byte[] pixels) + { + GCHandle _pixels = GCHandle.Alloc(pixels, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapRaquet(Meos.RaquetMake(quadbin, width, height, (int) pixtype, nodata, has_nodata, _pixels.AddrOfPinnedObject(), (ulong) pixels.Length)); + } + finally + { + _pixels.Free(); + } + } + + public static MeosPixType PixtypeFromString(string str) + => (MeosPixType) Meos.RaquetPixtypeFromString(str); + + public static ulong PixtypeSize(MeosPixType pixtype) + => Meos.RaquetPixtypeSize((int) pixtype); + public static Raquet? Read(string path, ulong quadbin) => MEOSFactory.WrapRaquet(Meos.RaquetRead(path, quadbin)); diff --git a/MEOS.NET/Types/TGeo.g.cs b/MEOS.NET/Types/TGeo.g.cs index 3de465c..ad0bcda 100644 --- a/MEOS.NET/Types/TGeo.g.cs +++ b/MEOS.NET/Types/TGeo.g.cs @@ -61,6 +61,9 @@ internal TGeo(IntPtr ptr) : base(ptr) { } public STBox?[] SpaceBoxes(double xsize, double ysize, double zsize, Geo sorigin, bool bitmatrix, bool border_inc) => MEOSFactory.WrapSTBoxArray(Meos.TgeoSpaceBoxes(this.Ptr, xsize, ysize, zsize, sorigin.Ptr, bitmatrix, border_inc)); + public SpaceSplit SpaceSplit(double xsize, double ysize, double zsize, Geo sorigin, bool bitmatrix, bool border_inc) + => Meos.TgeoSpaceSplit(this.Ptr, xsize, ysize, zsize, sorigin.Ptr, bitmatrix, border_inc); + public STBox?[] SpaceTimeBoxes(double xsize, double ysize, double zsize, Interval duration, Geo sorigin, DateTime torigin, bool bitmatrix, bool border_inc) { IntPtr _duration = Marshal.AllocHGlobal(Marshal.SizeOf()); @@ -75,6 +78,20 @@ internal TGeo(IntPtr ptr) : base(ptr) { } } } + public SpaceTimeSplit SpaceTimeSplit(double xsize, double ysize, double zsize, Interval duration, Geo sorigin, DateTime torigin, bool bitmatrix, bool border_inc) + { + IntPtr _duration = Marshal.AllocHGlobal(Marshal.SizeOf()); + try + { + Marshal.StructureToPtr(duration, _duration, false); + return Meos.TgeoSpaceTimeSplit(this.Ptr, xsize, ysize, zsize, _duration, sorigin.Ptr, MEOSConvert.ToTimestampTz(torigin), bitmatrix, border_inc); + } + finally + { + Marshal.FreeHGlobal(_duration); + } + } + public STBox?[] SplitEachNStboxes(int elem_count) => MEOSFactory.WrapSTBoxArray(Meos.TgeoSplitEachNStboxes(this.Ptr, elem_count)); diff --git a/MEOS.NET/Types/TJsonb.g.cs b/MEOS.NET/Types/TJsonb.g.cs index 9d58489..9534c9a 100644 --- a/MEOS.NET/Types/TJsonb.g.cs +++ b/MEOS.NET/Types/TJsonb.g.cs @@ -18,6 +18,9 @@ internal TJsonb(IntPtr ptr) : base(ptr) { } public override string ToString() => this.Out(); + public Temporal? ArrayElement(int idx, bool astext, NullHandleType null_handle) + => MEOSFactory.WrapTemporal(Meos.TjsonbArrayElement(this.Ptr, idx, astext, (int) null_handle)); + public Temporal? ArrayLength() => MEOSFactory.WrapTemporal(Meos.TjsonbArrayLength(this.Ptr)); @@ -131,6 +134,25 @@ public override string ToString() } } + public Temporal? ExtractPath(Text[] path_elems, bool astext, NullHandleType null_handle) + { + IntPtr[] _path_elemsValues = new IntPtr[path_elems.Length]; + for (int i = 0; i < path_elems.Length; i++) + { + _path_elemsValues[i] = path_elems[i].Ptr; + } + + GCHandle _path_elems = GCHandle.Alloc(_path_elemsValues, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapTemporal(Meos.TjsonbExtractPath(this.Ptr, _path_elems.AddrOfPinnedObject(), path_elems.Length, astext, (int) null_handle)); + } + finally + { + _path_elems.Free(); + } + } + public Temporal? Insert(Text[] keys, Jsonb newjb, bool after) { IntPtr[] _keysValues = new IntPtr[keys.Length]; @@ -153,6 +175,9 @@ public override string ToString() public Temporal? MinusValue(Jsonb jsb) => MEOSFactory.WrapTemporal(Meos.TjsonbMinusValue(this.Ptr, jsb.Ptr)); + public Temporal? ObjectField(Text key, bool astext, NullHandleType null_handle) + => MEOSFactory.WrapTemporal(Meos.TjsonbObjectField(this.Ptr, key.Ptr, astext, (int) null_handle)); + public string Out() => Meos.TjsonbOut(this.Ptr); @@ -184,9 +209,21 @@ public string Out() public Temporal? StripNulls(bool strip_in_arrays) => MEOSFactory.WrapTemporal(Meos.TjsonbStripNulls(this.Ptr, strip_in_arrays)); + public Temporal? ToTbool(string key, NullHandleType null_handle) + => MEOSFactory.WrapTemporal(Meos.TjsonbToTbool(this.Ptr, key, (int) null_handle)); + + public Temporal? ToTfloat(string key, InterpType interp, NullHandleType null_handle) + => MEOSFactory.WrapTemporal(Meos.TjsonbToTfloat(this.Ptr, key, (int) interp, (int) null_handle)); + + public Temporal? ToTint(string key, NullHandleType null_handle) + => MEOSFactory.WrapTemporal(Meos.TjsonbToTint(this.Ptr, key, (int) null_handle)); + public Temporal? ToTtext() => MEOSFactory.WrapTemporal(Meos.TjsonbToTtext(this.Ptr)); + public Temporal? ToTtextKey(string key, NullHandleType null_handle) + => MEOSFactory.WrapTemporal(Meos.TjsonbToTtextKey(this.Ptr, key, (int) null_handle)); + public Jsonb? ValueAtTimestamptz(DateTime t, bool strict) { IntPtr _value = Marshal.AllocHGlobal(8); diff --git a/MEOS.NET/Types/TPoint.g.cs b/MEOS.NET/Types/TPoint.g.cs index f17ed83..0bbf72a 100644 --- a/MEOS.NET/Types/TPoint.g.cs +++ b/MEOS.NET/Types/TPoint.g.cs @@ -17,6 +17,9 @@ internal TPoint(IntPtr ptr) : base(ptr) { } public Temporal? AngularDifference() => MEOSFactory.WrapTemporal(Meos.TpointAngularDifference(this.Ptr)); + public MvtGeom AsMvtgeom(STBox bounds, int extent, int buffer, bool clip_geom) + => Meos.TpointAsMvtgeom(this.Ptr, bounds.Ptr, extent, buffer, clip_geom); + public Temporal? AtElevation(Span s) => MEOSFactory.WrapTemporal(Meos.TpointAtElevation(this.Ptr, s.Ptr)); diff --git a/MEOS.NET/Types/Text.g.cs b/MEOS.NET/Types/Text.g.cs index fca12d3..4148362 100644 --- a/MEOS.NET/Types/Text.g.cs +++ b/MEOS.NET/Types/Text.g.cs @@ -18,6 +18,12 @@ internal Text(IntPtr ptr) : base(ptr) { } public override string ToString() => this.Out(); + public uint Hash(uint collid) + => Meos.TextHash(this.Ptr, collid); + + public ulong HashExtended(ulong seed, uint collid) + => Meos.TextHashExtended(this.Ptr, seed, collid); + public Text? Initcap() => MEOSFactory.WrapText(Meos.TextInitcap(this.Ptr)); diff --git a/tools/objectgen.py b/tools/objectgen.py index f5c48f3..6f9cae7 100644 --- a/tools/objectgen.py +++ b/tools/objectgen.py @@ -74,14 +74,18 @@ "DateADT *": ("DateOnly", 4, "MEOSConvert.ToDateOnly(Marshal.ReadInt32({0}))"), } -# C types the object layer passes and returns as C# scalars, keyed by the cleaned -# C spelling. Everything else is either a wrapped class pointer or deferred. -PASSTHROUGH_C = { - "bool", "char", "int", "int8", "int8_t", "uint8", "uint8_t", "int16", "int16_t", - "uint16", "uint16_t", "int32", "int32_t", "uint32", "uint32_t", "int64", - "int64_t", "uint64", "uint64_t", "long", "double", "float", "size_t", - "char *", "void", -} +def is_scalar(cs_type: str) -> bool: + """Whether the wrapper already states this as a C# value the layer hands on. + + The wrapper's own C# type is what decides it: codegen resolves every scalar + the catalog names — through the typedefs it carries, so `S2CellId` reads + `ulong` and `float8` reads `double` — and a struct it carries by value is a + value too. A second list of C spellings here would answer for the ones the + layer was told about and defer the rest.""" + return cs_type in _SCALAR_CS or cs_type in codegen.BY_VALUE_STRUCTS + + +_SCALAR_CS = set(codegen.SCALAR_MAP.values()) | {"void", "bool", "string", "string?"} # Acronym runs the error names carry, kept upper-case so the C# spelling reads # the way the catalog's own camelCase names do (`asMFJSON`). @@ -308,7 +312,9 @@ def map_return(self, f: dict, wrapper_ret: str) -> tuple[str, str] | None: return ("DateOnly", "MEOSConvert.ToDateOnly($)") if wrapper_ret in ("string", "string?"): return (wrapper_ret, "$") - if c in PASSTHROUGH_C and wrapper_ret != "IntPtr": + if c in codegen.ENUM_TYPES: + return (enum_type_name(c), f"({enum_type_name(c)}) $") + if is_scalar(wrapper_ret): return (wrapper_ret, "$") cls = self.m.class_for_ctype(c) if cls and wrapper_ret == "IntPtr": @@ -361,9 +367,13 @@ def map_param(self, c_type: str, cs_type: str, name: str) -> tuple[str, str] | N return ("DateTime", f"MEOSConvert.ToTimestampTz({name})") if c == "DateADT": return ("DateOnly", f"MEOSConvert.ToDateADT({name})") - if c == "interpType": - return ("InterpType", f"(int) {name}") - if c in PASSTHROUGH_C and cs_type != "IntPtr": + if c in codegen.ENUM_TYPES: + # The wrapper takes the enum's value as an int, and the catalog + # already names every enum the surface uses — so a method takes the + # C# enum whichever one it is, rather than the one enum the layer + # was told about. + return (enum_type_name(c), f"(int) {name}") + if is_scalar(cs_type): return (cs_type, name) if cs_type == "string": return ("string", name)