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 741186 : 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 741186 : int rc = DBRES_OK;
98 741186 : switch (type) {
99 : case DBTYPE_INTEGER:
100 258136 : rc = databasevm_bind_int(xdata, index+1, ival);
101 258136 : 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 856 : rc = databasevm_bind_null(xdata, index+1);
109 856 : break;
110 :
111 : case DBTYPE_TEXT:
112 384159 : rc = databasevm_bind_text(xdata, index+1, pval, (int)ival);
113 384159 : break;
114 :
115 : case DBTYPE_BLOB:
116 98035 : rc = databasevm_bind_blob(xdata, index+1, (const void *)pval, ival);
117 98035 : break;
118 : }
119 :
120 741186 : 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 1292 : uint64_t pk_checksum (const char *buffer, size_t blen) {
150 1292 : const uint8_t *p = (const uint8_t *)buffer;
151 1292 : uint64_t h = 14695981039346656037ULL;
152 1841089 : for (size_t i = 0; i < blen; i++) {
153 1839797 : h ^= p[i];
154 1839797 : h *= 1099511628211ULL;
155 1839797 : }
156 1292 : return h;
157 : }
158 :
159 : // MARK: - Decoding -
160 :
161 3736609 : static inline int pk_decode_check_bounds (size_t bseek, size_t blen, size_t need) {
162 : // bounds check helper for decoding
163 3736609 : if (bseek > blen) return 0;
164 3736609 : return need <= (blen - bseek);
165 3736609 : }
166 :
167 1550234 : int pk_decode_u8 (const uint8_t *buffer, size_t blen, size_t *bseek, uint8_t *out) {
168 1550234 : if (!pk_decode_check_bounds(*bseek, blen, 1)) return 0;
169 1550230 : *out = buffer[*bseek];
170 1550230 : *bseek += 1;
171 1550230 : return 1;
172 1550234 : }
173 :
174 1383360 : static int pk_decode_uint64 (const uint8_t *buffer, size_t blen, size_t *bseek, size_t nbytes, uint64_t *out) {
175 1383360 : if (nbytes > 8) return 0;
176 1383360 : if (!pk_decode_check_bounds(*bseek, blen, nbytes)) return 0;
177 :
178 : // decode bytes in big-endian order (most significant byte first)
179 1383360 : uint64_t v = 0;
180 5395121 : for (size_t i = 0; i < nbytes; i++) {
181 4011761 : v = (v << 8) | (uint64_t)buffer[*bseek];
182 4011761 : (*bseek)++;
183 4011761 : }
184 :
185 1383360 : *out = v;
186 1383360 : return 1;
187 1383360 : }
188 :
189 803015 : static int pk_decode_data (const uint8_t *buffer, size_t blen, size_t *bseek, size_t n, const uint8_t **out) {
190 803015 : if (!pk_decode_check_bounds(*bseek, blen, n)) return 0;
191 803015 : *out = buffer + *bseek;
192 803015 : *bseek += n;
193 :
194 803015 : return 1;
195 803015 : }
196 :
197 161103 : 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 161103 : uint64_t bits_be = 0;
202 161103 : if (!pk_decode_uint64(buffer, blen, bseek, sizeof(uint64_t), &bits_be)) return 0;
203 :
204 161103 : uint64_t bits = be64_to_host(bits_be);
205 161103 : double value = 0.0;
206 161103 : memcpy(&value, &bits, sizeof(bits));
207 161103 : *out = value;
208 161103 : return 1;
209 161103 : }
210 :
211 215028 : int pk_decode (char *buffer, size_t blen, int count, size_t *seek, int skip_decode_idx, pk_decode_callback cb, void *xdata) {
212 215028 : const uint8_t *ubuf = (const uint8_t *)buffer;
213 215028 : size_t bseek = (seek) ? *seek : 0;
214 215028 : 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 1599247 : for (size_t i = 0; i < (size_t)count; i++) {
221 1384220 : uint8_t type_byte = 0;
222 1384220 : if (!pk_decode_u8(ubuf, blen, &bseek, &type_byte)) return -1;
223 1384219 : int raw_type = (int)(type_byte & 0x07);
224 1384219 : 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 1384219 : bool skip_decode = ((skip_decode_idx >= 0) && (i == (size_t)skip_decode_idx));
229 1384219 : size_t initial_bseek = bseek - 1; // points to type_byte
230 :
231 1384219 : 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 419242 : if (nbytes < 1 || nbytes > 8) return -1;
249 419242 : uint64_t u = 0;
250 419242 : if (!pk_decode_uint64(ubuf, blen, &bseek, nbytes, &u)) return -1;
251 :
252 419242 : 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 419242 : int64_t value = (int64_t)u;
257 419242 : if (raw_type == DATABASE_TYPE_NEGATIVE_INTEGER) value = -value;
258 419242 : if (cb) if (cb(xdata, (int)i, DBTYPE_INTEGER, value, 0.0, NULL) != DBRES_OK) return -1;
259 : }
260 : }
261 419242 : 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 161103 : if (nbytes != 0) return -1;
267 161103 : double value = 0.0;
268 161103 : if (!pk_decode_double(ubuf, blen, &bseek, &value)) return -1;
269 :
270 161103 : 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 161103 : if (raw_type == DATABASE_TYPE_NEGATIVE_FLOAT) value = -value;
275 161103 : if (cb) if (cb(xdata, (int)i, DBTYPE_FLOAT, 0, value, NULL) != DBRES_OK) return -1;
276 : }
277 : }
278 161103 : break;
279 :
280 : case DBTYPE_TEXT:
281 : case DBTYPE_BLOB: {
282 : // validate nbytes for length field
283 803015 : if (nbytes < 1 || nbytes > 8) return -1;
284 803015 : uint64_t ulen = 0;
285 803015 : if (!pk_decode_uint64(ubuf, blen, &bseek, nbytes, &ulen)) return -1;
286 :
287 : // ensure ulen fits in size_t on this platform
288 803015 : if (ulen > (uint64_t)SIZE_MAX) return -1;
289 803015 : size_t len = (size_t)ulen;
290 803015 : const uint8_t *p = NULL;
291 803015 : if (!pk_decode_data(ubuf, blen, &bseek, len, &p)) return -1;
292 :
293 803015 : 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 803015 : if (cb) if (cb(xdata, (int)i, raw_type, (int64_t)len, 0.0, (char *)p) != DBRES_OK) return -1;
299 : }
300 : }
301 803015 : break;
302 :
303 : case DBTYPE_NULL: {
304 856 : if (nbytes != 0) return -1;
305 856 : 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 856 : if (cb) if (cb(xdata, (int)i, DBTYPE_NULL, 0, 0.0, NULL) != DBRES_OK) return -1;
310 : }
311 : }
312 856 : break;
313 :
314 : default:
315 : // should never reach this point
316 0 : return -1;
317 : }
318 1384219 : }
319 :
320 215027 : if (seek) *seek = bseek;
321 215027 : return count;
322 215028 : }
323 :
324 166013 : int pk_decode_prikey (char *buffer, size_t blen, pk_decode_callback cb, void *xdata) {
325 166013 : const uint8_t *ubuf = (const uint8_t *)buffer;
326 166013 : size_t bseek = 0;
327 166013 : uint8_t count = 0;
328 166013 : if (!pk_decode_u8(ubuf, blen, &bseek, &count)) return -1;
329 166010 : return pk_decode(buffer, blen, count, &bseek, -1, cb, xdata);
330 166013 : }
331 :
332 : // MARK: - Encoding -
333 :
334 2303900 : size_t pk_encode_nbytes_needed (int64_t value) {
335 2303900 : uint64_t v = (uint64_t)value;
336 2303900 : if (v <= 0xFFULL) return 1;
337 1033835 : if (v <= 0xFFFFULL) return 2;
338 322250 : if (v <= 0xFFFFFFULL) return 3;
339 322229 : if (v <= 0xFFFFFFFFULL) return 4;
340 322229 : if (v <= 0xFFFFFFFFFFULL) return 5;
341 322229 : if (v <= 0xFFFFFFFFFFFFULL) return 6;
342 322215 : if (v <= 0xFFFFFFFFFFFFFFULL) return 7;
343 319731 : return 8;
344 2303900 : }
345 :
346 3852556 : 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 3852556 : if (b > (SIZE_MAX - a)) return 1;
349 3852556 : *out = a + b;
350 3852556 : return 0;
351 3852556 : }
352 :
353 112669 : size_t pk_encode_size (dbvalue_t **argv, int argc, int reserved, int skip_idx) {
354 : // estimate the required buffer size
355 112669 : size_t required = reserved;
356 : size_t nbytes;
357 : int64_t val;
358 :
359 1647955 : for (int i = 0; i < argc; i++) {
360 1535286 : switch (database_value_type(argv[i])) {
361 : case DBTYPE_INTEGER: {
362 588735 : val = database_value_int(argv[i]);
363 588735 : if (val == INT64_MIN) {
364 2 : if (pk_encode_add_overflow_size(required, 1, &required)) return SIZE_MAX;
365 2 : break;
366 : }
367 588733 : if (val < 0) val = -val;
368 588733 : nbytes = pk_encode_nbytes_needed(val);
369 :
370 588733 : size_t tmp = 0;
371 588733 : if (pk_encode_add_overflow_size(1, nbytes, &tmp)) return SIZE_MAX;
372 588733 : if (pk_encode_add_overflow_size(required, tmp, &required)) return SIZE_MAX;
373 588733 : } break;
374 :
375 : case DBTYPE_FLOAT: {
376 161101 : size_t tmp = 0;
377 161101 : if (pk_encode_add_overflow_size(1, sizeof(uint64_t), &tmp)) return SIZE_MAX;
378 161101 : if (pk_encode_add_overflow_size(required, tmp, &required)) return SIZE_MAX;
379 161101 : } break;
380 :
381 : case DBTYPE_TEXT:
382 : case DBTYPE_BLOB: {
383 783718 : size_t len_sz = (size_t)database_value_bytes(argv[i]);
384 783718 : 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 783718 : if (len_sz > (size_t)INT64_MAX) return SIZE_MAX;
392 783718 : nbytes = pk_encode_nbytes_needed((int64_t)len_sz);
393 :
394 783718 : size_t tmp = 0;
395 : // 1(type) + nbytes(len) + len_sz(payload)
396 783718 : if (pk_encode_add_overflow_size(1, nbytes, &tmp)) return SIZE_MAX;
397 783718 : if (pk_encode_add_overflow_size(tmp, len_sz, &tmp)) return SIZE_MAX;
398 783718 : if (pk_encode_add_overflow_size(required, tmp, &required)) return SIZE_MAX;
399 783718 : } break;
400 :
401 : case DBTYPE_NULL: {
402 1732 : if (pk_encode_add_overflow_size(required, 1, &required)) return SIZE_MAX;
403 1732 : } break;
404 : }
405 1535286 : }
406 :
407 112669 : return required;
408 112669 : }
409 :
410 1107891 : size_t pk_encode_u8 (char *buffer, size_t bseek, uint8_t value) {
411 1107891 : buffer[bseek++] = value;
412 1107891 : return bseek;
413 : }
414 :
415 1092549 : static size_t pk_encode_uint64 (char *buffer, size_t bseek, uint64_t value, size_t nbytes) {
416 4776868 : for (size_t i = 0; i < nbytes; i++) {
417 3684319 : buffer[bseek++] = (uint8_t)((value >> (8 * (nbytes - 1 - i))) & 0xFFu);
418 3684319 : }
419 1092549 : return bseek;
420 : }
421 :
422 556344 : size_t pk_encode_data (char *buffer, size_t bseek, char *data, size_t datalen) {
423 556344 : memcpy(buffer + bseek, data, datalen);
424 556344 : return bseek + datalen;
425 : }
426 :
427 63573 : char *pk_encode (dbvalue_t **argv, int argc, char *b, bool is_prikey, size_t *bsize, int skip_idx) {
428 63573 : size_t bseek = 0;
429 63573 : char *buffer = b;
430 :
431 : // always compute blen (even if it is not a primary key)
432 63573 : size_t blen = pk_encode_size(argv, argc, (is_prikey) ? 1 : 0, skip_idx);
433 63573 : if (blen == SIZE_MAX) return NULL;
434 63573 : if (argc < 0) return NULL;
435 :
436 : // in primary-key encoding the number of items must be explicitly added to the encoded buffer
437 63573 : if (is_prikey) {
438 14477 : if (!bsize) return NULL;
439 : // must fit in a single byte
440 14477 : if (argc > 255) return NULL;
441 :
442 : // if schema does not enforce NOT NULL on primary keys, check at runtime
443 : #ifndef CLOUDSYNC_CHECK_NOTNULL_PRIKEYS
444 666030 : for (int i = 0; i < argc; i++) {
445 651555 : if (database_value_type(argv[i]) == DBTYPE_NULL) return PRIKEY_NULL_CONSTRAINT_ERROR;
446 651553 : }
447 : #endif
448 :
449 : // 1 is the number of items in the serialization
450 : // always 1 byte so max 255 primary keys, even if there is an hard SQLite limit of 128
451 14475 : size_t blen_curr = *bsize;
452 14475 : buffer = (blen > blen_curr || b == NULL) ? cloudsync_memory_alloc((uint64_t)blen) : b;
453 14475 : if (!buffer) return NULL;
454 :
455 : // the first u8 value is the total number of items in the primary key(s)
456 14475 : bseek = pk_encode_u8(buffer, 0, (uint8_t)argc);
457 14475 : } else {
458 : // ensure buffer exists and is large enough also in non-prikey mode
459 49096 : size_t curr = (bsize) ? *bsize : 0;
460 49096 : if (buffer == NULL || curr < blen) return NULL;
461 : }
462 :
463 1156987 : for (int i = 0; i < argc; i++) {
464 1093416 : int type = database_value_type(argv[i]);
465 1093416 : switch (type) {
466 : case DBTYPE_INTEGER: {
467 375107 : int64_t value = database_value_int(argv[i]);
468 375107 : if (value == INT64_MIN) {
469 2 : bseek = pk_encode_u8(buffer, bseek, DATABASE_TYPE_MAX_NEGATIVE_INTEGER);
470 2 : break;
471 : }
472 375105 : if (value < 0) {value = -value; type = DATABASE_TYPE_NEGATIVE_INTEGER;}
473 375105 : size_t nbytes = pk_encode_nbytes_needed(value);
474 375105 : uint8_t type_byte = (uint8_t)((nbytes << 3) | type);
475 375105 : bseek = pk_encode_u8(buffer, bseek, type_byte);
476 375105 : bseek = pk_encode_uint64(buffer, bseek, (uint64_t)value, nbytes);
477 : }
478 375105 : break;
479 : case DBTYPE_FLOAT: {
480 : // Encode doubles as IEEE754 64-bit, big-endian
481 161100 : double value = database_value_double(argv[i]);
482 161100 : if (value < 0) {value = -value; type = DATABASE_TYPE_NEGATIVE_FLOAT;}
483 : uint64_t bits;
484 161100 : memcpy(&bits, &value, sizeof(bits));
485 161100 : bits = host_to_be64(bits);
486 161100 : bseek = pk_encode_u8(buffer, bseek, (uint8_t)type);
487 161100 : bseek = pk_encode_uint64(buffer, bseek, bits, sizeof(bits));
488 : }
489 161100 : break;
490 : case DBTYPE_TEXT:
491 : case DBTYPE_BLOB: {
492 556344 : size_t len = (size_t)database_value_bytes(argv[i]);
493 556344 : if (i == skip_idx) {
494 0 : memcpy(buffer + bseek, (char *)database_value_blob(argv[i]), len);
495 0 : bseek += len;
496 0 : break;
497 : }
498 :
499 556344 : if (len > (size_t)INT64_MAX) return NULL;
500 556344 : size_t nbytes = pk_encode_nbytes_needed((int64_t)len);
501 556344 : uint8_t type_byte = (uint8_t)((nbytes << 3) | database_value_type(argv[i]));
502 556344 : bseek = pk_encode_u8(buffer, bseek, type_byte);
503 556344 : bseek = pk_encode_uint64(buffer, bseek, (uint64_t)len, nbytes);
504 556344 : bseek = pk_encode_data(buffer, bseek, (char *)database_value_blob(argv[i]), len);
505 : }
506 556344 : break;
507 : case DBTYPE_NULL: {
508 865 : bseek = pk_encode_u8(buffer, bseek, DBTYPE_NULL);
509 : }
510 865 : break;
511 : }
512 1093416 : }
513 :
514 : // return actual bytes written; for prikey it's equal to blen, but safer to report bseek
515 63571 : if (bsize) *bsize = bseek;
516 63571 : return buffer;
517 63573 : }
518 :
519 14477 : char *pk_encode_prikey (dbvalue_t **argv, int argc, char *b, size_t *bsize) {
520 14477 : return pk_encode(argv, argc, b, true, bsize, -1);
521 : }
522 :
523 0 : char *pk_encode_value (dbvalue_t *value, size_t *bsize) {
524 0 : dbvalue_t *argv[1] = {value};
525 :
526 0 : size_t blen = pk_encode_size(argv, 1, 0, -1);
527 0 : if (blen == SIZE_MAX) return NULL;
528 :
529 0 : char *buffer = cloudsync_memory_alloc((uint64_t)blen);
530 0 : if (!buffer) return NULL;
531 :
532 0 : *bsize = blen;
533 0 : return pk_encode(argv, 1, buffer, false, bsize, -1);
534 0 : }
|