LCOV - code coverage report
Current view: top level - src - utils.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 132 132
Test Date: 2025-08-08 11:31:42 Functions: 100.0 % 13 13

            Line data    Source code
       1              : //
       2              : //  utils.c
       3              : //  cloudsync
       4              : //
       5              : //  Created by Marco Bambini on 21/08/24.
       6              : //
       7              : 
       8              : #include "utils.h"
       9              : #include <ctype.h>
      10              : #include <stdlib.h>
      11              : 
      12              : #ifdef _WIN32
      13              : #include <windows.h>
      14              : #include <objbase.h>
      15              : #include <bcrypt.h>
      16              : #include <ntstatus.h> //for STATUS_SUCCESS
      17              : #else
      18              : #include <unistd.h>
      19              : #if defined(__APPLE__)
      20              : #include <Security/Security.h>
      21              : #elif !defined(__ANDROID__)
      22              : #include <sys/random.h>
      23              : #endif
      24              : #endif
      25              : 
      26              : #ifndef SQLITE_CORE
      27              : SQLITE_EXTENSION_INIT3
      28              : #endif
      29              : 
      30              : #define FNV_OFFSET_BASIS    0xcbf29ce484222325ULL
      31              : #define FNV_PRIME           0x100000001b3ULL
      32              : #define HASH_CHAR(_c)       do { h ^= (uint8_t)(_c); h *= FNV_PRIME; h_final = h;} while (0)
      33              : 
      34              : // MARK: UUIDv7 -
      35              : 
      36              : /*
      37              :     UUIDv7 is a 128-bit unique identifier like it's older siblings, such as the widely used UUIDv4.
      38              :     But unlike v4, UUIDv7 is time-sortable with 1 ms precision.
      39              :     By combining the timestamp and the random parts, UUIDv7 becomes an excellent choice for record identifiers in databases, including distributed ones.
      40              :  
      41              :     UUIDv7 offers several advantages.
      42              :     It includes a 48-bit Unix timestamp with millisecond accuracy and will overflow far in the future (10899 AD).
      43              :     It also include 74 random bits which means billions can be created every second without collisions.
      44              :     Because of its structure UUIDv7s are globally sortable and can be created in parallel in a distributed system.
      45              :  
      46              :     https://antonz.org/uuidv7/#c
      47              :     https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-7
      48              :  */
      49              : 
      50         4116 : int cloudsync_uuid_v7 (uint8_t value[UUID_LEN]) {
      51              :     // fill the buffer with high-quality random data
      52              :     #ifdef _WIN32
      53              :     if (BCryptGenRandom(NULL, (BYTE*)value, UUID_LEN, BCRYPT_USE_SYSTEM_PREFERRED_RNG) != STATUS_SUCCESS) return -1;
      54              :     #elif defined(__APPLE__)
      55              :     // Use SecRandomCopyBytes for macOS/iOS
      56         4116 :     if (SecRandomCopyBytes(kSecRandomDefault, UUID_LEN, value) != errSecSuccess) return -1;
      57              :     #elif defined(__ANDROID__)
      58              :     //arc4random_buf doesn't have a return value to check for success
      59              :     arc4random_buf(value, UUID_LEN);
      60              :     #else
      61              :     if (getentropy(value, UUID_LEN) != 0) return -1;
      62              :     #endif
      63              :     
      64              :     // get current timestamp in ms
      65              :     struct timespec ts;
      66              :     #ifdef __ANDROID__
      67              :     if (clock_gettime(CLOCK_REALTIME, &ts) != 0) return -1;
      68              :     #else
      69         4116 :     if (timespec_get(&ts, TIME_UTC) == 0) return -1;
      70              :     #endif
      71              :     
      72              :     // add timestamp part to UUID
      73         4116 :     uint64_t timestamp = (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
      74         4116 :     value[0] = (timestamp >> 40) & 0xFF;
      75         4116 :     value[1] = (timestamp >> 32) & 0xFF;
      76         4116 :     value[2] = (timestamp >> 24) & 0xFF;
      77         4116 :     value[3] = (timestamp >> 16) & 0xFF;
      78         4116 :     value[4] = (timestamp >> 8) & 0xFF;
      79         4116 :     value[5] = timestamp & 0xFF;
      80              :     
      81              :     // version and variant
      82         4116 :     value[6] = (value[6] & 0x0F) | 0x70; // UUID version 7
      83         4116 :     value[8] = (value[8] & 0x3F) | 0x80; // RFC 4122 variant
      84              :     
      85         4116 :     return 0;
      86         4116 : }
      87              : 
      88         4068 : char *cloudsync_uuid_v7_stringify (uint8_t uuid[UUID_LEN], char value[UUID_STR_MAXLEN], bool dash_format) {
      89         4068 :     if (dash_format) {
      90         2068 :         snprintf(value, UUID_STR_MAXLEN, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
      91              :             uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
      92              :             uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]
      93              :         );
      94         2068 :     } else {
      95         2000 :         snprintf(value, UUID_STR_MAXLEN, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
      96              :             uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
      97              :             uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]
      98              :         );
      99              :     }
     100              :     
     101         4068 :     return (char *)value;
     102              : }
     103              : 
     104         2068 : char *cloudsync_uuid_v7_string (char value[UUID_STR_MAXLEN], bool dash_format) {
     105              :     uint8_t uuid[UUID_LEN];
     106         2068 :     if (cloudsync_uuid_v7(uuid) != 0) return NULL;
     107              :     
     108         2068 :     return cloudsync_uuid_v7_stringify(uuid, value, dash_format);
     109         2068 : }
     110              : 
     111         1001 : int cloudsync_uuid_v7_compare (uint8_t value1[UUID_LEN], uint8_t value2[UUID_LEN]) {
     112              :     // reconstruct the timestamp by reversing the bit shifts and combining the bytes
     113         1001 :     uint64_t t1 = ((uint64_t)value1[0] << 40) | ((uint64_t)value1[1] << 32) | ((uint64_t)value1[2] << 24) | ((uint64_t)value1[3] << 16) | ((uint64_t)value1[4] << 8)  | ((uint64_t)value1[5]);
     114         1001 :     uint64_t t2 = ((uint64_t)value2[0] << 40) | ((uint64_t)value2[1] << 32) | ((uint64_t)value2[2] << 24) | ((uint64_t)value2[3] << 16) | ((uint64_t)value2[4] << 8)  | ((uint64_t)value2[5]);
     115              :     
     116         1001 :     if (t1 == t2) return memcmp(value1, value2, UUID_LEN);
     117            1 :     return (t1 > t2) ? 1 : -1;
     118         1001 : }
     119              : 
     120              : // MARK: - General -
     121              : 
     122          269 : void *cloudsync_memory_zeroalloc (uint64_t size) {
     123          269 :     void *ptr = (void *)cloudsync_memory_alloc((sqlite3_uint64)size);
     124          269 :     if (!ptr) return NULL;
     125              :     
     126          269 :     memset(ptr, 0, (size_t)size);
     127          269 :     return ptr;
     128          269 : }
     129              : 
     130         2192 : char *cloudsync_string_ndup (const char *str, size_t len, bool lowercase) {
     131         2192 :     if (str == NULL) return NULL;
     132              :     
     133         2192 :     char *s = (char *)cloudsync_memory_alloc((sqlite3_uint64)(len + 1));
     134         2192 :     if (!s) return NULL;
     135              :     
     136         2192 :     if (lowercase) {
     137              :         // convert each character to lowercase and copy it to the new string
     138         3899 :         for (size_t i = 0; i < len; i++) {
     139         3619 :             s[i] = tolower(str[i]);
     140         3619 :         }
     141          280 :     } else {
     142         1912 :         memcpy(s, str, len);
     143              :     }
     144              :     
     145              :     // null-terminate the string
     146         2192 :     s[len] = '\0';
     147              :     
     148         2192 :     return s;
     149         2192 : }
     150              : 
     151         2053 : char *cloudsync_string_dup (const char *str, bool lowercase) {
     152         2053 :     if (str == NULL) return NULL;
     153              :     
     154         2053 :     size_t len = strlen(str);
     155         2053 :     return cloudsync_string_ndup(str, len, lowercase);
     156         2053 : }
     157              : 
     158         8200 : int cloudsync_blob_compare(const char *blob1, size_t size1, const char *blob2, size_t size2) {
     159         8200 :     if (size1 != size2) {
     160          422 :         return (int)(size1 - size2); // Blobs are different if sizes are different
     161              :     }
     162         7778 :     return memcmp(blob1, blob2, size1); // Use memcmp for byte-by-byte comparison
     163         8200 : }
     164              : 
     165        50001 : void cloudsync_rowid_decode (sqlite3_int64 rowid, sqlite3_int64 *db_version, sqlite3_int64 *seq) {
     166              :     // use unsigned 64-bit integer for intermediate calculations
     167              :     // when db_version is large enough, it can cause overflow, leading to negative values
     168              :     // to handle this correctly, we need to ensure the calculations are done in an unsigned 64-bit integer context
     169              :     // before converting back to sqlite3_int64 as needed
     170        50001 :     uint64_t urowid = (uint64_t)rowid;
     171              :     
     172              :     // define the bit mask for seq (30 bits)
     173        50001 :     const uint64_t SEQ_MASK = 0x3FFFFFFF; // (2^30 - 1)
     174              : 
     175              :     // extract seq by masking the lower 30 bits
     176        50001 :     *seq = (sqlite3_int64)(urowid & SEQ_MASK);
     177              : 
     178              :     // extract db_version by shifting 30 bits to the right
     179        50001 :     *db_version = (sqlite3_int64)(urowid >> 30);
     180        50001 : }
     181              : 
     182            2 : char *cloudsync_string_replace_prefix(const char *input, char *prefix, char *replacement) {
     183              :     //const char *prefix = "sqlitecloud://";
     184              :     //const char *replacement = "https://";
     185            2 :     size_t prefix_len = strlen(prefix);
     186            2 :     size_t replacement_len = strlen(replacement);
     187              : 
     188            2 :     if (strncmp(input, prefix, prefix_len) == 0) {
     189              :         // Allocate memory for new string
     190            1 :         size_t input_len = strlen(input);
     191            1 :         size_t new_len = input_len - prefix_len + replacement_len;
     192            1 :         char *result = cloudsync_memory_alloc(new_len + 1); // +1 for null terminator
     193            1 :         if (!result) return NULL;
     194              : 
     195              :         // Copy replacement and the rest of the input string
     196            1 :         strcpy(result, replacement);
     197            1 :         strcpy(result + replacement_len, input + prefix_len);
     198            1 :         return result;
     199              :     }
     200              : 
     201              :     // If no match, return the original string
     202            1 :     return (char *)input;
     203            2 : }
     204              : 
     205              : /*
     206              :  Compute a normalized hash of a SQLite CREATE TABLE statement.
     207              :  
     208              :  * Normalization:
     209              :   * - Skips comments (-- and / * )
     210              :   * - Skips non-printable characters
     211              :   * - Collapses runs of whitespace to single space
     212              :   * - Case-insensitive outside quotes
     213              :   * - Preserves quoted string content exactly
     214              :   * - Handles escaped quotes
     215              :   * - Trims trailing spaces and semicolons from the effective hash
     216              :  */
     217           95 : uint64_t fnv1a_hash (const char *data, size_t len) {
     218           95 :     uint64_t h = FNV_OFFSET_BASIS;
     219           95 :     int q = 0;              // quote state: 0 / '\'' / '"'
     220           95 :     int cmt = 0;            // comment state: 0 / 1=line / 2=block
     221           95 :     int last_space = 1;     // prevent leading space
     222           95 :     uint64_t h_final = h;   // hash state after last non-space, non-semicolon char
     223              :     
     224        28145 :     for (size_t i = 0; i < len; i++) {
     225        28050 :         int c = data[i];
     226        28050 :         int next = (i + 1 < len) ? data[i + 1] : 0;
     227              :         
     228              :         // detect start of comments
     229        28050 :         if (!q && !cmt && c == '-' && next == '-') {cmt = 1; i += 1; continue;}
     230        28050 :         if (!q && !cmt && c == '/' && next == '*') {cmt = 2; i += 1; continue;}
     231              :         
     232              :         // skip comments
     233        28050 :         if (cmt == 1) {if (c == '\n') cmt = 0; continue;}
     234        28050 :         if (cmt == 2) {if (c == '*' && next == '/') { cmt = 0; i += 1; } continue;}
     235              :         
     236              :         // handle quotes
     237        28050 :         if (c == '\'' || c == '"') {
     238         1414 :             if (q == c) {
     239          707 :                 if (next == c) {HASH_CHAR(c); i += 1; continue;}
     240          403 :                 q = 0;
     241         1110 :             } else if (!q) q = c;
     242         1110 :             HASH_CHAR(c);
     243         1110 :             last_space = 0;
     244         1110 :             continue;
     245              :         }
     246              :         
     247              :         // inside quote → hash exactly
     248        26636 :         if (q) {HASH_CHAR(c); last_space = 0; continue;}
     249              :         
     250              :         // skip non-printable
     251        17435 :         if (!isprint((unsigned char)c)) continue;
     252              :         
     253              :         // whitespace normalization
     254        17435 :         if (isspace((unsigned char)c)) {
     255              :             // look ahead to next non-space, non-comment char
     256         2606 :             size_t j = i + 1;
     257         2621 :             while (j < len && isspace((unsigned char)data[j])) j++;
     258              :             
     259         2606 :             int next_c = (j < len) ? data[j] : 0;
     260              :             
     261              :             // if next char is punctuation where space is irrelevant → skip space
     262         2606 :             if (next_c == '(' || next_c == ')' || next_c == ',' || next_c == ';' || next_c == 0) {
     263              :                 // skip inserting space
     264          145 :                 last_space = 1;
     265          145 :                 continue;
     266              :             }
     267              :             
     268              :             // else, insert one space
     269         2461 :             if (!last_space) {HASH_CHAR(' '); last_space = 1;}
     270         2461 :             continue;
     271              :         }
     272              :         
     273              :         // skip semicolons at end
     274        14829 :         if (c == ';') {last_space = 1; continue;}
     275              :         
     276              :         // normal visible char
     277        14829 :         HASH_CHAR(tolower(c));
     278        14829 :         last_space = 0;
     279        14829 :     }
     280              :     
     281           95 :     return h_final;
     282              : }
     283              : // MARK: - CRDT algos -
     284              : 
     285          222 : table_algo crdt_algo_from_name (const char *algo_name) {
     286          222 :     if (algo_name == NULL) return table_algo_none;
     287              :     
     288          222 :     if ((strcasecmp(algo_name, "CausalLengthSet") == 0) || (strcasecmp(algo_name, "cls") == 0)) return table_algo_crdt_cls;
     289           77 :     if ((strcasecmp(algo_name, "GrowOnlySet") == 0) || (strcasecmp(algo_name, "gos") == 0)) return table_algo_crdt_gos;
     290           70 :     if ((strcasecmp(algo_name, "DeleteWinsSet") == 0) || (strcasecmp(algo_name, "dws") == 0)) return table_algo_crdt_dws;
     291           70 :     if ((strcasecmp(algo_name, "AddWinsSet") == 0) || (strcasecmp(algo_name, "aws") == 0)) return table_algo_crdt_aws;
     292              :     
     293              :     // if nothing is found
     294           70 :     return table_algo_none;
     295          222 : }
     296              : 
     297           86 : const char *crdt_algo_name (table_algo algo) {
     298           86 :     switch (algo) {
     299           81 :         case table_algo_crdt_cls: return "cls";
     300            1 :         case table_algo_crdt_gos: return "gos";
     301            1 :         case table_algo_crdt_dws: return "dws";
     302            1 :         case table_algo_crdt_aws: return "aws";
     303            1 :         case table_algo_none: return NULL;
     304              :     }
     305            1 :     return NULL;
     306           86 : }
     307              : 
     308              : // MARK: - Memory Debugger -
     309              : 
     310              : #if CLOUDSYNC_DEBUG_MEMORY
     311              : #include <execinfo.h>
     312              : #include <inttypes.h>
     313              : #include <assert.h>
     314              : 
     315              : #include "khash.h"
     316              : KHASH_MAP_INIT_INT64(HASHTABLE_INT64_VOIDPTR, void*)
     317              : 
     318              : #define STACK_DEPTH             128
     319              : #define BUILD_ERROR(...)        char current_error[1024]; snprintf(current_error, sizeof(current_error), __VA_ARGS__)
     320              : #define BUILD_STACK(v1,v2)      size_t v1; char **v2 = _ptr_stacktrace(&v1)
     321              : 
     322              : typedef struct {
     323              :     void        *ptr;
     324              :     size_t      size;
     325              :     bool        deleted;
     326              :     size_t      nrealloc;
     327              : 
     328              :     // record where it has been allocated/reallocated
     329              :     size_t      nframe;
     330              :     char        **frames;
     331              : 
     332              :     // record where it has been freed
     333              :     size_t      nframe2;
     334              :     char        **frames2;
     335              : } mem_slot;
     336              : 
     337              : static void memdebug_report (char *str, char **stack, size_t nstack, mem_slot *slot);
     338              : 
     339              : static khash_t(HASHTABLE_INT64_VOIDPTR) *htable;
     340              : static uint64_t nalloc, nrealloc, nfree, mem_current, mem_max;
     341              : 
     342              : static void *_ptr_lookup (void *ptr) {
     343              :     khiter_t k = kh_get(HASHTABLE_INT64_VOIDPTR, htable, (int64_t)ptr);
     344              :     void *result = (k == kh_end(htable)) ? NULL : (void *)kh_value(htable, k);
     345              :     return result;
     346              : }
     347              : 
     348              : static bool _ptr_insert (void *ptr, mem_slot *slot) {
     349              :     int err = 0;
     350              :     khiter_t k = kh_put(HASHTABLE_INT64_VOIDPTR, htable, (int64_t)ptr, &err);
     351              :     if (err != -1) kh_value(htable, k) = (void *)slot;
     352              :     return (err != -1);
     353              : }
     354              : 
     355              : static char **_ptr_stacktrace (size_t *nframes) {
     356              :     #if _WIN32
     357              :     // http://www.codeproject.com/Articles/11132/Walking-the-callstack
     358              :     // https://spin.atomicobject.com/2013/01/13/exceptions-stack-traces-c/
     359              :     #else
     360              :     void *callstack[STACK_DEPTH];
     361              :     int n = backtrace(callstack, STACK_DEPTH);
     362              :     char **strs = backtrace_symbols(callstack, n);
     363              :     *nframes = (size_t)n;
     364              :     return strs;
     365              :     #endif
     366              : }
     367              : 
     368              : static mem_slot *_ptr_add (void *ptr, size_t size) {
     369              :     mem_slot *slot = (mem_slot *)calloc(1, sizeof(mem_slot));
     370              :     assert(slot);
     371              :     
     372              :     slot->ptr = ptr;
     373              :     slot->size = size;
     374              :     slot->frames = _ptr_stacktrace(&slot->nframe);
     375              :     bool ok = _ptr_insert(ptr, slot);
     376              :     assert(ok);
     377              : 
     378              :     ++nalloc;
     379              :     mem_current += size;
     380              :     if (mem_current > mem_max) mem_max = mem_current;
     381              :     
     382              :     return slot;
     383              : }
     384              : 
     385              : static void _ptr_remove (void *ptr) {
     386              :     mem_slot *slot = (mem_slot *)_ptr_lookup(ptr);
     387              :     if (!slot) {
     388              :         BUILD_ERROR("Unable to find old pointer to free.");
     389              :         memdebug_report(current_error, NULL, 0, NULL);
     390              :         return;
     391              :     }
     392              :     
     393              :     if (slot->deleted) {
     394              :         BUILD_ERROR("Pointer already freed.");
     395              :         BUILD_STACK(n, stack);
     396              :         memdebug_report(current_error, stack, n, slot);
     397              :     }
     398              :     
     399              :     size_t old_size = slot->size;
     400              :     slot->deleted = true;
     401              :     slot->frames2 = _ptr_stacktrace(&slot->nframe2);
     402              :     
     403              :     ++nfree;
     404              :     mem_current -= old_size;
     405              : }
     406              : 
     407              : static void _ptr_replace (void *old_ptr, void *new_ptr, size_t new_size) {
     408              :     if (old_ptr == NULL) {
     409              :         _ptr_add(new_ptr, new_size);
     410              :         return;
     411              :     }
     412              :     
     413              :     // remove old ptr (implicit free performed by realloc)
     414              :     _ptr_remove(old_ptr);
     415              :     
     416              :     // add newly allocated prt (implicit alloc performed by realloc)
     417              :     mem_slot *slot = _ptr_add(new_ptr, new_size);
     418              :     ++slot->nrealloc;
     419              :     
     420              :     ++nrealloc;
     421              :     if (mem_current > mem_max) mem_max = mem_current;
     422              : }
     423              : 
     424              : // MARK: -
     425              : 
     426              : static bool stacktrace_is_internal(const char *s) {
     427              :     static const char *reserved[] = {"??? ", "libdyld.dylib ", "memdebug_", "_ptr_", NULL};
     428              : 
     429              :     const char **r = reserved;
     430              :     while (*r) {
     431              :         if (strstr(s, *r)) return true;
     432              :         ++r;
     433              :     }
     434              :     return false;
     435              : }
     436              : 
     437              : static void memdebug_report (char *str, char **stack, size_t nstack, mem_slot *slot) {
     438              :     printf("%s\n", str);
     439              :     for (size_t i=0; i<nstack; ++i) {
     440              :         if (stacktrace_is_internal(stack[i])) continue;
     441              :         printf("%s\n", stack[i]);
     442              :     }
     443              : 
     444              :     if (slot) {
     445              :         printf("\nallocated:\n");
     446              :         for (size_t i=0; i<slot->nframe; ++i) {
     447              :             if (stacktrace_is_internal(slot->frames[i])) continue;
     448              :             printf("%s\n", slot->frames[i]);
     449              :         }
     450              : 
     451              :         printf("\nfreed:\n");
     452              :         for (size_t i=0; i<slot->nframe2; ++i) {
     453              :             if (stacktrace_is_internal(slot->frames2[i])) continue;
     454              :             printf("%s\n", slot->frames2[i]);
     455              :         }
     456              :     }
     457              : }
     458              : 
     459              : void memdebug_init (int once) {
     460              :     if (htable == NULL) htable = kh_init(HASHTABLE_INT64_VOIDPTR);
     461              : }
     462              : 
     463              : void memdebug_finalize (void) {
     464              :     printf("\n========== MEMORY STATS ==========\n");
     465              :     printf("Allocations count: %" PRIu64 "\n", nalloc);
     466              :     printf("Reallocations count: %" PRIu64 "\n", nrealloc);
     467              :     printf("Free count: %" PRIu64 "\n", nfree);
     468              :     printf("Leaked: %" PRIu64 " (bytes)\n", mem_current);
     469              :     printf("Max memory usage: %" PRIu64 " (bytes)\n", mem_max);
     470              :     printf("==================================\n\n");
     471              : 
     472              :     if (mem_current > 0) {
     473              :         printf("\n========== LEAKS DETAILS ==========\n");
     474              :         
     475              :         khiter_t k;
     476              :         for (k = kh_begin(htable); k != kh_end(htable); ++k) {
     477              :             if (kh_exist(htable, k)) {
     478              :                 mem_slot *slot = (mem_slot *)kh_value(htable, k);
     479              :                 if ((!slot->ptr) || (slot->deleted)) continue;
     480              :                 
     481              :                 printf("Block %p size: %zu (reallocated %zu)\n", slot->ptr, slot->size, slot->nrealloc);
     482              :                 printf("Call stack:\n");
     483              :                 printf("===========\n");
     484              :                 for (size_t j=0; j<slot->nframe; ++j) {
     485              :                     if (stacktrace_is_internal(slot->frames[j])) continue;
     486              :                     printf("%s\n", slot->frames[j]);
     487              :                 }
     488              :                 printf("===========\n\n");
     489              :             }
     490              :         }
     491              :     }
     492              : }
     493              : 
     494              : void *memdebug_alloc (sqlite3_uint64 size) {
     495              :     void *ptr = sqlite3_malloc64(size);
     496              :     if (!ptr) {
     497              :         BUILD_ERROR("Unable to allocated a block of %lld bytes", size);
     498              :         BUILD_STACK(n, stack);
     499              :         memdebug_report(current_error, stack, n, NULL);
     500              :         return NULL;
     501              :     }
     502              :     _ptr_add(ptr, size);
     503              :     return ptr;
     504              : }
     505              : 
     506              : void *memdebug_realloc (void *ptr, sqlite3_uint64 new_size) {
     507              :     if (!ptr) return memdebug_alloc(new_size);
     508              :     
     509              :     mem_slot *slot = _ptr_lookup(ptr);
     510              :     if (!slot) {
     511              :         BUILD_ERROR("Pointer being reallocated was now previously allocated.");
     512              :         BUILD_STACK(n, stack);
     513              :         memdebug_report(current_error, stack, n, NULL);
     514              :         return NULL;
     515              :     }
     516              :     
     517              :     void *back_ptr = ptr;
     518              :     void *new_ptr = sqlite3_realloc64(ptr, new_size);
     519              :     if (!new_ptr) {
     520              :         BUILD_ERROR("Unable to reallocate a block of %lld bytes.", new_size);
     521              :         BUILD_STACK(n, stack);
     522              :         memdebug_report(current_error, stack, n, slot);
     523              :         return NULL;
     524              :     }
     525              :     
     526              :     _ptr_replace(back_ptr, new_ptr, new_size);
     527              :     return new_ptr;
     528              : }
     529              : 
     530              : char *memdebug_vmprintf (const char *format, va_list list) {
     531              :     char *ptr = sqlite3_vmprintf(format, list);
     532              :     if (!ptr) {
     533              :         BUILD_ERROR("Unable to allocated for sqlite3_vmprintf with format %s", format);
     534              :         BUILD_STACK(n, stack);
     535              :         memdebug_report(current_error, stack, n, NULL);
     536              :         return NULL;
     537              :     }
     538              :     
     539              :     _ptr_add(ptr, sqlite3_msize(ptr));
     540              :     return ptr;
     541              : }
     542              : 
     543              : char *memdebug_mprintf(const char *format, ...) {
     544              :     va_list ap;
     545              :     char *z;
     546              :     
     547              :     va_start(ap, format);
     548              :     z = memdebug_vmprintf(format, ap);
     549              :     va_end(ap);
     550              :     
     551              :     return z;
     552              : }
     553              : 
     554              : sqlite3_uint64 memdebug_msize (void *ptr) {
     555              :     return sqlite3_msize(ptr);
     556              : }
     557              : 
     558              : void memdebug_free (void *ptr) {
     559              :     if (!ptr) {
     560              :         BUILD_ERROR("Trying to deallocate a NULL ptr.");
     561              :         BUILD_STACK(n, stack);
     562              :         memdebug_report(current_error, stack, n, NULL);
     563              :     }
     564              :     
     565              :     // ensure ptr has been previously allocated by malloc, calloc or realloc and not yet freed with free
     566              :     mem_slot *slot = _ptr_lookup(ptr);
     567              :     
     568              :     if (!slot) {
     569              :         BUILD_ERROR("Pointer being freed was not previously allocated.");
     570              :         BUILD_STACK(n, stack);
     571              :         memdebug_report(current_error, stack, n, NULL);
     572              :         return;
     573              :     }
     574              :     
     575              :     if (slot->deleted) {
     576              :         BUILD_ERROR("Pointer already freed.");
     577              :         BUILD_STACK(n, stack);
     578              :         memdebug_report(current_error, stack, n, slot);
     579              :         return;
     580              :     }
     581              :     
     582              :     _ptr_remove(ptr);
     583              :     sqlite3_free(ptr);
     584              : }
     585              : 
     586              : #endif
        

Generated by: LCOV version 2.3.1-1