1 /*
2 ** 2020-04-14
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 **
13 ** This SQLite extension implements the UINT collating sequence.
14 **
15 ** UINT works like BINARY for text, except that embedded strings
16 ** of digits compare in numeric order.
17 **
18 **     *   Leading zeros are handled properly, in the sense that
19 **         they do not mess of the maginitude comparison of embedded
20 **         strings of digits.  "x00123y" is equal to "x123y".
21 **
22 **     *   Only unsigned integers are recognized.  Plus and minus
23 **         signs are ignored.  Decimal points and exponential notation
24 **         are ignored.
25 **
26 **     *   Embedded integers can be of arbitrary length.  Comparison
27 **         is *not* limited integers that can be expressed as a
28 **         64-bit machine integer.
29 */
30 #include "sqlite3ext.h"
31 SQLITE_EXTENSION_INIT1
32 #include <assert.h>
33 #include <string.h>
34 #include <ctype.h>
35 
36 /*
37 ** Compare text in lexicographic order, except strings of digits
38 ** compare in numeric order.
39 */
uintCollFunc(void * notUsed,int nKey1,const void * pKey1,int nKey2,const void * pKey2)40 static int uintCollFunc(
41   void *notUsed,
42   int nKey1, const void *pKey1,
43   int nKey2, const void *pKey2
44 ){
45   const unsigned char *zA = (const unsigned char*)pKey1;
46   const unsigned char *zB = (const unsigned char*)pKey2;
47   int i=0, j=0, x;
48   (void)notUsed;
49   while( i<nKey1 && j<nKey2 ){
50     x = zA[i] - zB[j];
51     if( isdigit(zA[i]) ){
52       int k;
53       if( !isdigit(zB[j]) ) return x;
54       while( i<nKey1 && zA[i]=='0' ){ i++; }
55       while( j<nKey2 && zB[j]=='0' ){ j++; }
56       k = 0;
57       while( i+k<nKey1 && isdigit(zA[i+k])
58              && j+k<nKey2 && isdigit(zB[j+k]) ){
59         k++;
60       }
61       if( i+k<nKey1 && isdigit(zA[i+k]) ){
62         return +1;
63       }else if( j+k<nKey2 && isdigit(zB[j+k]) ){
64         return -1;
65       }else{
66         x = memcmp(zA+i, zB+j, k);
67         if( x ) return x;
68         i += k;
69         j += k;
70       }
71     }else if( x ){
72       return x;
73     }else{
74       i++;
75       j++;
76     }
77   }
78   return (nKey1 - i) - (nKey2 - j);
79 }
80 
81 #ifdef _WIN32
82 __declspec(dllexport)
83 #endif
sqlite3_uint_init(sqlite3 * db,char ** pzErrMsg,const sqlite3_api_routines * pApi)84 int sqlite3_uint_init(
85   sqlite3 *db,
86   char **pzErrMsg,
87   const sqlite3_api_routines *pApi
88 ){
89   SQLITE_EXTENSION_INIT2(pApi);
90   (void)pzErrMsg;  /* Unused parameter */
91   return sqlite3_create_collation(db, "uint", SQLITE_UTF8, 0, uintCollFunc);
92 }
93