diff --git a/MEOS.NET.Tests/ByteBufferTests.cs b/MEOS.NET.Tests/ByteBufferTests.cs new file mode 100644 index 0000000..35279b5 --- /dev/null +++ b/MEOS.NET.Tests/ByteBufferTests.cs @@ -0,0 +1,54 @@ +using MEOS.NET.Types; + +namespace MEOS.NET.Tests +{ + /// + /// MEOS states the length of a buffer it answers through a pointer, and reads + /// one back as a pointer and a length. Both are one C# array to the caller. + /// + [TestClass] + public class ByteBufferTests : MeosTest + { + [TestMethod] + public void ASpanRoundTripsThroughItsOwnWKB() + { + Span span = FloatSpan.In("[8, 10]")!; + + byte[]? wkb = span.AsWKB(0); + + Assert.IsNotNull(wkb); + Assert.IsTrue(wkb!.Length > 0); + + Span? read = Span.FromWKB(wkb); + + Assert.IsNotNull(read); + Assert.AreEqual("[8, 10]", read!.ToString()); + } + + [TestMethod] + public void ATemporalValueRoundTripsThroughItsOwnWKB() + { + Temporal temp = TFloat.In("[25.5@2024-12-06, 27.5@2024-12-08]")!; + + byte[]? wkb = temp.AsWKB(0); + + Assert.IsNotNull(wkb); + + Temporal? read = Temporal.FromWKB(wkb!); + + Assert.IsNotNull(read); + Assert.AreEqual(temp.ToString(), read!.ToString()); + } + + [TestMethod] + public void TheHexFormNeedsNoLengthOfItsOwn() + { + Span span = FloatSpan.In("[8, 10]")!; + + string hex = span.AsHEXWKB(0); + + Assert.IsTrue(hex.Length > 0); + Assert.AreEqual("[8, 10]", Span.FromHEXWKB(hex)!.ToString()); + } + } +} diff --git a/MEOS.NET/Types/BigIntSet.g.cs b/MEOS.NET/Types/BigIntSet.g.cs index babb57f..b48cb5c 100644 --- a/MEOS.NET/Types/BigIntSet.g.cs +++ b/MEOS.NET/Types/BigIntSet.g.cs @@ -54,5 +54,18 @@ public long[] Values() public static Set? In(string str) => MEOSFactory.WrapSet(Meos.BigintsetIn(str)); + public static Set? Make(long[] values) + { + GCHandle _values = GCHandle.Alloc(values, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapSet(Meos.BigintsetMake(_values.AddrOfPinnedObject(), values.Length)); + } + finally + { + _values.Free(); + } + } + } } diff --git a/MEOS.NET/Types/Cbuffer.g.cs b/MEOS.NET/Types/Cbuffer.g.cs index 22713e7..1c978e0 100644 --- a/MEOS.NET/Types/Cbuffer.g.cs +++ b/MEOS.NET/Types/Cbuffer.g.cs @@ -21,9 +21,43 @@ public override string ToString() public string AsEWKT(int maxdd) => Meos.CbufferAsEwkt(this.Ptr, maxdd); + public string AsHEXWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + return Meos.CbufferAsHexwkb(this.Ptr, variant, _size_out); + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + public string AsText(int maxdd) => Meos.CbufferAsText(this.Ptr, maxdd); + public byte[]? AsWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + IntPtr _bytes = Meos.CbufferAsWkb(this.Ptr, variant, _size_out); + if (_bytes == IntPtr.Zero) + { + return null; + } + + byte[] _wkb = new byte[Marshal.ReadInt64(_size_out)]; + Marshal.Copy(_bytes, _wkb, 0, _wkb.Length); + return _wkb; + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + public int Cmp(Cbuffer cb2) => Meos.CbufferCmp(this.Ptr, cb2.Ptr); @@ -102,6 +136,19 @@ public bool Same(Cbuffer cb2) public static Cbuffer? FromHEXWKB(string hexwkb) => MEOSFactory.WrapCbuffer(Meos.CbufferFromHexwkb(hexwkb)); + public static Cbuffer? FromWKB(byte[] wkb) + { + GCHandle _wkb = GCHandle.Alloc(wkb, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapCbuffer(Meos.CbufferFromWkb(_wkb.AddrOfPinnedObject(), (ulong) wkb.Length)); + } + finally + { + _wkb.Free(); + } + } + public static Cbuffer? In(string str) => MEOSFactory.WrapCbuffer(Meos.CbufferIn(str)); diff --git a/MEOS.NET/Types/DateSet.g.cs b/MEOS.NET/Types/DateSet.g.cs index 779b5cf..f7cbb2a 100644 --- a/MEOS.NET/Types/DateSet.g.cs +++ b/MEOS.NET/Types/DateSet.g.cs @@ -57,5 +57,18 @@ public int[] Values() public static Set? In(string str) => MEOSFactory.WrapSet(Meos.DatesetIn(str)); + public static Set? Make(int[] values) + { + GCHandle _values = GCHandle.Alloc(values, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapSet(Meos.DatesetMake(_values.AddrOfPinnedObject(), values.Length)); + } + finally + { + _values.Free(); + } + } + } } diff --git a/MEOS.NET/Types/FloatSet.g.cs b/MEOS.NET/Types/FloatSet.g.cs index 89889d1..366d2fa 100644 --- a/MEOS.NET/Types/FloatSet.g.cs +++ b/MEOS.NET/Types/FloatSet.g.cs @@ -69,5 +69,18 @@ public double[] Values() public static Set? In(string str) => MEOSFactory.WrapSet(Meos.FloatsetIn(str)); + public static Set? Make(double[] values) + { + GCHandle _values = GCHandle.Alloc(values, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapSet(Meos.FloatsetMake(_values.AddrOfPinnedObject(), values.Length)); + } + finally + { + _values.Free(); + } + } + } } diff --git a/MEOS.NET/Types/IntSet.g.cs b/MEOS.NET/Types/IntSet.g.cs index 2f71aea..52bcb87 100644 --- a/MEOS.NET/Types/IntSet.g.cs +++ b/MEOS.NET/Types/IntSet.g.cs @@ -57,5 +57,18 @@ public int[] Values() public static Set? In(string str) => MEOSFactory.WrapSet(Meos.IntsetIn(str)); + public static Set? Make(int[] values) + { + GCHandle _values = GCHandle.Alloc(values, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapSet(Meos.IntsetMake(_values.AddrOfPinnedObject(), values.Length)); + } + finally + { + _values.Free(); + } + } + } } diff --git a/MEOS.NET/Types/Npoint.g.cs b/MEOS.NET/Types/Npoint.g.cs index a6a3919..e8cd438 100644 --- a/MEOS.NET/Types/Npoint.g.cs +++ b/MEOS.NET/Types/Npoint.g.cs @@ -21,9 +21,43 @@ public override string ToString() public string AsEWKT(int maxdd) => Meos.NpointAsEwkt(this.Ptr, maxdd); + public string AsHEXWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + return Meos.NpointAsHexwkb(this.Ptr, variant, _size_out); + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + public string AsText(int maxdd) => Meos.NpointAsText(this.Ptr, maxdd); + public byte[]? AsWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + IntPtr _bytes = Meos.NpointAsWkb(this.Ptr, variant, _size_out); + if (_bytes == IntPtr.Zero) + { + return null; + } + + byte[] _wkb = new byte[Marshal.ReadInt64(_size_out)]; + Marshal.Copy(_bytes, _wkb, 0, _wkb.Length); + return _wkb; + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + public int Cmp(Npoint np2) => Meos.NpointCmp(this.Ptr, np2.Ptr); @@ -90,6 +124,19 @@ public bool Same(Npoint np2) public static Npoint? FromHEXWKB(string hexwkb) => MEOSFactory.WrapNpoint(Meos.NpointFromHexwkb(hexwkb)); + public static Npoint? FromWKB(byte[] wkb) + { + GCHandle _wkb = GCHandle.Alloc(wkb, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapNpoint(Meos.NpointFromWkb(_wkb.AddrOfPinnedObject(), (ulong) wkb.Length)); + } + finally + { + _wkb.Free(); + } + } + public static Npoint? In(string str) => MEOSFactory.WrapNpoint(Meos.NpointIn(str)); diff --git a/MEOS.NET/Types/Pcpatch.g.cs b/MEOS.NET/Types/Pcpatch.g.cs index 9b11a35..bc98e83 100644 --- a/MEOS.NET/Types/Pcpatch.g.cs +++ b/MEOS.NET/Types/Pcpatch.g.cs @@ -78,5 +78,18 @@ public uint Npoints() } } + public static Pcpatch? MakeCoords(uint pcid, double[] values) + { + GCHandle _values = GCHandle.Alloc(values, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapPcpatch(Meos.PcpatchMakeCoords(pcid, _values.AddrOfPinnedObject(), values.Length)); + } + finally + { + _values.Free(); + } + } + } } diff --git a/MEOS.NET/Types/Pcpoint.g.cs b/MEOS.NET/Types/Pcpoint.g.cs index f006758..861f9be 100644 --- a/MEOS.NET/Types/Pcpoint.g.cs +++ b/MEOS.NET/Types/Pcpoint.g.cs @@ -44,5 +44,18 @@ public string HexOut(int maxdd) public static Pcpoint? HexIn(string str) => MEOSFactory.WrapPcpoint(Meos.PcpointHexIn(str)); + public static Pcpoint? Make(uint pcid, double[] values) + { + GCHandle _values = GCHandle.Alloc(values, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapPcpoint(Meos.PcpointMake(pcid, _values.AddrOfPinnedObject(), values.Length)); + } + finally + { + _values.Free(); + } + } + } } diff --git a/MEOS.NET/Types/Pose.g.cs b/MEOS.NET/Types/Pose.g.cs index 49beaa8..94dd808 100644 --- a/MEOS.NET/Types/Pose.g.cs +++ b/MEOS.NET/Types/Pose.g.cs @@ -30,9 +30,43 @@ public string AsEWKT(int maxdd) public string AsGeopose(int conformance, int precision) => Meos.PoseAsGeopose(this.Ptr, conformance, precision); + public string AsHEXWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + return Meos.PoseAsHexwkb(this.Ptr, variant, _size_out); + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + public string AsText(int maxdd) => Meos.PoseAsText(this.Ptr, maxdd); + public byte[]? AsWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + IntPtr _bytes = Meos.PoseAsWkb(this.Ptr, variant, _size_out); + if (_bytes == IntPtr.Zero) + { + return null; + } + + byte[] _wkb = new byte[Marshal.ReadInt64(_size_out)]; + Marshal.Copy(_bytes, _wkb, 0, _wkb.Length); + return _wkb; + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + public int Cmp(Pose pose2) => Meos.PoseCmp(this.Ptr, pose2.Ptr); @@ -138,6 +172,19 @@ public double[] Ypr() public static Pose? FromHEXWKB(string hexwkb) => MEOSFactory.WrapPose(Meos.PoseFromHexwkb(hexwkb)); + public static Pose? FromWKB(byte[] wkb) + { + GCHandle _wkb = GCHandle.Alloc(wkb, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapPose(Meos.PoseFromWkb(_wkb.AddrOfPinnedObject(), (ulong) wkb.Length)); + } + finally + { + _wkb.Free(); + } + } + public static Pose? In(string str) => MEOSFactory.WrapPose(Meos.PoseIn(str)); diff --git a/MEOS.NET/Types/PoseChain.g.cs b/MEOS.NET/Types/PoseChain.g.cs index 3a4c08a..1acd4af 100644 --- a/MEOS.NET/Types/PoseChain.g.cs +++ b/MEOS.NET/Types/PoseChain.g.cs @@ -24,9 +24,43 @@ public override string ToString() public string AsEWKT(int maxdd) => Meos.PosechainAsEwkt(this.Ptr, maxdd); + public string AsHEXWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + return Meos.PosechainAsHexwkb(this.Ptr, variant, _size_out); + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + public string AsText(int maxdd) => Meos.PosechainAsText(this.Ptr, maxdd); + public byte[]? AsWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + IntPtr _bytes = Meos.PosechainAsWkb(this.Ptr, variant, _size_out); + if (_bytes == IntPtr.Zero) + { + return null; + } + + byte[] _wkb = new byte[Marshal.ReadInt64(_size_out)]; + Marshal.Copy(_bytes, _wkb, 0, _wkb.Length); + return _wkb; + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + public int Cmp(PoseChain pc2) => Meos.PosechainCmp(this.Ptr, pc2.Ptr); @@ -120,6 +154,19 @@ public bool Same(PoseChain pc2) public static PoseChain? FromHEXWKB(string hexwkb) => MEOSFactory.WrapPoseChain(Meos.PosechainFromHexwkb(hexwkb)); + public static PoseChain? FromWKB(byte[] wkb) + { + GCHandle _wkb = GCHandle.Alloc(wkb, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapPoseChain(Meos.PosechainFromWkb(_wkb.AddrOfPinnedObject(), (ulong) wkb.Length)); + } + finally + { + _wkb.Free(); + } + } + public static PoseChain? In(string str) => MEOSFactory.WrapPoseChain(Meos.PosechainIn(str)); diff --git a/MEOS.NET/Types/Raquet.g.cs b/MEOS.NET/Types/Raquet.g.cs index 2acc778..eb367d6 100644 --- a/MEOS.NET/Types/Raquet.g.cs +++ b/MEOS.NET/Types/Raquet.g.cs @@ -18,6 +18,40 @@ internal Raquet(IntPtr ptr) : base(ptr) { } public override string ToString() => this.Out(); + public string AsHEXWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + return Meos.RaquetAsHexwkb(this.Ptr, variant, _size_out); + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + + public byte[]? AsWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + IntPtr _bytes = Meos.RaquetAsWkb(this.Ptr, variant, _size_out); + if (_bytes == IntPtr.Zero) + { + return null; + } + + byte[] _wkb = new byte[Marshal.ReadInt64(_size_out)]; + Marshal.Copy(_bytes, _wkb, 0, _wkb.Length); + return _wkb; + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + public int Cmp(Raquet rq2) => Meos.RaquetCmp(this.Ptr, rq2.Ptr); @@ -57,6 +91,27 @@ public double Nodata() public string Out() => Meos.RaquetOut(this.Ptr); + public byte[]? Pixels() + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + IntPtr _bytes = Meos.RaquetPixels(this.Ptr, _size_out); + if (_bytes == IntPtr.Zero) + { + return null; + } + + byte[] _wkb = new byte[Marshal.ReadInt64(_size_out)]; + Marshal.Copy(_bytes, _wkb, 0, _wkb.Length); + return _wkb; + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + public string Pixtype() => Meos.RaquetPixtype(this.Ptr); @@ -72,11 +127,37 @@ public int Width() public static Raquet? FromHEXWKB(string hexwkb) => MEOSFactory.WrapRaquet(Meos.RaquetFromHexwkb(hexwkb)); + public static Raquet? FromWKB(byte[] wkb) + { + GCHandle _wkb = GCHandle.Alloc(wkb, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapRaquet(Meos.RaquetFromWkb(_wkb.AddrOfPinnedObject(), (ulong) wkb.Length)); + } + finally + { + _wkb.Free(); + } + } + public static Raquet? In(string str) => MEOSFactory.WrapRaquet(Meos.RaquetIn(str)); public static Raquet? Read(string path, ulong quadbin) => MEOSFactory.WrapRaquet(Meos.RaquetRead(path, quadbin)); + public static Raquet? ReadBytes(byte[] data, ulong quadbin) + { + GCHandle _data = GCHandle.Alloc(data, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapRaquet(Meos.RaquetReadBytes(_data.AddrOfPinnedObject(), (ulong) data.Length, quadbin)); + } + finally + { + _data.Free(); + } + } + } } diff --git a/MEOS.NET/Types/STBox.g.cs b/MEOS.NET/Types/STBox.g.cs index 54be205..8342680 100644 --- a/MEOS.NET/Types/STBox.g.cs +++ b/MEOS.NET/Types/STBox.g.cs @@ -21,6 +21,40 @@ public override string ToString() public double Area(bool spheroid) => Meos.StboxArea(this.Ptr, spheroid); + public string AsHEXWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + return Meos.StboxAsHexwkb(this.Ptr, variant, _size_out); + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + + public byte[]? AsWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + IntPtr _bytes = Meos.StboxAsWkb(this.Ptr, variant, _size_out); + if (_bytes == IntPtr.Zero) + { + return null; + } + + byte[] _wkb = new byte[Marshal.ReadInt64(_size_out)]; + Marshal.Copy(_bytes, _wkb, 0, _wkb.Length); + return _wkb; + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + public int Cmp(STBox box2) => Meos.StboxCmp(this.Ptr, box2.Ptr); @@ -353,6 +387,19 @@ public double Volume() public static STBox? FromHEXWKB(string hexwkb) => MEOSFactory.WrapSTBox(Meos.StboxFromHexwkb(hexwkb)); + public static STBox? FromWKB(byte[] wkb) + { + GCHandle _wkb = GCHandle.Alloc(wkb, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapSTBox(Meos.StboxFromWkb(_wkb.AddrOfPinnedObject(), (ulong) wkb.Length)); + } + finally + { + _wkb.Free(); + } + } + public static STBox? GetSpaceTile(Geo point, double xsize, double ysize, double zsize, Geo sorigin) => MEOSFactory.WrapSTBox(Meos.StboxGetSpaceTile(point.Ptr, xsize, ysize, zsize, sorigin.Ptr)); diff --git a/MEOS.NET/Types/Set.g.cs b/MEOS.NET/Types/Set.g.cs index d027a4a..092ac78 100644 --- a/MEOS.NET/Types/Set.g.cs +++ b/MEOS.NET/Types/Set.g.cs @@ -14,6 +14,40 @@ public class Set : Collection { internal Set(IntPtr ptr) : base(ptr) { } + public string AsHEXWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + return Meos.SetAsHexwkb(this.Ptr, variant, _size_out); + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + + public byte[]? AsWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + IntPtr _bytes = Meos.SetAsWkb(this.Ptr, variant, _size_out); + if (_bytes == IntPtr.Zero) + { + return null; + } + + byte[] _wkb = new byte[Marshal.ReadInt64(_size_out)]; + Marshal.Copy(_bytes, _wkb, 0, _wkb.Length); + return _wkb; + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + public int Cmp(Set s2) => Meos.SetCmp(this.Ptr, s2.Ptr); @@ -71,5 +105,18 @@ public int NumValues() public static Set? FromHEXWKB(string hexwkb) => MEOSFactory.WrapSet(Meos.SetFromHexwkb(hexwkb)); + public static Set? FromWKB(byte[] wkb) + { + GCHandle _wkb = GCHandle.Alloc(wkb, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapSet(Meos.SetFromWkb(_wkb.AddrOfPinnedObject(), (ulong) wkb.Length)); + } + finally + { + _wkb.Free(); + } + } + } } diff --git a/MEOS.NET/Types/Span.g.cs b/MEOS.NET/Types/Span.g.cs index b953178..8cd8a6d 100644 --- a/MEOS.NET/Types/Span.g.cs +++ b/MEOS.NET/Types/Span.g.cs @@ -14,6 +14,40 @@ public class Span : Collection { internal Span(IntPtr ptr) : base(ptr) { } + public string AsHEXWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + return Meos.SpanAsHexwkb(this.Ptr, variant, _size_out); + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + + public byte[]? AsWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + IntPtr _bytes = Meos.SpanAsWkb(this.Ptr, variant, _size_out); + if (_bytes == IntPtr.Zero) + { + return null; + } + + byte[] _wkb = new byte[Marshal.ReadInt64(_size_out)]; + Marshal.Copy(_bytes, _wkb, 0, _wkb.Length); + return _wkb; + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + public int Cmp(Span s2) => Meos.SpanCmp(this.Ptr, s2.Ptr); @@ -59,5 +93,18 @@ public bool UpperInc() public static Span? FromHEXWKB(string hexwkb) => MEOSFactory.WrapSpan(Meos.SpanFromHexwkb(hexwkb)); + public static Span? FromWKB(byte[] wkb) + { + GCHandle _wkb = GCHandle.Alloc(wkb, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapSpan(Meos.SpanFromWkb(_wkb.AddrOfPinnedObject(), (ulong) wkb.Length)); + } + finally + { + _wkb.Free(); + } + } + } } diff --git a/MEOS.NET/Types/SpanSet.g.cs b/MEOS.NET/Types/SpanSet.g.cs index 31d1e34..ccb9dd5 100644 --- a/MEOS.NET/Types/SpanSet.g.cs +++ b/MEOS.NET/Types/SpanSet.g.cs @@ -14,6 +14,40 @@ public class SpanSet : Collection { internal SpanSet(IntPtr ptr) : base(ptr) { } + public string AsHEXWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + return Meos.SpansetAsHexwkb(this.Ptr, variant, _size_out); + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + + public byte[]? AsWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + IntPtr _bytes = Meos.SpansetAsWkb(this.Ptr, variant, _size_out); + if (_bytes == IntPtr.Zero) + { + return null; + } + + byte[] _wkb = new byte[Marshal.ReadInt64(_size_out)]; + Marshal.Copy(_bytes, _wkb, 0, _wkb.Length); + return _wkb; + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + public int Cmp(SpanSet ss2) => Meos.SpansetCmp(this.Ptr, ss2.Ptr); @@ -83,5 +117,18 @@ public bool UpperInc() public static SpanSet? FromHEXWKB(string hexwkb) => MEOSFactory.WrapSpanSet(Meos.SpansetFromHexwkb(hexwkb)); + public static SpanSet? FromWKB(byte[] wkb) + { + GCHandle _wkb = GCHandle.Alloc(wkb, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapSpanSet(Meos.SpansetFromWkb(_wkb.AddrOfPinnedObject(), (ulong) wkb.Length)); + } + finally + { + _wkb.Free(); + } + } + } } diff --git a/MEOS.NET/Types/TBox.g.cs b/MEOS.NET/Types/TBox.g.cs index bccb117..47c82ca 100644 --- a/MEOS.NET/Types/TBox.g.cs +++ b/MEOS.NET/Types/TBox.g.cs @@ -18,6 +18,40 @@ internal TBox(IntPtr ptr) : base(ptr) { } public override string ToString() => this.Out(15); + public string AsHEXWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + return Meos.TboxAsHexwkb(this.Ptr, variant, _size_out); + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + + public byte[]? AsWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + IntPtr _bytes = Meos.TboxAsWkb(this.Ptr, variant, _size_out); + if (_bytes == IntPtr.Zero) + { + return null; + } + + byte[] _wkb = new byte[Marshal.ReadInt64(_size_out)]; + Marshal.Copy(_bytes, _wkb, 0, _wkb.Length); + return _wkb; + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + public int Cmp(TBox box2) => Meos.TboxCmp(this.Ptr, box2.Ptr); @@ -250,6 +284,19 @@ public string Out(int maxdd) public static TBox? FromHEXWKB(string hexwkb) => MEOSFactory.WrapTBox(Meos.TboxFromHexwkb(hexwkb)); + public static TBox? FromWKB(byte[] wkb) + { + GCHandle _wkb = GCHandle.Alloc(wkb, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapTBox(Meos.TboxFromWkb(_wkb.AddrOfPinnedObject(), (ulong) wkb.Length)); + } + finally + { + _wkb.Free(); + } + } + public static TBox? In(string str) => MEOSFactory.WrapTBox(Meos.TboxIn(str)); diff --git a/MEOS.NET/Types/Temporal.g.cs b/MEOS.NET/Types/Temporal.g.cs index 957d13d..cb39b4a 100644 --- a/MEOS.NET/Types/Temporal.g.cs +++ b/MEOS.NET/Types/Temporal.g.cs @@ -34,6 +34,19 @@ internal Temporal(IntPtr ptr) : base(ptr) { } public Temporal? AppendTsequence(Temporal seq, bool expand) => MEOSFactory.WrapTemporal(Meos.TemporalAppendTsequence(this.Ptr, seq.Ptr, expand)); + public string AsHEXWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + return Meos.TemporalAsHexwkb(this.Ptr, variant, _size_out); + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + public string AsMFJSON(bool with_bbox, int flags, int precision, string srs) => Meos.TemporalAsMfjson(this.Ptr, with_bbox, flags, precision, srs); @@ -46,6 +59,27 @@ public string AsMFJSON(bool with_bbox, int flags, int precision, string srs) public Temporal? AsTsequenceset(InterpType interp) => MEOSFactory.WrapTemporal(Meos.TemporalAsTsequenceset(this.Ptr, (int) interp)); + public byte[]? AsWKB(byte variant) + { + IntPtr _size_out = Marshal.AllocHGlobal(sizeof(long)); + try + { + IntPtr _bytes = Meos.TemporalAsWkb(this.Ptr, variant, _size_out); + if (_bytes == IntPtr.Zero) + { + return null; + } + + byte[] _wkb = new byte[Marshal.ReadInt64(_size_out)]; + Marshal.Copy(_bytes, _wkb, 0, _wkb.Length); + return _wkb; + } + finally + { + Marshal.FreeHGlobal(_size_out); + } + } + public Temporal? AtMax() => MEOSFactory.WrapTemporal(Meos.TemporalAtMax(this.Ptr)); @@ -418,6 +452,19 @@ public bool UpperInc() public static Temporal? FromHEXWKB(string hexwkb) => MEOSFactory.WrapTemporal(Meos.TemporalFromHexwkb(hexwkb)); + public static Temporal? FromWKB(byte[] wkb) + { + GCHandle _wkb = GCHandle.Alloc(wkb, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapTemporal(Meos.TemporalFromWkb(_wkb.AddrOfPinnedObject(), (ulong) wkb.Length)); + } + finally + { + _wkb.Free(); + } + } + public static Temporal? MergeArray(Temporal[] temparr) { IntPtr[] _temparrValues = new IntPtr[temparr.Length]; diff --git a/MEOS.NET/Types/TsTzSet.g.cs b/MEOS.NET/Types/TsTzSet.g.cs index 313c633..2f295e1 100644 --- a/MEOS.NET/Types/TsTzSet.g.cs +++ b/MEOS.NET/Types/TsTzSet.g.cs @@ -88,5 +88,18 @@ public long[] Values() public static Set? In(string str) => MEOSFactory.WrapSet(Meos.TstzsetIn(str)); + public static Set? Make(long[] values) + { + GCHandle _values = GCHandle.Alloc(values, GCHandleType.Pinned); + try + { + return MEOSFactory.WrapSet(Meos.TstzsetMake(_values.AddrOfPinnedObject(), values.Length)); + } + finally + { + _values.Free(); + } + } + } } diff --git a/tools/objectgen.py b/tools/objectgen.py index dca65e8..128a756 100644 --- a/tools/objectgen.py +++ b/tools/objectgen.py @@ -263,7 +263,9 @@ 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, - structs: list[tuple[str, 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): self.name = name self.ret = ret self.params = params @@ -275,6 +277,12 @@ def __init__(self, name: str, ret: str, params: list[tuple[str, str]], self.out_param = out_param # (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. + self.scalar_arrays = scalar_arrays or [] + # The out-parameter MEOS states the answered buffer's length in, and + # whether that buffer is the answer itself. + self.length_out = length_out + self.byte_buffer = byte_buffer class Generator: @@ -401,8 +409,13 @@ def method_for(self, cls: str, entry: dict) -> Method | None: nxt = declared[i + 1] if i + 1 < len(declared) else None if (i == 0 and receiver_first) or not clean(p["cType"]).endswith("*"): continue - if (nxt and nxt["name"] == "count" - and clean(nxt["cType"]) in ("int", "int32", "int32_t")): + # The length is named `count` beside an array of values and `size` + # beside a byte buffer, and both say the same thing: the pointer is + # the first element of an array the caller already holds whole. + if (nxt and ((nxt["name"] == "count" + and clean(nxt["cType"]) in ("int", "int32", "int32_t")) + or (nxt["name"] == "size" + and clean(nxt["cType"]) == "size_t"))): counted.add(codegen.csharp_param_name(p["name"])) params = list(wrapper_params) @@ -413,9 +426,20 @@ def method_for(self, cls: str, entry: dict) -> Method | None: args.append("this.Ptr") params = params[1:] + # A `size_t *` out-parameter states the LENGTH of the buffer the call + # answers. It leaves the C# signature — a byte array and a string each + # carry their own length — and the buffer MEOS writes it into is + # allocated for the call. + out_params = (f.get("shape") or {}).get("outParams") or [] + length_out = next( + (codegen.csharp_param_name(p["name"]) for p in f.get("params", []) + if p["name"] in out_params and clean(p["cType"]) == "size_t *"), None) + if length_out: + 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. - out_params = (f.get("shape") or {}).get("outParams") or [] result_out = None if clean(f["returnType"]["c"]) == "bool" and len(out_params) == 1: pointee = clean(next( @@ -430,7 +454,10 @@ def method_for(self, cls: str, entry: dict) -> Method | None: f"{pointee}, which has no reader") return None - ret = self.map_return(f, wrapper_ret) + if length_out and clean(f["returnType"]["c"]) == "uint8_t *": + ret = ("byte[]?", "$") + else: + ret = self.map_return(f, wrapper_ret) if ret is None: self.deferred[cls].append( f"{oo}: return {clean(f['returnType']['c'])} needs wrapping") @@ -456,25 +483,39 @@ def method_for(self, cls: str, entry: dict) -> Method | None: 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]: args.append(f"_{pname}") continue + if pname == length_out: + args.append(f"_{pname}") + continue if pname in count_of: - args.append(f"{count_of[pname]}.Length") + cast = "" if cs_type == "int" else f"({cs_type}) " + args.append(f"{cast}{count_of[pname]}.Length") continue if pname in counted: + pointee = clean(c_by_name[pname])[:-1].strip() element = self.m.class_for_ctype(clean(c_by_name[pname])[:-1]) - if element is None: - self.deferred[cls].append( - f"{oo}: argument {pname} is an array of " - f"{clean(c_by_name[pname])[:-1]}, which has no class") - return None - sig.append((f"{element}[]", pname)) - arrays.append((pname, element)) - args.append(f"_{pname}.AddrOfPinnedObject()") - continue + if element is not None: + sig.append((f"{element}[]", pname)) + arrays.append((pname, element)) + args.append(f"_{pname}.AddrOfPinnedObject()") + continue + # A scalar element is the array itself: the values need no + # gathering, so what is pinned is the caller's own array. + scalar = codegen.SCALAR_MAP.get(pointee) + if scalar is not None: + sig.append((f"{scalar}[]", pname)) + scalar_arrays.append(pname) + args.append(f"_{pname}.AddrOfPinnedObject()") + continue + self.deferred[cls].append( + f"{oo}: argument {pname} is an array of " + f"{pointee}, which has no class") + return None mapped = self.map_param(c_by_name.get(pname, ""), cs_type, pname) if mapped is None: self.deferred[cls].append( @@ -491,13 +532,19 @@ def method_for(self, cls: str, entry: dict) -> Method | None: if result_out is not None: name, (_, size, reader) = result_out out = (name, size, reader.format(f"_{name}")) - if structs and (arrays or out): + if structs and (arrays or out or scalar_arrays or length_out): self.deferred[cls].append( f"{oo}: a struct argument beside a counted array or an " "out-parameter needs one body doing both") return None + if length_out and (arrays or out): + self.deferred[cls].append( + f"{oo}: a length out-parameter beside a counted array or a " + "value out-parameter needs one body doing both") + return None return Method(pascal(oo), ret_type, sig, ret_expr.replace("$", call), static, - arrays, out, structs) + arrays, out, structs, scalar_arrays, length_out, + ret_type == "byte[]?") def inherited_names(self, cls: str) -> set[tuple]: names: set[tuple] = set() @@ -548,7 +595,9 @@ def emit_class(self, cls: str) -> str: lines.append(f" public {new}{kind}{m.ret} {m.name}({args})") if m.out_param: lines.extend(self.out_param_body(m)) - elif m.arrays: + elif m.length_out: + lines.extend(self.buffer_body(m)) + elif m.arrays or m.scalar_arrays: lines.extend(self.array_body(m)) elif m.structs: lines.extend(self.struct_body(m)) @@ -610,6 +659,44 @@ def struct_body(self, method: Method) -> list[str]: lines += [" }", " }"] return lines + def buffer_body(self, method: Method) -> list[str]: + """The body of a method whose call states a length through a pointer. + + MEOS writes the length of what it answers into a buffer the caller + supplies. A string carries its own length, so there the buffer is + written and dropped; a byte array does not, so there it says how much of + what MEOS answered to copy.""" + name = method.length_out + lines = [ + " {", + f" IntPtr _{name} = Marshal.AllocHGlobal(sizeof(long));", + " try", + " {", + ] + if method.byte_buffer: + lines += [ + f" IntPtr _bytes = {method.body};", + " if (_bytes == IntPtr.Zero)", + " {", + " return null;", + " }", + "", + f" byte[] _wkb = new byte[Marshal.ReadInt64(_{name})];", + " Marshal.Copy(_bytes, _wkb, 0, _wkb.Length);", + " return _wkb;", + ] + else: + lines.append(f" return {method.body};") + lines += [ + " }", + " finally", + " {", + f" Marshal.FreeHGlobal(_{name});", + " }", + " }", + ] + return lines + def array_body(self, method: Method) -> list[str]: """The body of a method taking a counted array. @@ -618,6 +705,10 @@ def array_body(self, method: Method) -> list[str]: for the call. MEOS copies what it keeps, so the pin lasts exactly as long as the call does.""" lines = [" {"] + for name in method.scalar_arrays: + lines.append( + f" GCHandle _{name} = " + f"GCHandle.Alloc({ident(name)}, GCHandleType.Pinned);") for name, element in method.arrays: lines += [ f" IntPtr[] _{name}Values = new IntPtr[{ident(name)}.Length];", @@ -634,6 +725,8 @@ def array_body(self, method: Method) -> list[str]: lines.append(" }") lines.append(" finally") lines.append(" {") + for name in method.scalar_arrays: + lines.append(f" _{name}.Free();") for name, _ in method.arrays: lines.append(f" _{name}.Free();") lines += [" }", " }"]