From d536c870529da221abd786f128391e360b115c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20Zim=C3=A1nyi?= Date: Thu, 3 Sep 2026 14:38:29 +0200 Subject: [PATCH] Take the array the each family fills rather than making one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `jsonb_each` and its seven siblings fill an array the CALLER allocates: MEOS writes one `Jsonb *` per member of the object into the storage the parameter points at, and states how many it wrote through the count. The catalog now says so, and the wrappers take that array — `JsonbEach(IntPtr jb, IntPtr values)` answering the keys — instead of allocating one pointer's worth of storage, handing MEOS the address of it, and reading the first pointer MEOS wrote as the address of an array. For an object of more than one member the old shape wrote past its allocation and then walked whatever the first written pointer aimed at. The object layer defers the four methods over that shape. Nothing in a class's signature says how many members the value it is called on has, so a method answering `each` cannot size the array the call needs; the flat wrapper, whose caller holds the value, is where the size is known. The layer emits 1349 methods over 115 classes with 11 deferred. Its own test allocates room for eight, reads the two keys and the two values MEOS wrote, and so states the contract the wrapper now keeps. --- MEOS.NET.Tests/CallerAllocatedArrayTests.cs | 39 ++++++++ MEOS.NET/Functions/Meos.meos_json.g.cs | 100 +++++++------------- MEOS.NET/Functions/Meos.pgtypes.g.cs | 100 +++++++------------- MEOS.NET/Types/Jsonb.g.cs | 14 --- 4 files changed, 103 insertions(+), 150 deletions(-) create mode 100644 MEOS.NET.Tests/CallerAllocatedArrayTests.cs 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/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/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);