drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 1 | /* |
| 2 | ** 2007 April 6 |
| 3 | ** |
| 4 | ** The author disclaims copyright to this source code. In place of |
| 5 | ** a legal notice, here is a blessing: |
| 6 | ** |
| 7 | ** May you do good and not evil. |
| 8 | ** May you find forgiveness for yourself and forgive others. |
| 9 | ** May you share freely, never taking more than you give. |
| 10 | ** |
| 11 | ************************************************************************* |
| 12 | ** Code for testing all sorts of SQLite interfaces. This code |
| 13 | ** implements TCL commands for reading and writing the binary |
| 14 | ** database files and displaying the content of those files as |
| 15 | ** hexadecimal. We could, in theory, use the built-in "binary" |
| 16 | ** command of TCL to do a lot of this, but there are some issues |
| 17 | ** with historical versions of the "binary" command. So it seems |
| 18 | ** easier and safer to build our own mechanism. |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 19 | */ |
drh | 53c1402 | 2007-05-10 17:23:11 | [diff] [blame] | 20 | #include "sqliteInt.h" |
drh | 064b681 | 2024-07-30 15:49:02 | [diff] [blame] | 21 | #include "tclsqlite.h" |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 22 | #include <stdlib.h> |
| 23 | #include <string.h> |
| 24 | #include <assert.h> |
| 25 | |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 26 | |
| 27 | /* |
| 28 | ** Convert binary to hex. The input zBuf[] contains N bytes of |
| 29 | ** binary data. zBuf[] is 2*n+1 bytes long. Overwrite zBuf[] |
| 30 | ** with a hexadecimal representation of its original binary input. |
| 31 | */ |
drh | 9c7a60d | 2007-10-19 17:47:24 | [diff] [blame] | 32 | void sqlite3TestBinToHex(unsigned char *zBuf, int N){ |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 33 | const unsigned char zHex[] = "0123456789ABCDEF"; |
| 34 | int i, j; |
| 35 | unsigned char c; |
| 36 | i = N*2; |
| 37 | zBuf[i--] = 0; |
| 38 | for(j=N-1; j>=0; j--){ |
| 39 | c = zBuf[j]; |
| 40 | zBuf[i--] = zHex[c&0xf]; |
| 41 | zBuf[i--] = zHex[c>>4]; |
| 42 | } |
| 43 | assert( i==-1 ); |
| 44 | } |
| 45 | |
| 46 | /* |
| 47 | ** Convert hex to binary. The input zIn[] contains N bytes of |
| 48 | ** hexadecimal. Convert this into binary and write aOut[] with |
| 49 | ** the binary data. Spaces in the original input are ignored. |
| 50 | ** Return the number of bytes of binary rendered. |
| 51 | */ |
drh | 9c7a60d | 2007-10-19 17:47:24 | [diff] [blame] | 52 | int sqlite3TestHexToBin(const unsigned char *zIn, int N, unsigned char *aOut){ |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 53 | const unsigned char aMap[] = { |
| 54 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 55 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 56 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 57 | 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 0, 0, 0, 0, 0, 0, |
| 58 | 0,11,12,13,14,15,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 59 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 60 | 0,11,12,13,14,15,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 61 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 62 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 63 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 64 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 65 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 66 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 67 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 68 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 69 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 70 | }; |
| 71 | int i, j; |
| 72 | int hi=1; |
| 73 | unsigned char c; |
| 74 | |
| 75 | for(i=j=0; i<N; i++){ |
| 76 | c = aMap[zIn[i]]; |
| 77 | if( c==0 ) continue; |
| 78 | if( hi ){ |
| 79 | aOut[j] = (c-1)<<4; |
| 80 | hi = 0; |
| 81 | }else{ |
| 82 | aOut[j++] |= c-1; |
| 83 | hi = 1; |
| 84 | } |
| 85 | } |
| 86 | return j; |
| 87 | } |
| 88 | |
| 89 | |
| 90 | /* |
| 91 | ** Usage: hexio_read FILENAME OFFSET AMT |
| 92 | ** |
| 93 | ** Read AMT bytes from file FILENAME beginning at OFFSET from the |
| 94 | ** beginning of the file. Convert that information to hexadecimal |
| 95 | ** and return the resulting HEX string. |
| 96 | */ |
mistachkin | 7617e4a | 2016-07-28 17:11:20 | [diff] [blame] | 97 | static int SQLITE_TCLAPI hexio_read( |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 98 | void * clientData, |
| 99 | Tcl_Interp *interp, |
| 100 | int objc, |
| 101 | Tcl_Obj *CONST objv[] |
| 102 | ){ |
| 103 | int offset; |
| 104 | int amt, got; |
| 105 | const char *zFile; |
| 106 | unsigned char *zBuf; |
| 107 | FILE *in; |
| 108 | |
| 109 | if( objc!=4 ){ |
| 110 | Tcl_WrongNumArgs(interp, 1, objv, "FILENAME OFFSET AMT"); |
| 111 | return TCL_ERROR; |
| 112 | } |
| 113 | if( Tcl_GetIntFromObj(interp, objv[2], &offset) ) return TCL_ERROR; |
| 114 | if( Tcl_GetIntFromObj(interp, objv[3], &amt) ) return TCL_ERROR; |
| 115 | zFile = Tcl_GetString(objv[1]); |
drh | 1743575 | 2007-08-16 04:30:38 | [diff] [blame] | 116 | zBuf = sqlite3_malloc( amt*2+1 ); |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 117 | if( zBuf==0 ){ |
| 118 | return TCL_ERROR; |
| 119 | } |
drh | 498b876 | 2008-05-12 16:17:42 | [diff] [blame] | 120 | in = fopen(zFile, "rb"); |
| 121 | if( in==0 ){ |
| 122 | in = fopen(zFile, "r"); |
| 123 | } |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 124 | if( in==0 ){ |
stephan | b6503f1 | 2025-03-06 13:38:07 | [diff] [blame] | 125 | Tcl_AppendResult(interp, "cannot open input file ", zFile, NULL); |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 126 | return TCL_ERROR; |
| 127 | } |
| 128 | fseek(in, offset, SEEK_SET); |
drh | 83cc139 | 2012-04-19 18:04:28 | [diff] [blame] | 129 | got = (int)fread(zBuf, 1, amt, in); |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 130 | fclose(in); |
| 131 | if( got<0 ){ |
| 132 | got = 0; |
| 133 | } |
drh | 9c7a60d | 2007-10-19 17:47:24 | [diff] [blame] | 134 | sqlite3TestBinToHex(zBuf, got); |
stephan | b6503f1 | 2025-03-06 13:38:07 | [diff] [blame] | 135 | Tcl_AppendResult(interp, zBuf, NULL); |
drh | 1743575 | 2007-08-16 04:30:38 | [diff] [blame] | 136 | sqlite3_free(zBuf); |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 137 | return TCL_OK; |
| 138 | } |
| 139 | |
| 140 | |
| 141 | /* |
| 142 | ** Usage: hexio_write FILENAME OFFSET DATA |
| 143 | ** |
| 144 | ** Write DATA into file FILENAME beginning at OFFSET from the |
| 145 | ** beginning of the file. DATA is expressed in hexadecimal. |
| 146 | */ |
mistachkin | 7617e4a | 2016-07-28 17:11:20 | [diff] [blame] | 147 | static int SQLITE_TCLAPI hexio_write( |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 148 | void * clientData, |
| 149 | Tcl_Interp *interp, |
| 150 | int objc, |
| 151 | Tcl_Obj *CONST objv[] |
| 152 | ){ |
| 153 | int offset; |
drh | 064b681 | 2024-07-30 15:49:02 | [diff] [blame] | 154 | Tcl_Size nIn; |
| 155 | int nOut, written; |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 156 | const char *zFile; |
| 157 | const unsigned char *zIn; |
| 158 | unsigned char *aOut; |
| 159 | FILE *out; |
| 160 | |
| 161 | if( objc!=4 ){ |
| 162 | Tcl_WrongNumArgs(interp, 1, objv, "FILENAME OFFSET HEXDATA"); |
| 163 | return TCL_ERROR; |
| 164 | } |
| 165 | if( Tcl_GetIntFromObj(interp, objv[2], &offset) ) return TCL_ERROR; |
| 166 | zFile = Tcl_GetString(objv[1]); |
| 167 | zIn = (const unsigned char *)Tcl_GetStringFromObj(objv[3], &nIn); |
drh | 064b681 | 2024-07-30 15:49:02 | [diff] [blame] | 168 | aOut = sqlite3_malloc64( 1 + nIn/2 ); |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 169 | if( aOut==0 ){ |
| 170 | return TCL_ERROR; |
| 171 | } |
drh | 064b681 | 2024-07-30 15:49:02 | [diff] [blame] | 172 | nOut = sqlite3TestHexToBin(zIn, (int)nIn, aOut); |
drh | 498b876 | 2008-05-12 16:17:42 | [diff] [blame] | 173 | out = fopen(zFile, "r+b"); |
| 174 | if( out==0 ){ |
| 175 | out = fopen(zFile, "r+"); |
| 176 | } |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 177 | if( out==0 ){ |
stephan | b6503f1 | 2025-03-06 13:38:07 | [diff] [blame] | 178 | Tcl_AppendResult(interp, "cannot open output file ", zFile, NULL); |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 179 | return TCL_ERROR; |
| 180 | } |
| 181 | fseek(out, offset, SEEK_SET); |
drh | 83cc139 | 2012-04-19 18:04:28 | [diff] [blame] | 182 | written = (int)fwrite(aOut, 1, nOut, out); |
drh | 1743575 | 2007-08-16 04:30:38 | [diff] [blame] | 183 | sqlite3_free(aOut); |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 184 | fclose(out); |
| 185 | Tcl_SetObjResult(interp, Tcl_NewIntObj(written)); |
| 186 | return TCL_OK; |
| 187 | } |
| 188 | |
| 189 | /* |
drh | 12599d5 | 2024-11-18 17:05:45 | [diff] [blame] | 190 | ** USAGE: hexio_get_int [-littleendian] HEXDATA |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 191 | ** |
| 192 | ** Interpret the HEXDATA argument as a big-endian integer. Return |
| 193 | ** the value of that integer. HEXDATA can contain between 2 and 8 |
| 194 | ** hexadecimal digits. |
| 195 | */ |
mistachkin | 7617e4a | 2016-07-28 17:11:20 | [diff] [blame] | 196 | static int SQLITE_TCLAPI hexio_get_int( |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 197 | void * clientData, |
| 198 | Tcl_Interp *interp, |
| 199 | int objc, |
| 200 | Tcl_Obj *CONST objv[] |
| 201 | ){ |
| 202 | int val; |
drh | 064b681 | 2024-07-30 15:49:02 | [diff] [blame] | 203 | Tcl_Size nIn; |
| 204 | int nOut; |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 205 | const unsigned char *zIn; |
| 206 | unsigned char *aOut; |
| 207 | unsigned char aNum[4]; |
drh | 12599d5 | 2024-11-18 17:05:45 | [diff] [blame] | 208 | int bLittle = 0; |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 209 | |
drh | 12599d5 | 2024-11-18 17:05:45 | [diff] [blame] | 210 | if( objc==3 ){ |
| 211 | Tcl_Size n; |
| 212 | char *z = Tcl_GetStringFromObj(objv[1], &n); |
| 213 | if( n>=2 && n<=13 && memcmp(z, "-littleendian", n)==0 ){ |
| 214 | bLittle = 1; |
| 215 | } |
| 216 | } |
| 217 | if( (objc-bLittle)!=2 ){ |
| 218 | Tcl_WrongNumArgs(interp, 1, objv, "[-littleendian] HEXDATA"); |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 219 | return TCL_ERROR; |
| 220 | } |
drh | 12599d5 | 2024-11-18 17:05:45 | [diff] [blame] | 221 | zIn = (const unsigned char *)Tcl_GetStringFromObj(objv[1+bLittle], &nIn); |
drh | 064b681 | 2024-07-30 15:49:02 | [diff] [blame] | 222 | aOut = sqlite3_malloc64( 1 + nIn/2 ); |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 223 | if( aOut==0 ){ |
| 224 | return TCL_ERROR; |
| 225 | } |
drh | 064b681 | 2024-07-30 15:49:02 | [diff] [blame] | 226 | nOut = sqlite3TestHexToBin(zIn, (int)nIn, aOut); |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 227 | if( nOut>=4 ){ |
| 228 | memcpy(aNum, aOut, 4); |
| 229 | }else{ |
| 230 | memset(aNum, 0, sizeof(aNum)); |
drh | 431e853 | 2007-04-09 20:30:11 | [diff] [blame] | 231 | memcpy(&aNum[4-nOut], aOut, nOut); |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 232 | } |
drh | 1743575 | 2007-08-16 04:30:38 | [diff] [blame] | 233 | sqlite3_free(aOut); |
drh | 12599d5 | 2024-11-18 17:05:45 | [diff] [blame] | 234 | if( bLittle ){ |
| 235 | val = (int)((u32)aNum[3]<<24) | (aNum[2]<<16) | (aNum[1]<<8) | aNum[0]; |
| 236 | }else{ |
| 237 | val = (int)((u32)aNum[0]<<24) | (aNum[1]<<16) | (aNum[2]<<8) | aNum[3]; |
| 238 | } |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 239 | Tcl_SetObjResult(interp, Tcl_NewIntObj(val)); |
| 240 | return TCL_OK; |
| 241 | } |
| 242 | |
| 243 | |
| 244 | /* |
| 245 | ** USAGE: hexio_render_int16 INTEGER |
| 246 | ** |
| 247 | ** Render INTEGER has a 16-bit big-endian integer in hexadecimal. |
| 248 | */ |
mistachkin | 7617e4a | 2016-07-28 17:11:20 | [diff] [blame] | 249 | static int SQLITE_TCLAPI hexio_render_int16( |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 250 | void * clientData, |
| 251 | Tcl_Interp *interp, |
| 252 | int objc, |
| 253 | Tcl_Obj *CONST objv[] |
| 254 | ){ |
| 255 | int val; |
| 256 | unsigned char aNum[10]; |
| 257 | |
| 258 | if( objc!=2 ){ |
| 259 | Tcl_WrongNumArgs(interp, 1, objv, "INTEGER"); |
| 260 | return TCL_ERROR; |
| 261 | } |
| 262 | if( Tcl_GetIntFromObj(interp, objv[1], &val) ) return TCL_ERROR; |
| 263 | aNum[0] = val>>8; |
| 264 | aNum[1] = val; |
drh | 9c7a60d | 2007-10-19 17:47:24 | [diff] [blame] | 265 | sqlite3TestBinToHex(aNum, 2); |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 266 | Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)aNum, 4)); |
| 267 | return TCL_OK; |
| 268 | } |
| 269 | |
| 270 | |
| 271 | /* |
| 272 | ** USAGE: hexio_render_int32 INTEGER |
| 273 | ** |
| 274 | ** Render INTEGER has a 32-bit big-endian integer in hexadecimal. |
| 275 | */ |
mistachkin | 7617e4a | 2016-07-28 17:11:20 | [diff] [blame] | 276 | static int SQLITE_TCLAPI hexio_render_int32( |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 277 | void * clientData, |
| 278 | Tcl_Interp *interp, |
| 279 | int objc, |
| 280 | Tcl_Obj *CONST objv[] |
| 281 | ){ |
| 282 | int val; |
| 283 | unsigned char aNum[10]; |
| 284 | |
| 285 | if( objc!=2 ){ |
| 286 | Tcl_WrongNumArgs(interp, 1, objv, "INTEGER"); |
| 287 | return TCL_ERROR; |
| 288 | } |
| 289 | if( Tcl_GetIntFromObj(interp, objv[1], &val) ) return TCL_ERROR; |
| 290 | aNum[0] = val>>24; |
| 291 | aNum[1] = val>>16; |
| 292 | aNum[2] = val>>8; |
| 293 | aNum[3] = val; |
drh | 9c7a60d | 2007-10-19 17:47:24 | [diff] [blame] | 294 | sqlite3TestBinToHex(aNum, 4); |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 295 | Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)aNum, 8)); |
| 296 | return TCL_OK; |
| 297 | } |
| 298 | |
drh | 53c1402 | 2007-05-10 17:23:11 | [diff] [blame] | 299 | /* |
| 300 | ** USAGE: utf8_to_utf8 HEX |
| 301 | ** |
| 302 | ** The argument is a UTF8 string represented in hexadecimal. |
| 303 | ** The UTF8 might not be well-formed. Run this string through |
| 304 | ** sqlite3Utf8to8() convert it back to hex and return the result. |
| 305 | */ |
mistachkin | 7617e4a | 2016-07-28 17:11:20 | [diff] [blame] | 306 | static int SQLITE_TCLAPI utf8_to_utf8( |
drh | 53c1402 | 2007-05-10 17:23:11 | [diff] [blame] | 307 | void * clientData, |
| 308 | Tcl_Interp *interp, |
| 309 | int objc, |
| 310 | Tcl_Obj *CONST objv[] |
| 311 | ){ |
danielk1977 | 28c6630 | 2007-09-01 11:04:26 | [diff] [blame] | 312 | #ifdef SQLITE_DEBUG |
drh | 064b681 | 2024-07-30 15:49:02 | [diff] [blame] | 313 | Tcl_Size n; |
drh | 53c1402 | 2007-05-10 17:23:11 | [diff] [blame] | 314 | int nOut; |
| 315 | const unsigned char *zOrig; |
| 316 | unsigned char *z; |
| 317 | if( objc!=2 ){ |
| 318 | Tcl_WrongNumArgs(interp, 1, objv, "HEX"); |
| 319 | return TCL_ERROR; |
| 320 | } |
| 321 | zOrig = (unsigned char *)Tcl_GetStringFromObj(objv[1], &n); |
drh | 064b681 | 2024-07-30 15:49:02 | [diff] [blame] | 322 | z = sqlite3_malloc64( n+4 ); |
| 323 | n = sqlite3TestHexToBin(zOrig, (int)n, z); |
drh | 53c1402 | 2007-05-10 17:23:11 | [diff] [blame] | 324 | z[n] = 0; |
| 325 | nOut = sqlite3Utf8To8(z); |
drh | 9c7a60d | 2007-10-19 17:47:24 | [diff] [blame] | 326 | sqlite3TestBinToHex(z,nOut); |
stephan | b6503f1 | 2025-03-06 13:38:07 | [diff] [blame] | 327 | Tcl_AppendResult(interp, (char*)z, NULL); |
drh | 53c1402 | 2007-05-10 17:23:11 | [diff] [blame] | 328 | sqlite3_free(z); |
| 329 | return TCL_OK; |
dan | 211fb08 | 2011-04-01 09:04:36 | [diff] [blame] | 330 | #else |
| 331 | Tcl_AppendResult(interp, |
stephan | 065c0a6 | 2025-03-08 06:53:06 | [diff] [blame] | 332 | "[utf8_to_utf8] unavailable - SQLITE_DEBUG not defined", NULL |
dan | 211fb08 | 2011-04-01 09:04:36 | [diff] [blame] | 333 | ); |
| 334 | return TCL_ERROR; |
| 335 | #endif |
drh | 53c1402 | 2007-05-10 17:23:11 | [diff] [blame] | 336 | } |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 337 | |
dan | 4b4d446 | 2009-11-14 23:50:11 | [diff] [blame] | 338 | static int getFts3Varint(const char *p, sqlite_int64 *v){ |
| 339 | const unsigned char *q = (const unsigned char *) p; |
| 340 | sqlite_uint64 x = 0, y = 1; |
| 341 | while( (*q & 0x80) == 0x80 ){ |
| 342 | x += y * (*q++ & 0x7f); |
| 343 | y <<= 7; |
| 344 | } |
| 345 | x += y * (*q++); |
| 346 | *v = (sqlite_int64) x; |
| 347 | return (int) (q - (unsigned char *)p); |
| 348 | } |
| 349 | |
dan | 572b0dd | 2019-09-18 17:22:00 | [diff] [blame] | 350 | static int putFts3Varint(char *p, sqlite_int64 v){ |
| 351 | unsigned char *q = (unsigned char *) p; |
| 352 | sqlite_uint64 vu = v; |
| 353 | do{ |
| 354 | *q++ = (unsigned char) ((vu & 0x7f) | 0x80); |
| 355 | vu >>= 7; |
| 356 | }while( vu!=0 ); |
| 357 | q[-1] &= 0x7f; /* turn off high bit in final byte */ |
| 358 | assert( q - (unsigned char *)p <= 10 ); |
| 359 | return (int) (q - (unsigned char *)p); |
| 360 | } |
dan | 4b4d446 | 2009-11-14 23:50:11 | [diff] [blame] | 361 | |
dan | 09977bb | 2009-11-13 10:36:20 | [diff] [blame] | 362 | /* |
dan | f5fff2a | 2009-12-12 09:51:25 | [diff] [blame] | 363 | ** USAGE: read_fts3varint BLOB VARNAME |
dan | 09977bb | 2009-11-13 10:36:20 | [diff] [blame] | 364 | ** |
| 365 | ** Read a varint from the start of BLOB. Set variable VARNAME to contain |
| 366 | ** the interpreted value. Return the number of bytes of BLOB consumed. |
| 367 | */ |
mistachkin | 7617e4a | 2016-07-28 17:11:20 | [diff] [blame] | 368 | static int SQLITE_TCLAPI read_fts3varint( |
dan | 09977bb | 2009-11-13 10:36:20 | [diff] [blame] | 369 | void * clientData, |
| 370 | Tcl_Interp *interp, |
| 371 | int objc, |
| 372 | Tcl_Obj *CONST objv[] |
| 373 | ){ |
drh | 064b681 | 2024-07-30 15:49:02 | [diff] [blame] | 374 | Tcl_Size nBlob; |
dan | 09977bb | 2009-11-13 10:36:20 | [diff] [blame] | 375 | unsigned char *zBlob; |
| 376 | sqlite3_int64 iVal; |
| 377 | int nVal; |
| 378 | |
| 379 | if( objc!=3 ){ |
| 380 | Tcl_WrongNumArgs(interp, 1, objv, "BLOB VARNAME"); |
| 381 | return TCL_ERROR; |
| 382 | } |
| 383 | zBlob = Tcl_GetByteArrayFromObj(objv[1], &nBlob); |
| 384 | |
drh | 614d265 | 2009-12-02 18:03:57 | [diff] [blame] | 385 | nVal = getFts3Varint((char*)zBlob, (sqlite3_int64 *)(&iVal)); |
dan | 09977bb | 2009-11-13 10:36:20 | [diff] [blame] | 386 | Tcl_ObjSetVar2(interp, objv[2], 0, Tcl_NewWideIntObj(iVal), 0); |
| 387 | Tcl_SetObjResult(interp, Tcl_NewIntObj(nVal)); |
| 388 | return TCL_OK; |
| 389 | } |
| 390 | |
dan | 572b0dd | 2019-09-18 17:22:00 | [diff] [blame] | 391 | /* |
| 392 | ** USAGE: make_fts3record ARGLIST |
| 393 | */ |
| 394 | static int SQLITE_TCLAPI make_fts3record( |
| 395 | void * clientData, |
| 396 | Tcl_Interp *interp, |
| 397 | int objc, |
| 398 | Tcl_Obj *CONST objv[] |
| 399 | ){ |
| 400 | Tcl_Obj **aArg = 0; |
drh | 064b681 | 2024-07-30 15:49:02 | [diff] [blame] | 401 | Tcl_Size nArg = 0; |
dan | 572b0dd | 2019-09-18 17:22:00 | [diff] [blame] | 402 | unsigned char *aOut = 0; |
drh | 064b681 | 2024-07-30 15:49:02 | [diff] [blame] | 403 | sqlite3_int64 nOut = 0; |
| 404 | sqlite3_int64 nAlloc = 0; |
dan | 572b0dd | 2019-09-18 17:22:00 | [diff] [blame] | 405 | int i; |
dan | 572b0dd | 2019-09-18 17:22:00 | [diff] [blame] | 406 | |
| 407 | if( objc!=2 ){ |
| 408 | Tcl_WrongNumArgs(interp, 1, objv, "LIST"); |
| 409 | return TCL_ERROR; |
| 410 | } |
| 411 | if( Tcl_ListObjGetElements(interp, objv[1], &nArg, &aArg) ){ |
| 412 | return TCL_ERROR; |
| 413 | } |
| 414 | |
drh | 064b681 | 2024-07-30 15:49:02 | [diff] [blame] | 415 | for(i=0; i<(int)nArg; i++){ |
drh | 4a9a5cb | 2024-08-10 15:05:22 | [diff] [blame] | 416 | Tcl_WideInt iVal; |
dan | 572b0dd | 2019-09-18 17:22:00 | [diff] [blame] | 417 | if( TCL_OK==Tcl_GetWideIntFromObj(0, aArg[i], &iVal) ){ |
| 418 | if( nOut+10>nAlloc ){ |
| 419 | int nNew = nAlloc?nAlloc*2:128; |
| 420 | unsigned char *aNew = sqlite3_realloc(aOut, nNew); |
| 421 | if( aNew==0 ){ |
| 422 | sqlite3_free(aOut); |
| 423 | return TCL_ERROR; |
| 424 | } |
| 425 | aOut = aNew; |
| 426 | nAlloc = nNew; |
| 427 | } |
| 428 | nOut += putFts3Varint((char*)&aOut[nOut], iVal); |
| 429 | }else{ |
drh | 064b681 | 2024-07-30 15:49:02 | [diff] [blame] | 430 | Tcl_Size nVal = 0; |
dan | 572b0dd | 2019-09-18 17:22:00 | [diff] [blame] | 431 | char *zVal = Tcl_GetStringFromObj(aArg[i], &nVal); |
| 432 | while( (nOut + nVal)>nAlloc ){ |
drh | 064b681 | 2024-07-30 15:49:02 | [diff] [blame] | 433 | sqlite3_int64 nNew = nAlloc?nAlloc*2:128; |
| 434 | unsigned char *aNew = sqlite3_realloc64(aOut, nNew); |
dan | 572b0dd | 2019-09-18 17:22:00 | [diff] [blame] | 435 | if( aNew==0 ){ |
| 436 | sqlite3_free(aOut); |
| 437 | return TCL_ERROR; |
| 438 | } |
| 439 | aOut = aNew; |
| 440 | nAlloc = nNew; |
| 441 | } |
| 442 | memcpy(&aOut[nOut], zVal, nVal); |
| 443 | nOut += nVal; |
| 444 | } |
| 445 | } |
| 446 | |
| 447 | Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(aOut, nOut)); |
| 448 | sqlite3_free(aOut); |
| 449 | return TCL_OK; |
| 450 | } |
| 451 | |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 452 | |
| 453 | /* |
| 454 | ** Register commands with the TCL interpreter. |
| 455 | */ |
| 456 | int Sqlitetest_hexio_Init(Tcl_Interp *interp){ |
| 457 | static struct { |
| 458 | char *zName; |
| 459 | Tcl_ObjCmdProc *xProc; |
| 460 | } aObjCmd[] = { |
| 461 | { "hexio_read", hexio_read }, |
| 462 | { "hexio_write", hexio_write }, |
| 463 | { "hexio_get_int", hexio_get_int }, |
| 464 | { "hexio_render_int16", hexio_render_int16 }, |
| 465 | { "hexio_render_int32", hexio_render_int32 }, |
drh | 53c1402 | 2007-05-10 17:23:11 | [diff] [blame] | 466 | { "utf8_to_utf8", utf8_to_utf8 }, |
dan | f5fff2a | 2009-12-12 09:51:25 | [diff] [blame] | 467 | { "read_fts3varint", read_fts3varint }, |
dan | 572b0dd | 2019-09-18 17:22:00 | [diff] [blame] | 468 | { "make_fts3record", make_fts3record }, |
drh | 1592659 | 2007-04-06 15:02:13 | [diff] [blame] | 469 | }; |
| 470 | int i; |
| 471 | for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ |
| 472 | Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0); |
| 473 | } |
| 474 | return TCL_OK; |
| 475 | } |