4449a1b66d
FossilOrigin-Name: debe7060b16669ada7304ffb9bf7616c8fa30bd286d8be871ed17fd6d64a3d4c
241 lines
7.3 KiB
C
241 lines
7.3 KiB
C
/*
|
|
** 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;
|
|
}
|