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