| /* |
| ** 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); |
| ** SELECT random_json5(1); |
| */ |
| #ifdef SQLITE_STATIC_RANDOMJSON |
| # include "sqlite3.h" |
| #else |
| # include "sqlite3ext.h" |
| SQLITE_EXTENSION_INIT1 |
| #endif |
| #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 char *azJsonAtoms[] = { |
| /* JSON JSON-5 */ |
| "0", "0", |
| "1", "1", |
| "-1", "-1", |
| "2", "+2", |
| "3DDDD", "3DDDD", |
| "2.5DD", "2.5DD", |
| "0.75", ".75", |
| "-4.0e2", "-4.e2", |
| "5.0e-3", "+5e-3", |
| "6.DDe+0DD", "6.DDe+0DD", |
| "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.0005DD", "-0.0005DD", |
| "4.35e-3", "+4.35e-3", |
| "\"gem\\\"hay\"", "\"gem\\\"hay\"", |
| "\"icy'joy\"", "'icy\\'joy\'", |
| "\"keylog\"", "\"key\\\nlog\"", |
| "\"mix\\\\\\tnet\"", "\"mix\\\\\\tnet\"", |
| "\"oat\\r\\n\"", "\"oat\\r\\n\"", |
| "\"\\fpan\\b\"", "\"\\fpan\\b\"", |
| "{}", "{}", |
| "[]", "[]", |
| "[]", "[/*empty*/]", |
| "{}", "{//empty\n}", |
| "\"ask\"", "\"ask\"", |
| "\"bag\"", "\"bag\"", |
| "\"can\"", "\"can\"", |
| "\"day\"", "\"day\"", |
| "\"end\"", "'end'", |
| "\"fly\"", "\"fly\"", |
| "\"\\u00XX\\u00XX\"", "\"\\xXX\\xXX\"", |
| "\"y\\uXXXXz\"", "\"y\\uXXXXz\"", |
| "\"\"", "\"\"", |
| }; |
| static char *azJsonTemplate[] = { |
| /* JSON JSON-5 */ |
| "{\"a\":%,\"b\":%,\"cDD\":%}", "{a:%,b:%,cDD:%}", |
| "{\"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_\":%,\"a b c d\":%}", "{$g:%,_h_:%,\"a b c d\":%}", |
| "{\"x\":%,\n \"y\":%}", "{\"x\":%,\n \"y\":%}", |
| "{\"\\u00XX\":%,\"\\uXXXX\":%}", "{\"\\xXX\":%,\"\\uXXXX\":%}", |
| "{\"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; |
| char *z; |
| char *zX; |
| size_t n; |
| char zBuf[200]; |
| |
| j = 0; |
| if( zSrc==0 ) zSrc = "%"; |
| 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( (zX = strstr(z,"XX"))!=0 ){ |
| unsigned int y = prngInt(p); |
| if( (y&0xff)==((y>>8)&0xff) ) y += 0x100; |
| while( (y&0xff)==((y>>16)&0xff) || ((y>>8)&0xff)==((y>>16)&0xff) ){ |
| y += 0x10000; |
| } |
| memcpy(zBuf, z, n+1); |
| z = zBuf; |
| zX = strstr(z,"XX"); |
| while( zX!=0 ){ |
| zX[0] = "0123456789abcdef"[y%16]; y /= 16; |
| zX[1] = "0123456789abcdef"[y%16]; y /= 16; |
| zX = strstr(zX, "XX"); |
| } |
| }else if( (zX = strstr(z,"DD"))!=0 ){ |
| unsigned int y = prngInt(p); |
| memcpy(zBuf, z, n+1); |
| z = zBuf; |
| zX = strstr(z,"DD"); |
| while( zX!=0 ){ |
| zX[0] = "0123456789"[y%10]; y /= 10; |
| zX[1] = "0123456789"[y%10]; y /= 10; |
| zX = strstr(zX, "DD"); |
| } |
| } |
| assert( strstr(z, "XX")==0 ); |
| assert( strstr(z, "DD")==0 ); |
| if( j+n<STRSZ ){ |
| memcpy(&zDest[j], z, n); |
| j += (int)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; |
| #ifdef SQLITE_STATIC_RANDOMJSON |
| (void)pApi; /* Unused parameter */ |
| #else |
| SQLITE_EXTENSION_INIT2(pApi); |
| #endif |
| (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; |
| } |