diff --git a/MEOS.NET.Tests/ByValueStructTests.cs b/MEOS.NET.Tests/ByValueStructTests.cs
new file mode 100644
index 0000000..ad26c23
--- /dev/null
+++ b/MEOS.NET.Tests/ByValueStructTests.cs
@@ -0,0 +1,65 @@
+using System.Runtime.InteropServices;
+
+using MEOS.NET.Functions;
+using MEOS.NET.Structures;
+using MEOS.NET.Types;
+
+namespace MEOS.NET.Tests
+{
+ ///
+ /// The three MEOS functions that hand a struct back by value. A struct return
+ /// is not a pointer — the ABI passes it in registers or through a hidden
+ /// pointer the caller supplies — so a field read out of one is only the field
+ /// MEOS filled when the return is declared as the struct it is.
+ ///
+ [TestClass]
+ public class ByValueStructTests : MeosTest
+ {
+ private const string Trip =
+ "[POINT(1 1)@2000-01-01, POINT(2 2)@2000-01-02, POINT(3 3)@2000-01-03]";
+
+ [TestMethod]
+ public void EveryFieldOfAnMvtGeomIsTheOneMeosFilled()
+ {
+ Temporal trip = TGeomPoint.In(Trip)!;
+ STBox bounds = STBox.In("STBOX XT(((0,0),(10,10)),[2000-01-01, 2000-01-04])")!;
+
+ MvtGeom mvt = Meos.TpointAsMvtgeom(trip.Ptr, bounds.Ptr, 4096, 0, true);
+
+ // The geometry field is a GSERIALIZED the geometry functions read.
+ StringAssert.StartsWith(Meos.GeoAsText(mvt.geom, 6), "LINESTRING");
+
+ // The times field is an array of exactly count timestamps, in order.
+ Assert.IsTrue(mvt.count > 0, $"count was {mvt.count}");
+ long previous = long.MinValue;
+ for (int i = 0; i < mvt.count; i++)
+ {
+ long moment = Marshal.ReadInt64(mvt.times, i * sizeof(long));
+ Assert.IsTrue(moment > previous, $"timestamp {i} is out of order");
+ previous = moment;
+ }
+ }
+
+ [TestMethod]
+ public void EveryFieldOfASpaceSplitIsTheOneMeosFilled()
+ {
+ Temporal trip = TGeomPoint.In(Trip)!;
+ IntPtr origin = Meos.GeomIn("POINT(0 0)", -1);
+
+ SpaceSplit split = Meos.TgeoSpaceSplit(
+ trip.Ptr, 2.0, 2.0, 2.0, origin, false, true);
+
+ Assert.IsTrue(split.count > 0, $"count was {split.count}");
+ for (int i = 0; i < split.count; i++)
+ {
+ IntPtr fragment = Marshal.ReadIntPtr(split.fragments, i * IntPtr.Size);
+ IntPtr bin = Marshal.ReadIntPtr(split.bins, i * IntPtr.Size);
+
+ // Each fragment is a temporal point and each bin the geometry it
+ // falls in, so both read back through their own MEOS functions.
+ StringAssert.Contains(Meos.TspatialAsText(fragment, 6), "POINT");
+ StringAssert.StartsWith(Meos.GeoAsText(bin, 6), "POINT");
+ }
+ }
+ }
+}
diff --git a/MEOS.NET/Functions/Meos.Native.g.cs b/MEOS.NET/Functions/Meos.Native.g.cs
index b40fd7e..a5393d6 100644
--- a/MEOS.NET/Functions/Meos.Native.g.cs
+++ b/MEOS.NET/Functions/Meos.Native.g.cs
@@ -3,6 +3,8 @@
using System.CodeDom.Compiler;
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
public static partial class Meos
@@ -5746,7 +5748,7 @@ private static partial class Native
internal static partial IntPtr TgeompointToTgeometry(IntPtr temp);
[LibraryImport(DllPath, EntryPoint = "tpoint_as_mvtgeom", StringMarshalling = StringMarshalling.Utf8)]
- internal static partial IntPtr TpointAsMvtgeom(IntPtr temp, IntPtr bounds, int extent, int buffer, [MarshalAs(UnmanagedType.U1)] bool clip_geom);
+ internal static partial MvtGeom TpointAsMvtgeom(IntPtr temp, IntPtr bounds, int extent, int buffer, [MarshalAs(UnmanagedType.U1)] bool clip_geom);
[LibraryImport(DllPath, EntryPoint = "tpoint_tfloat_to_geomeas", StringMarshalling = StringMarshalling.Utf8)]
[return: MarshalAs(UnmanagedType.U1)]
@@ -6481,10 +6483,10 @@ private static partial class Native
internal static partial IntPtr StboxTimeTiles(IntPtr bounds, IntPtr duration, long torigin, [MarshalAs(UnmanagedType.U1)] bool border_inc, IntPtr count);
[LibraryImport(DllPath, EntryPoint = "tgeo_space_split", StringMarshalling = StringMarshalling.Utf8)]
- internal static partial IntPtr TgeoSpaceSplit(IntPtr temp, double xsize, double ysize, double zsize, IntPtr sorigin, [MarshalAs(UnmanagedType.U1)] bool bitmatrix, [MarshalAs(UnmanagedType.U1)] bool border_inc);
+ internal static partial SpaceSplit TgeoSpaceSplit(IntPtr temp, double xsize, double ysize, double zsize, IntPtr sorigin, [MarshalAs(UnmanagedType.U1)] bool bitmatrix, [MarshalAs(UnmanagedType.U1)] bool border_inc);
[LibraryImport(DllPath, EntryPoint = "tgeo_space_time_split", StringMarshalling = StringMarshalling.Utf8)]
- internal static partial IntPtr TgeoSpaceTimeSplit(IntPtr temp, double xsize, double ysize, double zsize, IntPtr duration, IntPtr sorigin, long torigin, [MarshalAs(UnmanagedType.U1)] bool bitmatrix, [MarshalAs(UnmanagedType.U1)] bool border_inc);
+ internal static partial SpaceTimeSplit TgeoSpaceTimeSplit(IntPtr temp, double xsize, double ysize, double zsize, IntPtr duration, IntPtr sorigin, long torigin, [MarshalAs(UnmanagedType.U1)] bool bitmatrix, [MarshalAs(UnmanagedType.U1)] bool border_inc);
[LibraryImport(DllPath, EntryPoint = "geo_cluster_kmeans", StringMarshalling = StringMarshalling.Utf8)]
internal static partial IntPtr GeoClusterKmeans(IntPtr geoms, uint ngeoms, uint k, IntPtr count);
diff --git a/MEOS.NET/Functions/Meos.hashtable.g.cs b/MEOS.NET/Functions/Meos.hashtable.g.cs
index 0046658..0edb4ac 100644
--- a/MEOS.NET/Functions/Meos.hashtable.g.cs
+++ b/MEOS.NET/Functions/Meos.hashtable.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions hashtable.h declares.
diff --git a/MEOS.NET/Functions/Meos.meos.g.cs b/MEOS.NET/Functions/Meos.meos.g.cs
index 3f8d40f..f6ded9e 100644
--- a/MEOS.NET/Functions/Meos.meos.g.cs
+++ b/MEOS.NET/Functions/Meos.meos.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions meos.h declares.
diff --git a/MEOS.NET/Functions/Meos.meos_catalog.g.cs b/MEOS.NET/Functions/Meos.meos_catalog.g.cs
index fe6baf3..ab3a246 100644
--- a/MEOS.NET/Functions/Meos.meos_catalog.g.cs
+++ b/MEOS.NET/Functions/Meos.meos_catalog.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions meos_catalog.h declares.
diff --git a/MEOS.NET/Functions/Meos.meos_cbuffer.g.cs b/MEOS.NET/Functions/Meos.meos_cbuffer.g.cs
index a2fe1af..5147c89 100644
--- a/MEOS.NET/Functions/Meos.meos_cbuffer.g.cs
+++ b/MEOS.NET/Functions/Meos.meos_cbuffer.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions meos_cbuffer.h declares.
diff --git a/MEOS.NET/Functions/Meos.meos_cellindex.g.cs b/MEOS.NET/Functions/Meos.meos_cellindex.g.cs
index 7baeef9..1ab9ebc 100644
--- a/MEOS.NET/Functions/Meos.meos_cellindex.g.cs
+++ b/MEOS.NET/Functions/Meos.meos_cellindex.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions meos_cellindex.h declares.
diff --git a/MEOS.NET/Functions/Meos.meos_geo.g.cs b/MEOS.NET/Functions/Meos.meos_geo.g.cs
index 3a1f76f..8a08333 100644
--- a/MEOS.NET/Functions/Meos.meos_geo.g.cs
+++ b/MEOS.NET/Functions/Meos.meos_geo.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions meos_geo.h declares.
@@ -760,8 +762,8 @@ public static IntPtr TgeometryToTgeompoint(IntPtr temp)
public static IntPtr TgeompointToTgeometry(IntPtr temp)
=> SafeExecution(() => Native.TgeompointToTgeometry(temp));
- public static IntPtr TpointAsMvtgeom(IntPtr temp, IntPtr bounds, int extent, int buffer, bool clip_geom)
- => SafeExecution(() => Native.TpointAsMvtgeom(temp, bounds, extent, buffer, clip_geom));
+ public static MvtGeom TpointAsMvtgeom(IntPtr temp, IntPtr bounds, int extent, int buffer, bool clip_geom)
+ => SafeExecution(() => Native.TpointAsMvtgeom(temp, bounds, extent, buffer, clip_geom));
public static bool TpointTfloatToGeomeas(IntPtr tpoint, IntPtr measure, bool segmentize, IntPtr result)
=> SafeExecution(() => Native.TpointTfloatToGeomeas(tpoint, measure, segmentize, result));
@@ -1714,11 +1716,11 @@ public static IntPtr[] StboxTimeTiles(IntPtr bounds, IntPtr duration, long torig
finally { Marshal.FreeHGlobal(_cnt); }
}
- public static IntPtr TgeoSpaceSplit(IntPtr temp, double xsize, double ysize, double zsize, IntPtr sorigin, bool bitmatrix, bool border_inc)
- => SafeExecution(() => Native.TgeoSpaceSplit(temp, xsize, ysize, zsize, sorigin, bitmatrix, border_inc));
+ public static SpaceSplit TgeoSpaceSplit(IntPtr temp, double xsize, double ysize, double zsize, IntPtr sorigin, bool bitmatrix, bool border_inc)
+ => SafeExecution(() => Native.TgeoSpaceSplit(temp, xsize, ysize, zsize, sorigin, bitmatrix, border_inc));
- public static IntPtr TgeoSpaceTimeSplit(IntPtr temp, double xsize, double ysize, double zsize, IntPtr duration, IntPtr sorigin, long torigin, bool bitmatrix, bool border_inc)
- => SafeExecution(() => Native.TgeoSpaceTimeSplit(temp, xsize, ysize, zsize, duration, sorigin, torigin, bitmatrix, border_inc));
+ public static SpaceTimeSplit TgeoSpaceTimeSplit(IntPtr temp, double xsize, double ysize, double zsize, IntPtr duration, IntPtr sorigin, long torigin, bool bitmatrix, bool border_inc)
+ => SafeExecution(() => Native.TgeoSpaceTimeSplit(temp, xsize, ysize, zsize, duration, sorigin, torigin, bitmatrix, border_inc));
public static int[] GeoClusterKmeans(IntPtr geoms, uint ngeoms, uint k)
{
diff --git a/MEOS.NET/Functions/Meos.meos_h3.g.cs b/MEOS.NET/Functions/Meos.meos_h3.g.cs
index edd4f5e..3d047f9 100644
--- a/MEOS.NET/Functions/Meos.meos_h3.g.cs
+++ b/MEOS.NET/Functions/Meos.meos_h3.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions meos_h3.h declares.
diff --git a/MEOS.NET/Functions/Meos.meos_internal.g.cs b/MEOS.NET/Functions/Meos.meos_internal.g.cs
index 8e8612b..4208a3a 100644
--- a/MEOS.NET/Functions/Meos.meos_internal.g.cs
+++ b/MEOS.NET/Functions/Meos.meos_internal.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions meos_internal.h declares.
diff --git a/MEOS.NET/Functions/Meos.meos_internal_geo.g.cs b/MEOS.NET/Functions/Meos.meos_internal_geo.g.cs
index 5e2f247..04d0ff3 100644
--- a/MEOS.NET/Functions/Meos.meos_internal_geo.g.cs
+++ b/MEOS.NET/Functions/Meos.meos_internal_geo.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions meos_internal_geo.h declares.
diff --git a/MEOS.NET/Functions/Meos.meos_json.g.cs b/MEOS.NET/Functions/Meos.meos_json.g.cs
index 5385087..10f7a4c 100644
--- a/MEOS.NET/Functions/Meos.meos_json.g.cs
+++ b/MEOS.NET/Functions/Meos.meos_json.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions meos_json.h declares.
diff --git a/MEOS.NET/Functions/Meos.meos_npoint.g.cs b/MEOS.NET/Functions/Meos.meos_npoint.g.cs
index a7eccdb..94245a1 100644
--- a/MEOS.NET/Functions/Meos.meos_npoint.g.cs
+++ b/MEOS.NET/Functions/Meos.meos_npoint.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions meos_npoint.h declares.
diff --git a/MEOS.NET/Functions/Meos.meos_pointcloud.g.cs b/MEOS.NET/Functions/Meos.meos_pointcloud.g.cs
index 351ea04..f2ff594 100644
--- a/MEOS.NET/Functions/Meos.meos_pointcloud.g.cs
+++ b/MEOS.NET/Functions/Meos.meos_pointcloud.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions meos_pointcloud.h declares.
diff --git a/MEOS.NET/Functions/Meos.meos_pose.g.cs b/MEOS.NET/Functions/Meos.meos_pose.g.cs
index 6a21533..a0435af 100644
--- a/MEOS.NET/Functions/Meos.meos_pose.g.cs
+++ b/MEOS.NET/Functions/Meos.meos_pose.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions meos_pose.h declares.
diff --git a/MEOS.NET/Functions/Meos.meos_quadbin.g.cs b/MEOS.NET/Functions/Meos.meos_quadbin.g.cs
index 1d979a5..22e4723 100644
--- a/MEOS.NET/Functions/Meos.meos_quadbin.g.cs
+++ b/MEOS.NET/Functions/Meos.meos_quadbin.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions meos_quadbin.h declares.
diff --git a/MEOS.NET/Functions/Meos.meos_raster.g.cs b/MEOS.NET/Functions/Meos.meos_raster.g.cs
index 47cc5f7..d585ecd 100644
--- a/MEOS.NET/Functions/Meos.meos_raster.g.cs
+++ b/MEOS.NET/Functions/Meos.meos_raster.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions meos_raster.h declares.
diff --git a/MEOS.NET/Functions/Meos.meos_rgeo.g.cs b/MEOS.NET/Functions/Meos.meos_rgeo.g.cs
index ad2c484..8449d4b 100644
--- a/MEOS.NET/Functions/Meos.meos_rgeo.g.cs
+++ b/MEOS.NET/Functions/Meos.meos_rgeo.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions meos_rgeo.h declares.
diff --git a/MEOS.NET/Functions/Meos.meos_s2cell.g.cs b/MEOS.NET/Functions/Meos.meos_s2cell.g.cs
index 42bd0a1..23621c6 100644
--- a/MEOS.NET/Functions/Meos.meos_s2cell.g.cs
+++ b/MEOS.NET/Functions/Meos.meos_s2cell.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions meos_s2cell.h declares.
diff --git a/MEOS.NET/Functions/Meos.pc_api.g.cs b/MEOS.NET/Functions/Meos.pc_api.g.cs
index 2ed3dd7..ba74bcf 100644
--- a/MEOS.NET/Functions/Meos.pc_api.g.cs
+++ b/MEOS.NET/Functions/Meos.pc_api.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions pc_api.h declares.
diff --git a/MEOS.NET/Functions/Meos.pg_bool.g.cs b/MEOS.NET/Functions/Meos.pg_bool.g.cs
index cc8bb5a..454d7c3 100644
--- a/MEOS.NET/Functions/Meos.pg_bool.g.cs
+++ b/MEOS.NET/Functions/Meos.pg_bool.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions pg_bool.h declares.
diff --git a/MEOS.NET/Functions/Meos.pg_date.g.cs b/MEOS.NET/Functions/Meos.pg_date.g.cs
index eddf146..649cb74 100644
--- a/MEOS.NET/Functions/Meos.pg_date.g.cs
+++ b/MEOS.NET/Functions/Meos.pg_date.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions pg_date.h declares.
diff --git a/MEOS.NET/Functions/Meos.pg_float.g.cs b/MEOS.NET/Functions/Meos.pg_float.g.cs
index 02a4f88..ebb7301 100644
--- a/MEOS.NET/Functions/Meos.pg_float.g.cs
+++ b/MEOS.NET/Functions/Meos.pg_float.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions pg_float.h declares.
diff --git a/MEOS.NET/Functions/Meos.pg_int.g.cs b/MEOS.NET/Functions/Meos.pg_int.g.cs
index 3ad943d..711b318 100644
--- a/MEOS.NET/Functions/Meos.pg_int.g.cs
+++ b/MEOS.NET/Functions/Meos.pg_int.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions pg_int.h declares.
diff --git a/MEOS.NET/Functions/Meos.pg_interval.g.cs b/MEOS.NET/Functions/Meos.pg_interval.g.cs
index 8d6a1a8..5e28934 100644
--- a/MEOS.NET/Functions/Meos.pg_interval.g.cs
+++ b/MEOS.NET/Functions/Meos.pg_interval.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions pg_interval.h declares.
diff --git a/MEOS.NET/Functions/Meos.pg_numeric.g.cs b/MEOS.NET/Functions/Meos.pg_numeric.g.cs
index 62e037e..a743327 100644
--- a/MEOS.NET/Functions/Meos.pg_numeric.g.cs
+++ b/MEOS.NET/Functions/Meos.pg_numeric.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions pg_numeric.h declares.
diff --git a/MEOS.NET/Functions/Meos.pg_text.g.cs b/MEOS.NET/Functions/Meos.pg_text.g.cs
index 4c5ec7e..0fbfc13 100644
--- a/MEOS.NET/Functions/Meos.pg_text.g.cs
+++ b/MEOS.NET/Functions/Meos.pg_text.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions pg_text.h declares.
diff --git a/MEOS.NET/Functions/Meos.pg_time.g.cs b/MEOS.NET/Functions/Meos.pg_time.g.cs
index 06a32e8..f2e43b5 100644
--- a/MEOS.NET/Functions/Meos.pg_time.g.cs
+++ b/MEOS.NET/Functions/Meos.pg_time.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions pg_time.h declares.
diff --git a/MEOS.NET/Functions/Meos.pg_timestamp.g.cs b/MEOS.NET/Functions/Meos.pg_timestamp.g.cs
index b5f18bc..43ad7b0 100644
--- a/MEOS.NET/Functions/Meos.pg_timestamp.g.cs
+++ b/MEOS.NET/Functions/Meos.pg_timestamp.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions pg_timestamp.h declares.
diff --git a/MEOS.NET/Functions/Meos.pgsql_compat.g.cs b/MEOS.NET/Functions/Meos.pgsql_compat.g.cs
index 770134e..86d8eab 100644
--- a/MEOS.NET/Functions/Meos.pgsql_compat.g.cs
+++ b/MEOS.NET/Functions/Meos.pgsql_compat.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions pgsql_compat.h declares.
diff --git a/MEOS.NET/Functions/Meos.pgtypes.g.cs b/MEOS.NET/Functions/Meos.pgtypes.g.cs
index 6a77cde..5dd3553 100644
--- a/MEOS.NET/Functions/Meos.pgtypes.g.cs
+++ b/MEOS.NET/Functions/Meos.pgtypes.g.cs
@@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
+using MEOS.NET.Structures;
+
namespace MEOS.NET.Functions
{
/// The MEOS functions pgtypes.h declares.
diff --git a/MEOS.NET/Structures/MeosStructs.g.cs b/MEOS.NET/Structures/MeosStructs.g.cs
new file mode 100644
index 0000000..8728d90
--- /dev/null
+++ b/MEOS.NET/Structures/MeosStructs.g.cs
@@ -0,0 +1,39 @@
+#nullable enable
+
+using System.CodeDom.Compiler;
+using System.Runtime.InteropServices;
+
+namespace MEOS.NET.Structures
+{
+ /// The MEOS MvtGeom struct, as MEOS returns it.
+ [GeneratedCode("MEOS.NET.Codegen", "0.1.0")]
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MvtGeom
+ {
+ public IntPtr geom;
+ public IntPtr times;
+ public int count;
+ }
+
+ /// The MEOS SpaceSplit struct, as MEOS returns it.
+ [GeneratedCode("MEOS.NET.Codegen", "0.1.0")]
+ [StructLayout(LayoutKind.Sequential)]
+ public struct SpaceSplit
+ {
+ public IntPtr fragments;
+ public IntPtr bins;
+ public int count;
+ }
+
+ /// The MEOS SpaceTimeSplit struct, as MEOS returns it.
+ [GeneratedCode("MEOS.NET.Codegen", "0.1.0")]
+ [StructLayout(LayoutKind.Sequential)]
+ public struct SpaceTimeSplit
+ {
+ public IntPtr fragments;
+ public IntPtr space_bins;
+ public IntPtr time_bins;
+ public int count;
+ }
+
+}
diff --git a/tools/codegen.py b/tools/codegen.py
index 281a751..4d2c277 100755
--- a/tools/codegen.py
+++ b/tools/codegen.py
@@ -27,6 +27,7 @@
# per MEOS header exactly as the catalog groups the functions.
NAMESPACE = "MEOS.NET.Functions"
CLASS = "Meos"
+STRUCT_NAMESPACE = "MEOS.NET.Structures"
# Canonical C type -> C# parameter/return type.
# `canonical` field from meos-idl.json is libclang-normalized (e.g. int32_t -> int).
@@ -89,6 +90,12 @@ def configure(idl: dict) -> None:
ENUM_TYPES.update(e["name"] for e in idl.get("enums", []) if e.get("name"))
STRUCTS.clear()
STRUCTS.update({s["name"]: s for s in idl.get("structs", []) if s.get("fields")})
+ BY_VALUE_STRUCTS.clear()
+ for f in idl.get("functions", []):
+ for spot in [f["returnType"]] + list(f.get("params", [])):
+ canonical = spot["canonical"].replace("const ", "").strip()
+ if "*" not in canonical and canonical in STRUCTS:
+ BY_VALUE_STRUCTS.add(canonical)
# Size and alignment in bytes of the scalar C types a struct field can have, on
@@ -104,6 +111,56 @@ def configure(idl: dict) -> None:
}
+# The catalog structs a function returns or takes BY VALUE, filled in by
+# ``configure``. Those are the only ones the binding gives a C# type: every
+# other MEOS struct crosses the boundary as a pointer and stays opaque.
+BY_VALUE_STRUCTS: set[str] = set()
+
+
+def csharp_field_type(c_type: str) -> str:
+ """The C# type of a by-value struct's field."""
+ t = c_type.replace("const ", "").strip()
+ if t.endswith("*"):
+ return "IntPtr"
+ if t in ENUM_TYPES:
+ return "int"
+ if t not in SCALAR_MAP:
+ raise SystemExit(f"codegen: struct field type {t!r} has no C# mapping")
+ return SCALAR_MAP[t]
+
+
+def gen_structs() -> str:
+ """The C# form of each struct a function hands back by value.
+
+ A struct return is not a pointer: the ABI passes it in registers or through a
+ hidden pointer the caller supplies, so declaring it as an IntPtr reads an
+ address where there is none."""
+ lines = [
+ "#nullable enable",
+ "",
+ "using System.CodeDom.Compiler;",
+ "using System.Runtime.InteropServices;",
+ "",
+ f"namespace {STRUCT_NAMESPACE}",
+ "{",
+ ]
+ for name in sorted(BY_VALUE_STRUCTS):
+ struct = STRUCTS[name]
+ lines += [
+ f" /// The MEOS {name} struct, as MEOS returns it.",
+ f' [GeneratedCode("MEOS.NET.Codegen", "{GENERATOR_VERSION}")]',
+ " [StructLayout(LayoutKind.Sequential)]",
+ f" public struct {name}",
+ " {",
+ ]
+ for field in struct["fields"]:
+ lines.append(f" public {csharp_field_type(field['cType'])} "
+ f"{csharp_param_name(field['name'])};")
+ lines += [" }", ""]
+ lines += ["}", ""]
+ return "\n".join(lines)
+
+
def _type_layout(c_type: str) -> tuple[int, int]:
"""``(size, alignment)`` in bytes of a struct field's type: a scalar, a
pointer, a fixed-size array, or a nested catalog struct."""
@@ -160,6 +217,8 @@ def csharp_type_for(canonical: str) -> str:
return "IntPtr"
if t in ENUM_TYPES:
return "int"
+ if t in BY_VALUE_STRUCTS:
+ return t
return SCALAR_MAP.get(t, "IntPtr") # unknown scalar -> opaque pointer is safer than guessing
@@ -214,6 +273,8 @@ def gen_external_functions(funcs: list[dict]) -> str:
lines.append("using System.CodeDom.Compiler;")
lines.append("using System.Runtime.InteropServices;")
lines.append("")
+ lines.append(f"using {STRUCT_NAMESPACE};")
+ lines.append("")
lines.append(f"namespace {NAMESPACE}")
lines.append("{")
lines.append(f" public static partial class {CLASS}")
@@ -547,6 +608,8 @@ def gen_exposed_functions(funcs: list[dict], header: str | None = None) -> str:
lines.append("")
lines.append("using System.Runtime.InteropServices;")
lines.append("")
+ lines.append(f"using {STRUCT_NAMESPACE};")
+ lines.append("")
lines.append(f"namespace {NAMESPACE}")
lines.append("{")
if header:
@@ -624,13 +687,21 @@ def main(idl_path: str, dll_path: str = DLL_PATH) -> None:
if clashing:
raise SystemExit(f"codegen: C names sharing one C# name: {clashing}")
+ struct_dir = repo_root / "MEOS.NET" / "Structures"
+ if struct_dir.exists():
+ for stale in struct_dir.glob("*.g.cs"):
+ stale.unlink()
+ struct_dir.mkdir(parents=True, exist_ok=True)
+ (struct_dir / "MeosStructs.g.cs").write_text(gen_structs())
+
(out_dir / "Meos.Native.g.cs").write_text(gen_external_functions(funcs))
grouped = by_header(funcs)
for header, group in sorted(grouped.items()):
stem = header.removesuffix(".h")
(out_dir / f"Meos.{stem}.g.cs").write_text(gen_exposed_functions(group, header))
print(f"Wrote {len(funcs)} functions across {len(grouped)} headers "
- f"to MEOS.NET/Functions/", file=sys.stderr)
+ f"to MEOS.NET/Functions/, {len(BY_VALUE_STRUCTS)} by-value structs",
+ file=sys.stderr)
if __name__ == "__main__":