LCOV - code coverage report
Current view: top level - src - pk.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 87.8 % 312 274
Test Date: 2026-07-17 12:57:54 Functions: 95.7 % 23 22

            Line data    Source code
       1              : //
       2              : //  pk.c
       3              : //  cloudsync
       4              : //
       5              : //  Created by Marco Bambini on 21/08/24.
       6              : //
       7              : 
       8              : #include "pk.h"
       9              : #include "utils.h"
      10              : #include "cloudsync_endian.h"
      11              : #include "cloudsync.h"
      12              : 
      13              : #include <stdio.h>
      14              : #include <string.h>
      15              : #include <limits.h>
      16              : #include <inttypes.h>
      17              :  
      18              : /*
      19              :  
      20              :  The pk_encode and pk_decode functions are designed to serialize and deserialize an array of values (sqlite_value structures)
      21              :  into a binary format that can be transmitted over a network or stored efficiently.
      22              :  These functions support all the data types natively supported by SQLite (integer, float, blob, text, and null)
      23              :  and ensure that the serialized data is platform-independent, particularly with respect to endianess.
      24              :  
      25              :  pk_encode
      26              :  =========
      27              :  The pk_encode function encodes an array of values into a contiguous memory buffer.
      28              :  This buffer can then be sent over a network or saved to a file, ensuring that the data can be reliably reconstructed later, regardless of the platform.
      29              :  
      30              :  Algorithm:
      31              : 
      32              :  * Number of Columns: The first byte of the buffer stores the number of columns (num_args), which is limited to 255 columns.
      33              :  * Type and Length Encoding: For each column:
      34              :     * The type of the column (e.g., integer, float, text) is encoded in a single byte. The first 3 bits represent the type, and the remaining 5 bits encode the number of bytes required for the integer or length information if applicable.
      35              :     * If the column is an integer or a blob/text type, additional bytes are written to the buffer to store the actual value or the length of the data.
      36              :     * Endianess handling is applied using htonl/htonll to ensure integers and floating-point numbers are consistently stored in big-endian format (network byte order), making the serialized data platform-independent.
      37              :     * Floating-point numbers are treated as 64-bit integers for endianess conversion.
      38              :  * Efficient Storage: By using only the minimum number of bytes required to represent integers and lengths, the solution optimizes storage space, reducing the size of the serialized buffer.
      39              :  
      40              :  Advantages:
      41              : 
      42              :  * Platform Independence: By converting all integers and floating-point values to network byte order, the serialized data can be transmitted between systems with different endianess.
      43              :  * Efficiency: The function encodes data into the smallest possible format, minimizing the memory footprint of the serialized data. This is particularly important for network transmission and storage.
      44              :  * Flexibility: Supports multiple data types (integer, float, text, blob, null) and variable-length data, making it suitable for a wide range of applications.
      45              :  
      46              :  pk_decode
      47              :  =========
      48              :  The pk_decode function decodes the buffer created by pk_encode back into an array of sqlite_value structures.
      49              :  This allows the original data to be reconstructed and used by the application.
      50              :  
      51              :  Algorithm:
      52              : 
      53              :  * Read Number of Columns: The function starts by reading the first byte to determine the number of columns in the buffer.
      54              :  * Type and Length Decoding: For each column:
      55              :     * The function reads the type byte to determine the column's data type and the number of bytes used to store length or integer values.
      56              :     * Depending on the type, the appropriate number of bytes is read from the buffer to reconstruct the integer, floating-point value, blob, or text data.
      57              :     * Endianess is handled by converting from network byte order back to the host's byte order using ntohl/ntohll.
      58              :  * Memory Management: For blob and text data, memory is dynamically allocated to store the decoded data. The caller is responsible for freeing this memory after use.
      59              :  
      60              :  Advantages:
      61              : 
      62              :  * Correctness: By reversing the serialization process, the unpack_columns function ensures that the original data can be accurately reconstructed.
      63              :  * Endianess Handling: The function handles endianess conversion during decoding, ensuring that data is correctly interpreted regardless of the platform on which it was serialized or deserialized.
      64              :  * Robustness: The function includes error handling to manage cases where the buffer is malformed or insufficient data is available, reducing the risk of corruption or crashes.
      65              :  
      66              :  Overall Advantages of the Solution
      67              : 
      68              :  * Portability: The serialized format is platform-independent, ensuring data can be transmitted across different architectures without compatibility issues.
      69              :  * Efficiency: The use of compact encoding for integers and lengths reduces the size of the serialized data, optimizing it for storage and transmission.
      70              :  * Versatility: The ability to handle multiple data types and variable-length data makes this solution suitable for complex data structures.
      71              :  * Simplicity: The functions are designed to be straightforward to use, with clear memory management responsibilities.
      72              :  
      73              :  Notes
      74              :  
      75              :  * Floating point values are encoded as IEEE754 double, 64-bit, big-endian byte order.
      76              :  
      77              :  */
      78              : 
      79              : // Three bits are reserved for the type field, so only values in the 0..7 range can be used (8 values)
      80              : // SQLITE already reserved values from 1 to 5
      81              : // #define SQLITE_INTEGER                   1   // now DBTYPE_INTEGER
      82              : // #define SQLITE_FLOAT                     2   // now DBTYPE_FLOAT
      83              : // #define SQLITE_TEXT                      3   // now DBTYPE_TEXT
      84              : // #define SQLITE_BLOB                      4   // now DBTYPE_BLOB
      85              : // #define SQLITE_NULL                      5   // now DBTYPE_NULL
      86              : #define DATABASE_TYPE_NEGATIVE_INTEGER      0   // was SQLITE_NEGATIVE_INTEGER
      87              : #define DATABASE_TYPE_MAX_NEGATIVE_INTEGER  6   // was SQLITE_MAX_NEGATIVE_INTEGER
      88              : #define DATABASE_TYPE_NEGATIVE_FLOAT        7   // was SQLITE_NEGATIVE_FLOAT
      89              : 
      90              : char * const PRIKEY_NULL_CONSTRAINT_ERROR = "PRIKEY_NULL_CONSTRAINT_ERROR";
      91              : 
      92              : // MARK: - Public Callbacks -
      93              : 
      94       781761 : int pk_decode_bind_callback (void *xdata, int index, int type, int64_t ival, double dval, char *pval) {
      95              :     // default decode callback used to bind values to a dbvm_t vm
      96              :     
      97       781761 :     int rc = DBRES_OK;
      98       781761 :     switch (type) {
      99              :         case DBTYPE_INTEGER:
     100       267641 :             rc = databasevm_bind_int(xdata, index+1, ival);
     101       267641 :             break;
     102              :         
     103              :         case DBTYPE_FLOAT:
     104            0 :             rc = databasevm_bind_double(xdata, index+1, dval);
     105            0 :             break;
     106              :             
     107              :         case DBTYPE_NULL:
     108          846 :             rc = databasevm_bind_null(xdata, index+1);
     109          846 :             break;
     110              :             
     111              :         case DBTYPE_TEXT:
     112       410047 :             rc = databasevm_bind_text(xdata, index+1, pval, (int)ival);
     113       410047 :             break;
     114              :             
     115              :         case DBTYPE_BLOB:
     116       103227 :             rc = databasevm_bind_blob(xdata, index+1, (const void *)pval, ival);
     117       103227 :             break;
     118              :     }
     119              :     
     120       781761 :     return rc;
     121              : }
     122              : 
     123            7 : int pk_decode_print_callback (void *xdata, int index, int type, int64_t ival, double dval, char *pval) {
     124            7 :     switch (type) {
     125              :         case DBTYPE_INTEGER:
     126            4 :             printf("%d\tINTEGER:\t%" PRId64 "\n", index, ival);
     127            4 :             break;
     128              :         
     129              :         case DBTYPE_FLOAT:
     130            1 :             printf("%d\tFLOAT:\t%.5f\n", index, dval);
     131            1 :             break;
     132              :             
     133              :         case DBTYPE_NULL:
     134            0 :             printf("%d\tNULL\n", index);
     135            0 :             break;
     136              :             
     137              :         case DBTYPE_TEXT:
     138            1 :             printf("%d\tTEXT:\t%.*s\n", index, (int)ival, pval);
     139            1 :             break;
     140              :             
     141              :         case DBTYPE_BLOB:
     142            1 :             printf("%d\tBLOB:\t%" PRId64 " bytes\n", index, ival);
     143            1 :             break;
     144              :     }
     145              :     
     146            7 :     return DBRES_OK;
     147              : }
     148              : 
     149         1505 : uint64_t pk_checksum (const char *buffer, size_t blen) {
     150         1505 :     const uint8_t *p = (const uint8_t *)buffer;
     151         1505 :     uint64_t h = 14695981039346656037ULL;
     152     27523935 :     for (size_t i = 0; i < blen; i++) {
     153     27522430 :         h ^= p[i];
     154     27522430 :         h *= 1099511628211ULL;
     155     27522430 :     }
     156         1505 :     return h;
     157              : }
     158              : 
     159              : // MARK: - Decoding -
     160              : 
     161      3878108 : static inline int pk_decode_check_bounds (size_t bseek, size_t blen, size_t need) {
     162              :     // bounds check helper for decoding
     163      3878108 :     if (bseek > blen) return 0;
     164      3878108 :     return need <= (blen - bseek);
     165      3878108 : }
     166              : 
     167      1613854 : int pk_decode_u8 (const uint8_t *buffer, size_t blen, size_t *bseek, uint8_t *out) {
     168      1613854 :     if (!pk_decode_check_bounds(*bseek, blen, 1)) return 0;
     169      1613850 :     *out = buffer[*bseek];
     170      1613850 :     *bseek += 1;
     171      1613850 :     return 1;
     172      1613854 : }
     173              : 
     174      1427624 : static int pk_decode_uint64 (const uint8_t *buffer, size_t blen, size_t *bseek, size_t nbytes, uint64_t *out) {
     175      1427624 :     if (nbytes > 8) return 0;
     176      1427624 :     if (!pk_decode_check_bounds(*bseek, blen, nbytes)) return 0;
     177              :     
     178              :     // decode bytes in big-endian order (most significant byte first)
     179      1427624 :     uint64_t v = 0;
     180      5496199 :     for (size_t i = 0; i < nbytes; i++) {
     181      4068575 :         v = (v << 8) | (uint64_t)buffer[*bseek];
     182      4068575 :         (*bseek)++;
     183      4068575 :     }
     184              :     
     185      1427624 :     *out = v;
     186      1427624 :     return 1;
     187      1427624 : }
     188              : 
     189       836630 : static int pk_decode_data (const uint8_t *buffer, size_t blen, size_t *bseek, size_t n, const uint8_t **out) {
     190       836630 :     if (!pk_decode_check_bounds(*bseek, blen, n)) return 0;
     191       836630 :     *out = buffer + *bseek;
     192       836630 :     *bseek += n;
     193              :     
     194       836630 :     return 1;
     195       836630 : }
     196              : 
     197       161497 : int pk_decode_double (const uint8_t *buffer, size_t blen, size_t *bseek, double *out) {
     198              :     // Doubles are encoded as IEEE754 64-bit, big-endian.
     199              :     // Convert back to host order before memcpy into double.
     200              :     
     201       161497 :     uint64_t bits_be = 0;
     202       161497 :     if (!pk_decode_uint64(buffer, blen, bseek, sizeof(uint64_t), &bits_be)) return 0;
     203              :     
     204       161497 :     uint64_t bits = be64_to_host(bits_be);
     205       161497 :     double value = 0.0;
     206       161497 :     memcpy(&value, &bits, sizeof(bits));
     207       161497 :     *out = value;
     208       161497 :     return 1;
     209       161497 : }
     210              : 
     211       236776 : int pk_decode (char *buffer, size_t blen, int count, size_t *seek, int skip_decode_idx, pk_decode_callback cb, void *xdata) {
     212       236776 :     const uint8_t *ubuf = (const uint8_t *)buffer;
     213       236776 :     size_t bseek = (seek) ? *seek : 0;
     214       236776 :     if (count == -1) {
     215            1 :         uint8_t c = 0;
     216            1 :         if (!pk_decode_u8(ubuf, blen, &bseek, &c)) return -1;
     217            1 :         count = (int)c;
     218            1 :     }
     219              :         
     220      1665249 :     for (size_t i = 0; i < (size_t)count; i++) {
     221      1428474 :         uint8_t type_byte = 0;
     222      1428474 :         if (!pk_decode_u8(ubuf, blen, &bseek, &type_byte)) return -1;
     223      1428473 :         int raw_type = (int)(type_byte & 0x07);
     224      1428473 :         size_t nbytes = (size_t)((type_byte >> 3) & 0x1F);
     225              : 
     226              :         // skip_decode wants the raw encoded slice (type_byte + optional len/int + payload)
     227              :         // we still must parse with the *raw* type to know how much to skip
     228      1428473 :         bool skip_decode = ((skip_decode_idx >= 0) && (i == (size_t)skip_decode_idx));
     229      1428473 :         size_t initial_bseek = bseek - 1; // points to type_byte
     230              : 
     231      1428473 :         switch (raw_type) {
     232              :             case DATABASE_TYPE_MAX_NEGATIVE_INTEGER: {
     233              :                 // must not carry length bits
     234            3 :                 if (nbytes != 0) return -1;
     235            3 :                 if (skip_decode) {
     236            0 :                     size_t slice_len = bseek - initial_bseek;
     237            0 :                     if (cb) if (cb(xdata, (int)i, DBTYPE_BLOB, (int64_t)slice_len, 0.0, (char *)(buffer + initial_bseek)) != DBRES_OK) return -1;
     238            0 :                 } else {
     239            3 :                     int64_t value = INT64_MIN;
     240            3 :                     if (cb) if (cb(xdata, (int)i, DBTYPE_INTEGER, value, 0.0, NULL) != DBRES_OK) return -1;
     241              :                 }
     242              :             }
     243            3 :                 break;
     244              :                 
     245              :             case DATABASE_TYPE_NEGATIVE_INTEGER:
     246              :             case DBTYPE_INTEGER: {
     247              :                 // validate nbytes to avoid UB/overreads
     248       429497 :                 if (nbytes < 1 || nbytes > 8) return -1;
     249       429497 :                 uint64_t u = 0;
     250       429497 :                 if (!pk_decode_uint64(ubuf, blen, &bseek, nbytes, &u)) return -1;
     251              :                 
     252       429497 :                 if (skip_decode) {
     253            0 :                     size_t slice_len = bseek - initial_bseek;
     254            0 :                     if (cb) if (cb(xdata, (int)i, DBTYPE_BLOB, (int64_t)slice_len, 0.0, (char *)(buffer + initial_bseek)) != DBRES_OK) return -1;
     255            0 :                 } else {
     256       429497 :                     int64_t value = (int64_t)u;
     257       429497 :                     if (raw_type == DATABASE_TYPE_NEGATIVE_INTEGER) value = -value;
     258       429497 :                     if (cb) if (cb(xdata, (int)i, DBTYPE_INTEGER, value, 0.0, NULL) != DBRES_OK) return -1;
     259              :                 }
     260              :             }
     261       429497 :                 break;
     262              :                 
     263              :             case DATABASE_TYPE_NEGATIVE_FLOAT:
     264              :             case DBTYPE_FLOAT: {
     265              :                 // encoder stores float type with no length bits, so enforce nbytes==0
     266       161497 :                 if (nbytes != 0) return -1;
     267       161497 :                 double value = 0.0;
     268       161497 :                 if (!pk_decode_double(ubuf, blen, &bseek, &value)) return -1;
     269              :                 
     270       161497 :                 if (skip_decode) {
     271            0 :                     size_t slice_len = bseek - initial_bseek;
     272            0 :                     if (cb) if (cb(xdata, (int)i, DBTYPE_BLOB, (int64_t)slice_len, 0.0, (char *)(buffer + initial_bseek)) != DBRES_OK) return -1;
     273            0 :                 } else {
     274       161497 :                     if (raw_type == DATABASE_TYPE_NEGATIVE_FLOAT) value = -value;
     275       161497 :                     if (cb) if (cb(xdata, (int)i, DBTYPE_FLOAT, 0, value, NULL) != DBRES_OK) return -1;
     276              :                 }
     277              :             }
     278       161497 :                 break;
     279              :                 
     280              :             case DBTYPE_TEXT:
     281              :             case DBTYPE_BLOB: {
     282              :                 // validate nbytes for length field
     283       836630 :                 if (nbytes < 1 || nbytes > 8) return -1;
     284       836630 :                 uint64_t ulen = 0;
     285       836630 :                 if (!pk_decode_uint64(ubuf, blen, &bseek, nbytes, &ulen)) return -1;
     286              :                 
     287              :                 // ensure ulen fits in size_t on this platform
     288       836630 :                 if (ulen > (uint64_t)SIZE_MAX) return -1;
     289       836630 :                 size_t len = (size_t)ulen;
     290       836630 :                 const uint8_t *p = NULL;
     291       836630 :                 if (!pk_decode_data(ubuf, blen, &bseek, len, &p)) return -1;
     292              :                 
     293       836630 :                 if (skip_decode) {
     294              :                     // return the full encoded slice (type_byte + len bytes + payload)
     295            0 :                     size_t slice_len = bseek - initial_bseek;
     296            0 :                     if (cb) if (cb(xdata, (int)i, DBTYPE_BLOB, (int64_t)slice_len, 0.0, (char *)(buffer + initial_bseek)) != DBRES_OK) return -1;
     297            0 :                 } else {
     298       836630 :                     if (cb) if (cb(xdata, (int)i, raw_type, (int64_t)len, 0.0, (char *)p) != DBRES_OK) return -1;
     299              :                 }
     300              :             }
     301       836630 :                 break;
     302              :                 
     303              :             case DBTYPE_NULL: {
     304          846 :                 if (nbytes != 0) return -1;
     305          846 :                 if (skip_decode) {
     306            0 :                     size_t slice_len = bseek - initial_bseek;
     307            0 :                     if (cb) if (cb(xdata, (int)i, DBTYPE_BLOB, (int64_t)slice_len, 0.0, (char *)(buffer + initial_bseek)) != DBRES_OK) return -1;
     308            0 :                 } else {
     309          846 :                     if (cb) if (cb(xdata, (int)i, DBTYPE_NULL, 0, 0.0, NULL) != DBRES_OK) return -1;
     310              :                 }
     311              :             }
     312          846 :                 break;
     313              :             
     314              :             default:
     315              :                 // should never reach this point
     316            0 :                 return -1;
     317              :         }
     318      1428473 :     }
     319              :     
     320       236775 :     if (seek) *seek = bseek;
     321       236775 :     return count;
     322       236776 : }
     323              : 
     324       185379 : int pk_decode_prikey (char *buffer, size_t blen, pk_decode_callback cb, void *xdata) {
     325       185379 :     const uint8_t *ubuf = (const uint8_t *)buffer;
     326       185379 :     size_t bseek = 0;
     327       185379 :     uint8_t count = 0;
     328       185379 :     if (!pk_decode_u8(ubuf, blen, &bseek, &count)) return -1;
     329       185376 :     return pk_decode(buffer, blen, count, &bseek, -1, cb, xdata);
     330       185379 : }
     331              : 
     332              : // MARK: - Encoding -
     333              : 
     334      2427276 : size_t pk_encode_nbytes_needed (int64_t value) {
     335      2427276 :     uint64_t v = (uint64_t)value;
     336      2427276 :     if (v <= 0xFFULL) return 1;
     337      1057951 :     if (v <= 0xFFFFULL) return 2;
     338       323708 :     if (v <= 0xFFFFFFULL) return 3;
     339       323465 :     if (v <= 0xFFFFFFFFULL) return 4;
     340       323465 :     if (v <= 0xFFFFFFFFFFULL) return 5;
     341       323465 :     if (v <= 0xFFFFFFFFFFFFULL) return 6;
     342       323457 :     if (v <= 0xFFFFFFFFFFFFFFULL) return 7;
     343       320963 :     return 8;
     344      2427276 : }
     345              : 
     346      4083334 : static inline int pk_encode_add_overflow_size (size_t a, size_t b, size_t *out) {
     347              :     // safe size_t addition helper (prevents overflow)
     348      4083334 :     if (b > (SIZE_MAX - a)) return 1;
     349      4083334 :     *out = a + b;
     350      4083334 :     return 0;
     351      4083334 : }
     352              : 
     353       123625 : size_t pk_encode_size (dbvalue_t **argv, int argc, int reserved, int skip_idx) {
     354              :     // estimate the required buffer size
     355       123625 :     size_t required = reserved;
     356              :     size_t nbytes;
     357              :     int64_t val;
     358              :     
     359      1748773 :     for (int i = 0; i < argc; i++) {
     360      1625148 :         switch (database_value_type(argv[i])) {
     361              :             case DBTYPE_INTEGER: {
     362       627189 :                 val = database_value_int(argv[i]);
     363       627189 :                 if (val == INT64_MIN) {
     364            2 :                     if (pk_encode_add_overflow_size(required, 1, &required)) return SIZE_MAX;
     365            2 :                     break;
     366              :                 }
     367       627187 :                 if (val < 0) val = -val;
     368       627187 :                 nbytes = pk_encode_nbytes_needed(val);
     369              :                 
     370       627187 :                 size_t tmp = 0;
     371       627187 :                 if (pk_encode_add_overflow_size(1, nbytes, &tmp)) return SIZE_MAX;
     372       627187 :                 if (pk_encode_add_overflow_size(required, tmp, &required)) return SIZE_MAX;
     373       627187 :             } break;
     374              :                 
     375              :             case DBTYPE_FLOAT: {
     376       161495 :                 size_t tmp = 0;
     377       161495 :                 if (pk_encode_add_overflow_size(1, sizeof(uint64_t), &tmp)) return SIZE_MAX;
     378       161495 :                 if (pk_encode_add_overflow_size(required, tmp, &required)) return SIZE_MAX;
     379       161495 :             } break;
     380              :                 
     381              :             case DBTYPE_TEXT:
     382              :             case DBTYPE_BLOB: {
     383       834752 :                 size_t len_sz = (size_t)database_value_bytes(argv[i]);
     384       834752 :                 if (i == skip_idx) {
     385            0 :                     if (pk_encode_add_overflow_size(required, len_sz, &required)) return SIZE_MAX;
     386            0 :                     break;
     387              :                 }
     388              :                 
     389              :                 // Ensure length can be represented by encoder (we encode length with up to 8 bytes)
     390              :                 // pk_encode_nbytes_needed expects int64-ish values; clamp-check here.
     391       834752 :                 if (len_sz > (size_t)INT64_MAX) return SIZE_MAX;
     392       834752 :                 nbytes = pk_encode_nbytes_needed((int64_t)len_sz);
     393              :                 
     394       834752 :                 size_t tmp = 0;
     395              :                 // 1(type) + nbytes(len) + len_sz(payload)
     396       834752 :                 if (pk_encode_add_overflow_size(1, nbytes, &tmp)) return SIZE_MAX;
     397       834752 :                 if (pk_encode_add_overflow_size(tmp, len_sz, &tmp)) return SIZE_MAX;
     398       834752 :                 if (pk_encode_add_overflow_size(required, tmp, &required)) return SIZE_MAX;
     399       834752 :             } break;
     400              :                 
     401              :             case DBTYPE_NULL: {
     402         1712 :                 if (pk_encode_add_overflow_size(required, 1, &required)) return SIZE_MAX;
     403         1712 :             } break;
     404              :         }
     405      1625148 :     }
     406              :     
     407       123625 :     return required;
     408       123625 : }
     409              : 
     410      1143050 : size_t pk_encode_u8 (char *buffer, size_t bseek, uint8_t value) {
     411      1143050 :     buffer[bseek++] = value;
     412      1143050 :     return bseek;
     413              : }
     414              : 
     415      1126207 : static size_t pk_encode_uint64 (char *buffer, size_t bseek, uint64_t value, size_t nbytes) {
     416      4858032 :     for (size_t i = 0; i < nbytes; i++) {
     417      3731825 :         buffer[bseek++] = (uint8_t)((value >> (8 * (nbytes - 1 - i))) & 0xFFu);
     418      3731825 :     }
     419      1126207 :     return bseek;
     420              : }
     421              : 
     422       576167 : size_t pk_encode_data (char *buffer, size_t bseek, char *data, size_t datalen) {
     423       576167 :     memcpy(buffer + bseek, data, datalen);
     424       576167 :     return bseek + datalen;
     425              : }
     426              : 
     427          624 : size_t pk_encode_raw_size (int type, int64_t len_or_value) {
     428          624 :     switch (type) {
     429              :         case DBTYPE_INTEGER: {
     430          240 :             if (len_or_value == INT64_MIN) return 1;
     431          240 :             if (len_or_value < 0) len_or_value = -len_or_value;
     432          240 :             return 1 + pk_encode_nbytes_needed(len_or_value);
     433              :         }
     434              :         case DBTYPE_FLOAT:
     435            0 :             return 1 + sizeof(uint64_t);
     436              :         case DBTYPE_TEXT:
     437              :         case DBTYPE_BLOB: {
     438          384 :             if (len_or_value < 0) return SIZE_MAX;
     439          384 :             size_t nbytes = pk_encode_nbytes_needed(len_or_value);
     440          384 :             return 1 + nbytes + (size_t)len_or_value;
     441              :         }
     442              :         case DBTYPE_NULL:
     443            0 :             return 1;
     444              :     }
     445            0 :     return SIZE_MAX;
     446          624 : }
     447              : 
     448          128 : size_t pk_encode_raw_int (char *buffer, int64_t value) {
     449          128 :     int type = DBTYPE_INTEGER;
     450          128 :     size_t bseek = 0;
     451          128 :     if (value == INT64_MIN) {
     452            0 :         return pk_encode_u8(buffer, bseek, DATABASE_TYPE_MAX_NEGATIVE_INTEGER);
     453              :     }
     454          128 :     if (value < 0) { value = -value; type = DATABASE_TYPE_NEGATIVE_INTEGER; }
     455          128 :     size_t nbytes = pk_encode_nbytes_needed(value);
     456          128 :     uint8_t type_byte = (uint8_t)((nbytes << 3) | type);
     457          128 :     bseek = pk_encode_u8(buffer, bseek, type_byte);
     458          128 :     return pk_encode_uint64(buffer, bseek, (uint64_t)value, nbytes);
     459          128 : }
     460              : 
     461           64 : size_t pk_encode_raw_text (char *buffer, const char *value, size_t len) {
     462           64 :     size_t nbytes = pk_encode_nbytes_needed((int64_t)len);
     463           64 :     uint8_t type_byte = (uint8_t)((nbytes << 3) | DBTYPE_TEXT);
     464           64 :     size_t bseek = pk_encode_u8(buffer, 0, type_byte);
     465           64 :     bseek = pk_encode_uint64(buffer, bseek, (uint64_t)len, nbytes);
     466           64 :     return pk_encode_data(buffer, bseek, (char *)value, len);
     467              : }
     468              : 
     469           96 : size_t pk_encode_raw_blob (char *buffer, const void *value, size_t len) {
     470           96 :     size_t nbytes = pk_encode_nbytes_needed((int64_t)len);
     471           96 :     uint8_t type_byte = (uint8_t)((nbytes << 3) | DBTYPE_BLOB);
     472           96 :     size_t bseek = pk_encode_u8(buffer, 0, type_byte);
     473           96 :     bseek = pk_encode_uint64(buffer, bseek, (uint64_t)len, nbytes);
     474           96 :     return pk_encode_data(buffer, bseek, (char *)value, len);
     475              : }
     476              :     
     477        68251 : char *pk_encode (dbvalue_t **argv, int argc, char *b, bool is_prikey, size_t *bsize, int skip_idx) {
     478        68251 :     size_t bseek = 0;
     479        68251 :     char *buffer = b;
     480              :     
     481              :     // always compute blen (even if it is not a primary key)
     482        68251 :     size_t blen = pk_encode_size(argv, argc, (is_prikey) ? 1 : 0, skip_idx);
     483        68251 :     if (blen == SIZE_MAX) return NULL;
     484        68251 :     if (argc < 0) return NULL;
     485              :     
     486              :     // in primary-key encoding the number of items must be explicitly added to the encoded buffer
     487        68251 :     if (is_prikey) {
     488        15988 :         if (!bsize) return NULL;
     489              :         // must fit in a single byte
     490        15988 :         if (argc > 255) return NULL;
     491              :         
     492              :         // if schema does not enforce NOT NULL on primary keys, check at runtime
     493              :         #ifndef CLOUDSYNC_CHECK_NOTNULL_PRIKEYS
     494       672398 :         for (int i = 0; i < argc; i++) {
     495       656412 :             if (database_value_type(argv[i]) == DBTYPE_NULL) return PRIKEY_NULL_CONSTRAINT_ERROR;
     496       656410 :         }
     497              :         #endif
     498              :         
     499              :         // 1 is the number of items in the serialization
     500              :         // always 1 byte so max 255 primary keys, even if there is an hard SQLite limit of 128
     501        15986 :         size_t blen_curr = *bsize;
     502        15986 :         buffer = (blen > blen_curr || b == NULL) ? cloudsync_memory_alloc((uint64_t)blen) : b;
     503        15986 :         if (!buffer) return NULL;
     504              :         
     505              :         // the first u8 value is the total number of items in the primary key(s)
     506        15986 :         bseek = pk_encode_u8(buffer, 0, (uint8_t)argc);
     507        15986 :     } else {
     508              :         // ensure buffer exists and is large enough also in non-prikey mode
     509        52263 :         size_t curr = (bsize) ? *bsize : 0;
     510        52263 :         if (buffer == NULL || curr < blen) return NULL;
     511              :     }
     512              :         
     513      1195025 :     for (int i = 0; i < argc; i++) {
     514      1126776 :         int type = database_value_type(argv[i]);
     515      1126776 :         switch (type) {
     516              :             case DBTYPE_INTEGER: {
     517       388420 :                 int64_t value = database_value_int(argv[i]);
     518       388420 :                 if (value == INT64_MIN) {
     519            2 :                     bseek = pk_encode_u8(buffer, bseek, DATABASE_TYPE_MAX_NEGATIVE_INTEGER);
     520            2 :                     break;
     521              :                 }
     522       388418 :                 if (value < 0) {value = -value; type = DATABASE_TYPE_NEGATIVE_INTEGER;}
     523       388418 :                 size_t nbytes = pk_encode_nbytes_needed(value);
     524       388418 :                 uint8_t type_byte = (uint8_t)((nbytes << 3) | type);
     525       388418 :                 bseek = pk_encode_u8(buffer, bseek, type_byte);
     526       388418 :                 bseek = pk_encode_uint64(buffer, bseek, (uint64_t)value, nbytes);
     527              :             }
     528       388418 :                 break;
     529              :             case DBTYPE_FLOAT: {
     530              :                 // Encode doubles as IEEE754 64-bit, big-endian
     531       161494 :                 double value = database_value_double(argv[i]);
     532       161494 :                 if (value < 0) {value = -value; type = DATABASE_TYPE_NEGATIVE_FLOAT;}
     533              :                 uint64_t bits;
     534       161494 :                 memcpy(&bits, &value, sizeof(bits));
     535       161494 :                 bits = host_to_be64(bits);
     536       161494 :                 bseek = pk_encode_u8(buffer, bseek, (uint8_t)type);
     537       161494 :                 bseek = pk_encode_uint64(buffer, bseek, bits, sizeof(bits));
     538              :             }
     539       161494 :                 break;
     540              :             case DBTYPE_TEXT:
     541              :             case DBTYPE_BLOB: {
     542       576007 :                 size_t len = (size_t)database_value_bytes(argv[i]);
     543       576007 :                 if (i == skip_idx) {
     544            0 :                     memcpy(buffer + bseek, (char *)database_value_blob(argv[i]), len);
     545            0 :                     bseek += len;
     546            0 :                     break;
     547              :                 }
     548              : 
     549       576007 :                 if (len > (size_t)INT64_MAX) return NULL;
     550       576007 :                 size_t nbytes = pk_encode_nbytes_needed((int64_t)len);
     551       576007 :                 uint8_t type_byte = (uint8_t)((nbytes << 3) | database_value_type(argv[i]));
     552       576007 :                 bseek = pk_encode_u8(buffer, bseek, type_byte);
     553       576007 :                 bseek = pk_encode_uint64(buffer, bseek, (uint64_t)len, nbytes);
     554       576007 :                 bseek = pk_encode_data(buffer, bseek, (char *)database_value_blob(argv[i]), len);
     555              :             }
     556       576007 :                 break;
     557              :             case DBTYPE_NULL: {
     558          855 :                 bseek = pk_encode_u8(buffer, bseek, DBTYPE_NULL);
     559              :             }
     560          855 :                 break;
     561              :         }
     562      1126776 :     }
     563              :     
     564              :     // return actual bytes written; for prikey it's equal to blen, but safer to report bseek
     565        68249 :     if (bsize) *bsize = bseek;
     566        68249 :     return buffer;
     567        68251 : }
     568              : 
     569        15988 : char *pk_encode_prikey (dbvalue_t **argv, int argc, char *b, size_t *bsize) {
     570        15988 :     return pk_encode(argv, argc, b, true, bsize, -1);
     571              : }
     572              : 
     573            0 : char *pk_encode_value (dbvalue_t *value, size_t *bsize) {
     574            0 :     dbvalue_t *argv[1] = {value};
     575              :     
     576            0 :     size_t blen = pk_encode_size(argv, 1, 0, -1);
     577            0 :     if (blen == SIZE_MAX) return NULL;
     578              :     
     579            0 :     char *buffer = cloudsync_memory_alloc((uint64_t)blen);
     580            0 :     if (!buffer) return NULL;
     581              :     
     582            0 :     *bsize = blen;
     583            0 :     return pk_encode(argv, 1, buffer, false, bsize, -1);
     584            0 : }
        

Generated by: LCOV version 2.5-0