aboutsummaryrefslogtreecommitdiff
path: root/ext/misc/randomjson.c
diff options
context:
space:
mode:
authordrh <>2023-04-28 17:38:35 +0000
committerdrh <>2023-04-28 17:38:35 +0000
commit8e7efe37645453fac5ca98f8a9837f34d14f75b6 (patch)
tree7f22fbdf69f6dcf485b46bdf5062f0d53a938b68 /ext/misc/randomjson.c
parent272ae627c535b1ee11b82ba045c641945e34bdc5 (diff)
downloadsqlite-8e7efe37645453fac5ca98f8a9837f34d14f75b6.tar.gz
sqlite-8e7efe37645453fac5ca98f8a9837f34d14f75b6.zip
Improvements to the accuracy of json_error(). Add the extension SQL
functions random_json(SEED) and random_json5(SEED). FossilOrigin-Name: 8d09dc1c45a8026b94f70273d064e47939f30cadedc17548b5a26ba054a8d3a7
Diffstat (limited to 'ext/misc/randomjson.c')
-rw-r--r--ext/misc/randomjson.c202
1 files changed, 202 insertions, 0 deletions
diff --git a/ext/misc/randomjson.c b/ext/misc/randomjson.c
new file mode 100644
index 000000000..3a6f545fe
--- /dev/null
+++ b/ext/misc/randomjson.c
@@ -0,0 +1,202 @@
+/*
+** 2023-04-28
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This SQLite extension implements a the random_json(SEED) and
+** random_json5(SEED) functions. Given a numeric SEED value, these
+** routines generate pseudo-random JSON or JSON5, respectively. The
+** same value is always generated for the same seed.
+**
+** These SQL functions are intended for testing. They do not have any
+** practical real-world use, that we know of.
+**
+** COMPILE:
+**
+** gcc --shared -fPIC -o randomjson.so -I. ext/misc/randomjson.c
+**
+** USING FROM THE CLI:
+**
+** .load ./randomjson
+** SELECT random_json(1);
+*/
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT1
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* Pseudo-random number generator */
+typedef struct Prng {
+ unsigned int x, y;
+} Prng;
+
+/* Reseed the PRNG */
+static void prngSeed(Prng *p, unsigned int iSeed){
+ p->x = iSeed | 1;
+ p->y = iSeed;
+}
+
+/* Extract a random number */
+static unsigned int prngInt(Prng *p){
+ p->x = (p->x>>1) ^ ((1+~(p->x&1)) & 0xd0000001);
+ p->y = p->y*1103515245 + 12345;
+ return p->x ^ p->y;
+}
+
+static const char *azJsonAtoms[] = {
+ /* JSON /* JSON-5 */
+ "0", "0",
+ "1", "1",
+ "-1", "-1",
+ "2", "+2",
+ "3", "3",
+ "2.5", "2.5",
+ "0.75", ".75",
+ "-4.0e2", "-4.e2",
+ "5.0e-3", "+5e-3",
+ "0", "0x0",
+ "512", "0x200",
+ "256", "+0x100",
+ "-2748", "-0xabc",
+ "true", "true",
+ "false", "false",
+ "null", "null",
+ "9.0e999", "Infinity",
+ "-9.0e999", "-Infinity",
+ "9.0e999", "+Infinity",
+ "null", "NaN",
+ "-0.0005123", "-0.0005123",
+ "4.35e-3", "+4.35e-3",
+ "\"gem\\\"hay\"", "\"gem\\\"hay\"",
+ "\"icy'joy\"", "'icy\\'joy\'",
+ "\"keylog\"", "\"key\\\nlog\"",
+ "\"mix\\\\\\tnet\"", "\"mix\\\\\\tnet\"",
+ "{}", "{}",
+ "[]", "[]",
+ "[]", "[/*empty*/]",
+ "{}", "{//empty\n}",
+ "\"ask\"", "\"ask\"",
+ "\"bag\"", "\"bag\"",
+ "\"can\"", "\"can\"",
+ "\"day\"", "\"day\"",
+ "\"end\"", "'end'",
+ "\"fly\"", "\"fly\"",
+ "\"\"", "\"\"",
+};
+static const char *azJsonTemplate[] = {
+ /* JSON JSON-5 */
+ "{\"a\":%,\"b\":%,\"c\":%}", "{a:%,b:%,c:%}",
+ "{\"a\":%,\"b\":%,\"c\":%,\"d\":%,\"e\":%}", "{a:%,b:%,c:%,d:%,e:%}",
+ "{\"a\":%,\"b\":%,\"c\":%,\"d\":%,\"\":%}", "{a:%,b:%,c:%,d:%,\"\":%}",
+ "{\"d\":%}", "{d:%}",
+ "{\"eeee\":%, \"ffff\":%}", "{eeee:% /*and*/, ffff:%}",
+ "{\"$g\":%,\"_h_\":%}", "{$g:%,_h_:%,}",
+ "{\"x\":%,\n \"y\":%}", "{\"x\":%,\n \"y\":%}",
+ "{\"a b c d\":%,\"e\":%,\"f\":%,\"x\":%,\"y\":%}",
+ "{\"a b c d\":%,e:%,f:%,x:%,y:%}",
+ "{\"Z\":%}", "{Z:%,}",
+ "[%]", "[%,]",
+ "[%,%]", "[%,%]",
+ "[%,%,%]", "[%,%,%,]",
+ "[%,%,%,%]", "[%,%,%,%]",
+ "[%,%,%,%,%]", "[%,%,%,%,%]",
+};
+
+#define count(X) (sizeof(X)/sizeof(X[0]))
+
+#define STRSZ 10000
+
+static void jsonExpand(
+ const char *zSrc,
+ char *zDest,
+ Prng *p,
+ int eType, /* 0 for JSON, 1 for JSON5 */
+ unsigned int r /* Growth probability 0..1000. 0 means no growth */
+){
+ unsigned int i, j, k;
+ const char *z;
+ size_t n;
+
+ j = 0;
+ if( zSrc==0 ){
+ k = prngInt(p)%(count(azJsonTemplate)/2);
+ k = k*2 + eType;
+ zSrc = azJsonTemplate[k];
+ }
+ if( strlen(zSrc)>=STRSZ/10 ) r = 0;
+ for(i=0; zSrc[i]; i++){
+ if( zSrc[i]!='%' ){
+ if( j<STRSZ ) zDest[j++] = zSrc[i];
+ continue;
+ }
+ if( r==0 || (r<1000 && (prngInt(p)%1000)<=r) ){
+ /* Fill in without values without any new % */
+ k = prngInt(p)%(count(azJsonAtoms)/2);
+ k = k*2 + eType;
+ z = azJsonAtoms[k];
+ }else{
+ /* Add new % terms */
+ k = prngInt(p)%(count(azJsonTemplate)/2);
+ k = k*2 + eType;
+ z = azJsonTemplate[k];
+ }
+ n = strlen(z);
+ if( j+n<STRSZ ){
+ memcpy(&zDest[j], z, n);
+ j += n;
+ }
+ }
+ zDest[STRSZ-1] = 0;
+ if( j<STRSZ ) zDest[j] = 0;
+}
+
+static void randJsonFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ unsigned int iSeed;
+ int eType = *(int*)sqlite3_user_data(context);
+ Prng prng;
+ char z1[STRSZ+1], z2[STRSZ+1];
+
+ iSeed = (unsigned int)sqlite3_value_int(argv[0]);
+ prngSeed(&prng, iSeed);
+ jsonExpand(0, z2, &prng, eType, 1000);
+ jsonExpand(z2, z1, &prng, eType, 1000);
+ jsonExpand(z1, z2, &prng, eType, 100);
+ jsonExpand(z2, z1, &prng, eType, 0);
+ sqlite3_result_text(context, z1, -1, SQLITE_TRANSIENT);
+}
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_randomjson_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ static int cOne = 1;
+ static int cZero = 0;
+ int rc = SQLITE_OK;
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErrMsg; /* Unused parameter */
+ rc = sqlite3_create_function(db, "random_json", 1,
+ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
+ &cZero, randJsonFunc, 0, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "random_json5", 1,
+ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
+ &cOne, randJsonFunc, 0, 0);
+ }
+ return rc;
+}