From 102a46d71ef63f916c240dbd02f849ea20b734ab Mon Sep 17 00:00:00 2001 From: Garth Gillespie Date: Wed, 16 Nov 2022 16:36:18 -0800 Subject: [PATCH] added sqlite3_serialize() and sqlite3_deserialize() (src) --- src/SQLitePCLRaw.core/isqlite3.cs | 5 ++ src/SQLitePCLRaw.core/raw.cs | 127 ++++++++++++++++++++++++++++++ src/providers/provider.tt | 55 +++++++++++++ 3 files changed, 187 insertions(+) diff --git a/src/SQLitePCLRaw.core/isqlite3.cs b/src/SQLitePCLRaw.core/isqlite3.cs index 55b0594d..7ec0d25c 100644 --- a/src/SQLitePCLRaw.core/isqlite3.cs +++ b/src/SQLitePCLRaw.core/isqlite3.cs @@ -246,6 +246,8 @@ public interface ISQLite3Provider int sqlite3_stricmp(IntPtr p, IntPtr q); int sqlite3_strnicmp(IntPtr p, IntPtr q, int n); + IntPtr sqlite3_malloc(int n); + IntPtr sqlite3_malloc64(ulong n); void sqlite3_free(IntPtr p); int sqlite3_key(sqlite3 db, ReadOnlySpan key); @@ -276,6 +278,9 @@ public interface ISQLite3Provider int sqlite3_keyword_count(); int sqlite3_keyword_name(int i, out string name); + + IntPtr sqlite3_serialize(sqlite3 db, utf8z schema, out long size, uint flags); + int sqlite3_deserialize(sqlite3 db, utf8z schema, IntPtr data, long szDb, long szBuf, uint flags); } } diff --git a/src/SQLitePCLRaw.core/raw.cs b/src/SQLitePCLRaw.core/raw.cs index ca5d2278..fd54181d 100644 --- a/src/SQLitePCLRaw.core/raw.cs +++ b/src/SQLitePCLRaw.core/raw.cs @@ -339,6 +339,12 @@ static public string GetNativeLibraryName() public const int SQLITE_TRACE_ROW = 0x04; public const int SQLITE_TRACE_CLOSE = 0x08; + public const int SQLITE_SERIALIZE_NOCOPY = 0x001; /* Do no memory allocations */ + + public const int SQLITE_DESERIALIZE_FREEONCLOSE = 1; /* Call sqlite3_free() on close */ + public const int SQLITE_DESERIALIZE_RESIZEABLE = 2; /* Resize using sqlite3_realloc64() */ + public const int SQLITE_DESERIALIZE_READONLY = 4; /* Database is read-only */ + static public int sqlite3_open(utf8z filename, out sqlite3 db) { int rc = Provider.sqlite3_open(filename, out var p_db); @@ -1494,5 +1500,126 @@ static public int sqlite3_keyword_name(int i, out string name) { return Provider.sqlite3_keyword_name(i, out name); } + + static public byte[] sqlite3_serialize(sqlite3 db, utf8z schema) + { + var p = Provider.sqlite3_serialize(db, schema, out var size, 0); + if (p == IntPtr.Zero) + { + return null; + } + + try + { + if (size > int.MaxValue) + { + throw new OverflowException(); + } + unsafe + { + return new ReadOnlySpan(p.ToPointer(), (int)size).ToArray(); + } + } + finally + { + Provider.sqlite3_free(p); + } + } + + static public byte[] sqlite3_serialize(sqlite3 db, string schema) + { + return sqlite3_serialize(db, schema.to_utf8z()); + } + + static public long sqlite3_serialize(sqlite3 db, utf8z schema, System.IO.Stream stream) + { + var p = Provider.sqlite3_serialize(db, schema, out var size, 0); + if (p == IntPtr.Zero) + { + return -1; + } + + try + { + unsafe + { + new System.IO.UnmanagedMemoryStream((byte*)p, size).CopyTo(stream); + } + return size; + } + finally + { + Provider.sqlite3_free(p); + } + } + + static public long sqlite3_serialize(sqlite3 db, string schema, System.IO.Stream stream) + { + return sqlite3_serialize(db, schema.to_utf8z(), stream); + } + + static public int sqlite3_deserialize(sqlite3 db, utf8z schema, ReadOnlySpan data) + { + var size = data.Length; + + var p = Provider.sqlite3_malloc(size); + if (p == IntPtr.Zero) + { + return SQLITE_NOMEM; + } + + try + { + unsafe + { + data.CopyTo(new Span(p.ToPointer(), size)); + } + } + catch + { + Provider.sqlite3_free(p); + throw; + } + + uint flags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE; + return Provider.sqlite3_deserialize(db, schema, p, size, size, flags); + } + + static public int sqlite3_deserialize(sqlite3 db, string schema, ReadOnlySpan data) + { + return sqlite3_deserialize(db, schema.to_utf8z(), data); + } + + static public int sqlite3_deserialize(sqlite3 db, utf8z schema, System.IO.Stream stream) + { + var size = stream.Length; + + var p = Provider.sqlite3_malloc64((ulong)size); + if (p == IntPtr.Zero) + { + return SQLITE_NOMEM; + } + + try + { + unsafe + { + stream.CopyTo(new System.IO.UnmanagedMemoryStream((byte*)p, size, size, System.IO.FileAccess.Write)); + } + } + catch + { + Provider.sqlite3_free(p); + throw; + } + + uint flags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE; + return Provider.sqlite3_deserialize(db, schema, p, size, size, flags); + } + + static public int sqlite3_deserialize(sqlite3 db, string schema, System.IO.Stream stream) + { + return sqlite3_deserialize(db, schema.to_utf8z(), stream); + } } } diff --git a/src/providers/provider.tt b/src/providers/provider.tt index f22fbaf1..249cd5a7 100644 --- a/src/providers/provider.tt +++ b/src/providers/provider.tt @@ -205,6 +205,16 @@ namespace SQLitePCL return rc; } + IntPtr ISQLite3Provider.sqlite3_malloc(int n) + { + return NativeMethods.sqlite3_malloc(n); + } + + IntPtr ISQLite3Provider.sqlite3_malloc64(ulong n) + { + return NativeMethods.sqlite3_malloc64(n); + } + void ISQLite3Provider.sqlite3_free(IntPtr p) { NativeMethods.sqlite3_free(p); @@ -1788,6 +1798,22 @@ namespace SQLitePCL return rc; } + unsafe IntPtr ISQLite3Provider.sqlite3_serialize(sqlite3 db, utf8z schema, out long size, uint flags) + { + fixed (byte* p_schema = schema) + { + return NativeMethods.sqlite3_serialize(db, p_schema, out size, flags); + } + } + + unsafe int ISQLite3Provider.sqlite3_deserialize(sqlite3 db, utf8z schema, IntPtr data, long szDb, long szBuf, uint flags) + { + fixed (byte* p_schema = schema) + { + return NativeMethods.sqlite3_deserialize(db, p_schema, data, szDb, szBuf, flags); + } + } + static class NativeMethods { <# @@ -2181,6 +2207,13 @@ namespace SQLitePCL new Parm("int", "n"), } ), + new Function( + "IntPtr", + "sqlite3_malloc64", + new Parm[] { + new Parm("ulong", "n"), + } + ), new Function( "IntPtr", "sqlite3_realloc", @@ -3151,6 +3184,28 @@ namespace SQLitePCL new Parm("int", "length").SetIsOut(true), } ), + new Function( + "IntPtr", + "sqlite3_serialize", + new Parm[] { + new Parm("sqlite3", "db"), + new Parm("byte*", "schema"), + new Parm("long", "size").SetIsOut(true), + new Parm("uint", "flags"), + } + ), + new Function( + "int", + "sqlite3_deserialize", + new Parm[] { + new Parm("sqlite3", "db"), + new Parm("byte*", "schema"), + new Parm("IntPtr", "data"), + new Parm("long", "szDb"), + new Parm("long", "szBuf"), + new Parm("uint", "flags"), + } + ), };