1 /***********************************************************************************************************************************
2 Pack Type
3
4 Each pack field begins with a one byte tag. The four high order bits of the tag contain the field type (PackTypeMap). To allow more
5 types than four bits will allow, the type bits can be set to 0xF and the rest of the type (- 0xF) will be stored by a base-128
6 encoded integer immediately following the tag. The four lower order bits vary by type.
7
8 When the "more ID delta" indicator is set then the tag (and type, if any) will be followed by a base-128 encoded integer with the
9 higher order ID delta bits. The ID delta represents the delta from the ID of the previous field. When the "more value indicator"
10 then the tag (and the type and ID delta, if any) will be followed by a base-128 encoded integer with the high order value bits, i.e.
11 the bits that were not stored directly in the tag.
12
13 For integer types the value is the integer being stored but for string and binary types the value is 1 if the size is greater than 0
14 and 0 if the size is 0. When the size is greater than 0 the tag is immediately followed by (or after the delta ID if "more ID delta"
15 is set) the base-128 encoded size and then by the string/binary bytes. For string and binary types the value bit indicates if there
16 is data, not the length of the data, which is why the length is stored immediately following the tag when the value bit is set. This
17 prevents storing an additional byte when the string/binary length is zero.
18
19 The following are definitions for the pack tag field and examples of how it is interpreted.
20
21 Integer types (packTypeMapData[type].valueMultiBit) when an unsigned value is <= 1 or a signed value is >= -1 and <= 0:
22 3 - more value indicator bit set to 0
23 2 - value low order bit
24 1 - more ID delta indicator bit
25 0 - ID delta low order bit
26
27 Example: b704
28 b = unsigned int 64 type
29 7 = tag byte low bits: 0 1 1 1 meaning:
30 "value low order bit" - the value of the u64 field is 1
31 "more ID delta indicator bit" - there exists a gap (i.e. NULLs are not stored so there is a gap between the stored IDs)
32 "ID delta low order bit" - gaps are interpreted as the currently stored ID minus previously stored ID minus 1, therefore if
33 the previously store ID is 1 and the ID of this u64 field is 11 then a gap of 10 exists. 10 is represented internally as
34 9 since there is always at least a gap of 1 which never needs to be recorded (it is a given). 9 in bit format is
35 1 0 0 1 - the low-order bit is 1 so the "ID delta low order bit" is set.
36 04 = since the low order bit of the internal ID delta was already set in bit 0 of the tag byte, then the remain bits are shifted
37 right by one and represented in this second byte as 4. To get the ID delta for 04, shift the 4 back to the left one and then
38 add back the "ID delta low order bit" to give a binary representation of 1 0 0 1 = 9. Add back the 1 which is never
39 recorded and the ID gap is 10.
40
41 Integer types (packTypeMapData[type].valueMultiBit) when an unsigned value is > 1 or a signed value is < -1 or > 0:
42 3 - more value indicator bit set to 1
43 2 - more ID delta indicator bit
44 0-1 - ID delta low order bits
45
46 Example: 5e021f
47 5 = signed int 64 type
48 e = tag byte low bits: 1 1 1 0 meaning:
49 "more value indicator bit set to 1" - the actual value is < -1 or > 0
50 "more ID delta indicator bit" - there exists a gap (i.e. NULLs are not stored so there is a gap between the stored IDs)
51 "ID delta low order bits" - here the bit 1 is set to 1 and bit 0 is not so the ID delta has the second low order bit set but
52 not the first
53 02 = since bit 0 and bit 1 of the tag byte are accounted for then the 02 is the result of shifting the ID delta right by 2.
54 Shifting the 2 back to the left by 2 and adding back the second low order bit as 1 and the first low order bit as 0 then
55 the bit representation would be 1 0 1 0 which is ten (10) so the gap between the IDs is 11.
56 1f = signed, zigzag representation of -16 (the actual value)
57
58 String, binary types, and boolean (packTypeMapData[type].valueSingleBit):
59 3 - value bit
60 2 - more ID delta indicator bit
61 0-1 - ID delta low order bits
62 Note: binary type is interpreted the same way as string type
63
64 Example: 8c090673616d706c65
65 8 = string type
66 c = tag byte low bits: 1 1 0 0 meaning:
67 "value bit" - there is data
68 "more ID delta indicator bit" - there exists a gap (i.e. NULLs are not stored so there is a gap between the stored IDs)
69 09 = since neither "ID delta low order bits" is set in the tag, they are both 0, so shifting 9 left by 2, the 2 low order bits
70 are now 0 so the result is 0x24 = 36 in decimal. Add back the 1 which is never recorded and the ID gap is 37.
71 06 = the length of the string is 6 bytes
72 73616d706c65 = the 6 bytes of the string value ("sample")
73
74 Example: 30
75 3 = boolean type
76 0 = "value bit" 0 means the value is false
77 Note that if the boolean had been pack written with .defaultWrite = false there would have been a gap instead of the 30.
78
79 Array and object types:
80 3 - more ID delta indicator bit
81 0-2 - ID delta low order bits
82 Note: arrays and objects are merely containers for the other pack types.
83
84 Example: 1801 (container begin)
85 1 = array type
86 8 = "more ID delta indicator bit" - there exists a gap (i.e. NULLs are not stored so there is a gap between the stored IDs)
87 01 = since there are three "ID delta low order bits", the 01 will be shifted left by 3 with zeros, resulting in 8. Add back
88 the 1 which is never recorded and the ID gap is 9.
89 ...
90 00 = container end - the array/object container end will occur when a 0 byte (00) is encountered that is not part of a pack
91 field within the array/object
92
93 ***********************************************************************************************************************************/
94 #include "build.auto.h"
95
96 #include <string.h>
97
98 #include "common/debug.h"
99 #include "common/io/bufferRead.h"
100 #include "common/io/bufferWrite.h"
101 #include "common/io/io.h"
102 #include "common/io/read.h"
103 #include "common/io/write.h"
104 #include "common/type/convert.h"
105 #include "common/type/pack.h"
106
107 /***********************************************************************************************************************************
108 Constants
109 ***********************************************************************************************************************************/
110 #define PACK_UINT64_SIZE_MAX 10
111
112 /***********************************************************************************************************************************
113 Map PackType types to the types that will be written into the pack. This hides the details of the type IDs from the user and allows
114 the IDs used in the pack to differ from the IDs the user sees.
115 ***********************************************************************************************************************************/
116 typedef enum
117 {
118 pckTypeMapUnknown = 0, // Used internally when the type is not known
119 pckTypeMapArray = 1, // Maps to pckTypeArray
120 pckTypeMapBool = 2, // Maps to pckTypeBool
121 pckTypeMapI32 = 3, // Maps to pckTypeI32
122 pckTypeMapI64 = 4, // Maps to pckTypeI64
123 pckTypeMapObj = 5, // Maps to pckTypeObj
124 pckTypeMapPtr = 6, // Maps to pckTypePtr
125 pckTypeMapStr = 7, // Maps to pckTypeStr
126 pckTypeMapU32 = 8, // Maps to pckTypeU32
127 pckTypeMapU64 = 9, // Maps to pckTypeU64
128 pckTypeMapStrId = 10, // Maps to pckTypeStrId
129
130 // The empty positions before 15 can be used for new types that will be encoded entirely in the tag
131
132 pckTypeMapTime = 15, // Maps to pckTypeTime
133 pckTypeMapBin = 16, // Maps to pckTypeBin
134 pckTypeMapPack = 17, // Maps to pckTypePack
135 pckTypeMapMode = 18, // Maps to pckTypeMode
136 } PackTypeMap;
137
138 typedef struct PackTypeMapData
139 {
140 PackType type; // Data type
141 bool valueSingleBit; // Can the value be stored in a single bit (e.g. bool)
142 bool valueMultiBit; // Can the value require multiple bits (e.g. integer)
143 bool size; // Does the type require a size (e.g. string)
144 } PackTypeMapData;
145
146 static const PackTypeMapData packTypeMapData[] =
147 {
148 // Unknown type map data should not be used
149 {0},
150
151 // Formats that can be encoded entirely in the tag
152 {
153 .type = pckTypeArray,
154 },
155 {
156 .type = pckTypeBool,
157 .valueSingleBit = true,
158 },
159 {
160 .type = pckTypeI32,
161 .valueMultiBit = true,
162 },
163 {
164 .type = pckTypeI64,
165 .valueMultiBit = true,
166 },
167 {
168 .type = pckTypeObj,
169 },
170 {
171 .type = pckTypePtr,
172 .valueMultiBit = true,
173 },
174 {
175 .type = pckTypeStr,
176 .valueSingleBit = true,
177 .size = true,
178 },
179 {
180 .type = pckTypeU32,
181 .valueMultiBit = true,
182 },
183 {
184 .type = pckTypeU64,
185 .valueMultiBit = true,
186 },
187 {
188 .type = pckTypeStrId,
189 .valueMultiBit = true,
190 },
191
192 // Placeholders for unused types that can be encoded entirely in the tag
193 {0},
194 {0},
195 {0},
196 {0},
197
198 // Formats that require an extra byte to encode
199 {
200 .type = pckTypeTime,
201 .valueMultiBit = true,
202 },
203 {
204 .type = pckTypeBin,
205 .valueSingleBit = true,
206 .size = true,
207 },
208 {
209 .type = pckTypePack,
210 .size = true,
211 },
212 {
213 .type = pckTypeMode,
214 .valueMultiBit = true,
215 },
216 };
217
218 #define PACK_TYPE_MAP_SIZE (sizeof(packTypeMapData) / sizeof(PackTypeMapData))
219
220 /***********************************************************************************************************************************
221 Object types
222 ***********************************************************************************************************************************/
223 typedef struct PackTagStack
224 {
225 PackTypeMap typeMap; // Tag type map
226 unsigned int idLast; // Last id in the container
227 unsigned int nullTotal; // Total nulls since last tag written
228 } PackTagStack;
229
230 struct PackRead
231 {
232 MemContext *memContext; // Mem context
233 IoRead *read; // Read pack from
234 Buffer *buffer; // Buffer containing read data
235 const uint8_t *bufferPtr; // Pointer to buffer
236 size_t bufferPos; // Position in the buffer
237 size_t bufferUsed; // Amount of data in the buffer
238
239 unsigned int tagNextId; // Next tag id
240 PackTypeMap tagNextTypeMap; // Next tag type map
241 uint64_t tagNextValue; // Next tag value
242
243 List *tagStack; // Stack of object/array tags
244 PackTagStack *tagStackTop; // Top tag on the stack
245 };
246
247 struct PackWrite
248 {
249 MemContext *memContext; // Mem context
250 IoWrite *write; // Write pack to
251 Buffer *buffer; // Buffer to contain write data
252
253 List *tagStack; // Stack of object/array tags
254 PackTagStack *tagStackTop; // Top tag on the stack
255 };
256
257 /**********************************************************************************************************************************/
258 // Helper to create common data
259 static PackRead *
pckReadNewInternal(void)260 pckReadNewInternal(void)
261 {
262 FUNCTION_TEST_VOID();
263
264 PackRead *this = NULL;
265
266 MEM_CONTEXT_NEW_BEGIN("PackRead")
267 {
268 this = memNew(sizeof(PackRead));
269
270 *this = (PackRead)
271 {
272 .memContext = MEM_CONTEXT_NEW(),
273 .tagStack = lstNewP(sizeof(PackTagStack)),
274 };
275
276 this->tagStackTop = lstAdd(this->tagStack, &(PackTagStack){.typeMap = pckTypeMapObj});
277 }
278 MEM_CONTEXT_NEW_END();
279
280 FUNCTION_TEST_RETURN(this);
281 }
282
283 PackRead *
pckReadNew(IoRead * read)284 pckReadNew(IoRead *read)
285 {
286 FUNCTION_TEST_BEGIN();
287 FUNCTION_TEST_PARAM(IO_READ, read);
288 FUNCTION_TEST_END();
289
290 ASSERT(read != NULL);
291
292 PackRead *this = pckReadNewInternal();
293
294 MEM_CONTEXT_BEGIN(this->memContext)
295 {
296 this->read = read;
297 this->buffer = bufNew(ioBufferSize());
298 this->bufferPtr = bufPtr(this->buffer);
299 }
300 MEM_CONTEXT_END();
301
302 FUNCTION_TEST_RETURN(this);
303 }
304
305 PackRead *
pckReadNewBuf(const Buffer * buffer)306 pckReadNewBuf(const Buffer *buffer)
307 {
308 FUNCTION_TEST_BEGIN();
309 FUNCTION_TEST_PARAM(BUFFER, buffer);
310 FUNCTION_TEST_END();
311
312 if (buffer == NULL)
313 FUNCTION_TEST_RETURN(NULL);
314
315 PackRead *this = pckReadNewInternal();
316 this->bufferPtr = bufPtrConst(buffer);
317 this->bufferUsed = bufUsed(buffer);
318
319 FUNCTION_TEST_RETURN(this);
320 }
321
322 /***********************************************************************************************************************************
323 Read bytes from the buffer
324
325 IMPORTANT NOTE: To avoid having dyamically created return buffers the current buffer position (this->bufferPos) is stored in the
326 object. Therefore this function should not be used as a parameter in other function calls since the value of this->bufferPos will
327 change.
328 ***********************************************************************************************************************************/
329 static size_t
pckReadBuffer(PackRead * this,size_t size)330 pckReadBuffer(PackRead *this, size_t size)
331 {
332 FUNCTION_TEST_BEGIN();
333 FUNCTION_TEST_PARAM(PACK_READ, this);
334 FUNCTION_TEST_PARAM(SIZE, size);
335 FUNCTION_TEST_END();
336
337 ASSERT(this != NULL);
338
339 size_t remaining = this->bufferUsed - this->bufferPos;
340
341 if (remaining < size)
342 {
343 if (this->read != NULL)
344 {
345 // Nothing can be remaining since each read fetches exactly the number of bytes required
346 ASSERT(remaining == 0);
347 bufUsedZero(this->buffer);
348
349 // Limit the buffer for the next read so we don't read past the end of the pack
350 bufLimitSet(this->buffer, size < bufSizeAlloc(this->buffer) ? size : bufSizeAlloc(this->buffer));
351
352 // Read bytes
353 ioReadSmall(this->read, this->buffer);
354 this->bufferPos = 0;
355 this->bufferUsed = bufUsed(this->buffer);
356 remaining = this->bufferUsed;
357 }
358
359 if (remaining < 1)
360 THROW(FormatError, "unexpected EOF");
361
362 FUNCTION_TEST_RETURN(remaining < size ? remaining : size);
363 }
364
365 FUNCTION_TEST_RETURN(size);
366 }
367
368 /***********************************************************************************************************************************
369 Unpack an unsigned 64-bit integer from base-128 varint encoding
370 ***********************************************************************************************************************************/
371 static uint64_t
pckReadU64Internal(PackRead * this)372 pckReadU64Internal(PackRead *this)
373 {
374 FUNCTION_TEST_BEGIN();
375 FUNCTION_TEST_PARAM(PACK_READ, this);
376 FUNCTION_TEST_END();
377
378 ASSERT(this != NULL);
379
380 uint64_t result = 0;
381 uint8_t byte;
382
383 // Convert bytes from varint-128 encoding to a uint64
384 for (unsigned int bufferIdx = 0; bufferIdx < PACK_UINT64_SIZE_MAX; bufferIdx++)
385 {
386 // Get the next encoded byte
387 pckReadBuffer(this, 1);
388 byte = this->bufferPtr[this->bufferPos];
389
390 // Shift the lower order 7 encoded bits into the uint64 in reverse order
391 result |= (uint64_t)(byte & 0x7f) << (7 * bufferIdx);
392
393 // Increment buffer position to indicate that the byte has been processed
394 this->bufferPos++;
395
396 // Done if the high order bit is not set to indicate more data
397 if (byte < 0x80)
398 break;
399 }
400
401 // By this point all bytes should have been read so error if this is not the case. This could be due to a coding error or
402 // corrupton in the data stream.
403 if (byte >= 0x80)
404 THROW(FormatError, "unterminated base-128 integer");
405
406 FUNCTION_TEST_RETURN(result);
407 }
408
409 /***********************************************************************************************************************************
410 Read next field tag
411 ***********************************************************************************************************************************/
412 static bool
pckReadTagNext(PackRead * this)413 pckReadTagNext(PackRead *this)
414 {
415 FUNCTION_TEST_BEGIN();
416 FUNCTION_TEST_PARAM(PACK_READ, this);
417 FUNCTION_TEST_END();
418
419 ASSERT(this != NULL);
420
421 bool result = false;
422
423 // Read the tag byte
424 pckReadBuffer(this, 1);
425 unsigned int tag = this->bufferPtr[this->bufferPos];
426 this->bufferPos++;
427
428 // If the current container is complete (e.g. object)
429 if (tag == 0)
430 {
431 this->tagNextId = UINT_MAX;
432 }
433 // Else a regular tag
434 else
435 {
436 // Read field type (e.g. int64, string)
437 this->tagNextTypeMap = tag >> 4;
438
439 if (this->tagNextTypeMap == 0xF)
440 this->tagNextTypeMap = (unsigned int)pckReadU64Internal(this) + 0xF;
441
442 CHECK(this->tagNextTypeMap < PACK_TYPE_MAP_SIZE && packTypeMapData[this->tagNextTypeMap].type != 0);
443
444 // If the value can contain multiple bits (e.g. integer)
445 if (packTypeMapData[this->tagNextTypeMap].valueMultiBit)
446 {
447 // If the value is stored following the tag (value > 1 bit)
448 if (tag & 0x8)
449 {
450 // Read low order bits of the field ID delta
451 this->tagNextId = tag & 0x3;
452
453 // Read high order bits of the field ID delta when specified
454 if (tag & 0x4)
455 this->tagNextId |= (unsigned int)pckReadU64Internal(this) << 2;
456
457 // Read value
458 this->tagNextValue = pckReadU64Internal(this);
459 }
460 // Else the value is stored in the tag (value == 1 bit)
461 else
462 {
463 // Read low order bit of the field ID delta
464 this->tagNextId = tag & 0x1;
465
466 // Read high order bits of the field ID delta when specified
467 if (tag & 0x2)
468 this->tagNextId |= (unsigned int)pckReadU64Internal(this) << 1;
469
470 // Read value
471 this->tagNextValue = (tag >> 2) & 0x3;
472 }
473 }
474 // Else the value is a single bit (e.g. boolean)
475 else if (packTypeMapData[this->tagNextTypeMap].valueSingleBit)
476 {
477 // Read low order bits of the field ID delta
478 this->tagNextId = tag & 0x3;
479
480 // Read high order bits of the field ID delta when specified
481 if (tag & 0x4)
482 this->tagNextId |= (unsigned int)pckReadU64Internal(this) << 2;
483
484 // Read value
485 this->tagNextValue = (tag >> 3) & 0x1;
486 }
487 // Else the value is multiple tags (e.g. container)
488 else
489 {
490 // Read low order bits of the field ID delta
491 this->tagNextId = tag & 0x7;
492
493 // Read high order bits of the field ID delta when specified
494 if (tag & 0x8)
495 this->tagNextId |= (unsigned int)pckReadU64Internal(this) << 3;
496
497 // Value length is variable so is stored after the tag
498 this->tagNextValue = 0;
499 }
500
501 // Increment the next tag id
502 this->tagNextId += this->tagStackTop->idLast + 1;
503
504 // Tag was found
505 result = true;
506 }
507
508 FUNCTION_TEST_RETURN(result);
509 }
510
511 /***********************************************************************************************************************************
512 Read field tag
513
514 Some tags and data may be skipped based on the value of the id parameter.
515 ***********************************************************************************************************************************/
516 static uint64_t
pckReadTag(PackRead * this,unsigned int * id,PackTypeMap typeMap,bool peek)517 pckReadTag(PackRead *this, unsigned int *id, PackTypeMap typeMap, bool peek)
518 {
519 FUNCTION_TEST_BEGIN();
520 FUNCTION_TEST_PARAM(PACK_READ, this);
521 FUNCTION_TEST_PARAM_P(UINT, id);
522 FUNCTION_TEST_PARAM(ENUM, typeMap);
523 FUNCTION_TEST_PARAM(BOOL, peek); // Look at the next tag without advancing the field id
524 FUNCTION_TEST_END();
525
526 ASSERT(this != NULL);
527 ASSERT(id != NULL);
528 ASSERT((peek && typeMap == pckTypeMapUnknown) || (!peek && typeMap != pckTypeMapUnknown));
529
530 // Increment the id by one if no id was specified
531 if (*id == 0)
532 {
533 *id = this->tagStackTop->idLast + 1;
534 }
535 // Else check that the id has been incremented
536 else if (*id <= this->tagStackTop->idLast)
537 THROW_FMT(FormatError, "field %u was already read", *id);
538
539 // Search for the requested id
540 do
541 {
542 // Get the next tag if it has not been read yet
543 if (this->tagNextId == 0)
544 pckReadTagNext(this);
545
546 // Return if the id does not exist
547 if (*id < this->tagNextId)
548 {
549 break;
550 }
551 // Else the id exists
552 else if (*id == this->tagNextId)
553 {
554 // When not peeking the next tag (just to see what it is) then error if the type map is not as specified
555 if (!peek)
556 {
557 if (this->tagNextTypeMap != typeMap)
558 {
559 THROW_FMT(
560 FormatError, "field %u is type '%s' but expected '%s'", this->tagNextId,
561 strZ(strIdToStr(packTypeMapData[this->tagNextTypeMap].type)),
562 strZ(strIdToStr(packTypeMapData[typeMap].type)));
563 }
564
565 this->tagStackTop->idLast = this->tagNextId;
566 this->tagNextId = 0;
567 }
568
569 break;
570 }
571
572 // Read data for the field being skipped if this is not the field requested
573 if (packTypeMapData[this->tagNextTypeMap].size && this->tagNextValue != 0)
574 {
575 size_t sizeExpected = (size_t)pckReadU64Internal(this);
576
577 while (sizeExpected != 0)
578 {
579 size_t sizeRead = pckReadBuffer(this, sizeExpected);
580 sizeExpected -= sizeRead;
581 this->bufferPos += sizeRead;
582 }
583 }
584
585 // Increment the last id to the id just read
586 this->tagStackTop->idLast = this->tagNextId;
587
588 // Read tag on the next iteration
589 this->tagNextId = 0;
590 }
591 while (1);
592
593 FUNCTION_TEST_RETURN(this->tagNextValue);
594 }
595
596 /**********************************************************************************************************************************/
597 bool
pckReadNext(PackRead * this)598 pckReadNext(PackRead *this)
599 {
600 FUNCTION_TEST_BEGIN();
601 FUNCTION_TEST_PARAM(PACK_READ, this);
602 FUNCTION_TEST_END();
603
604 ASSERT(this != NULL);
605
606 FUNCTION_TEST_RETURN(pckReadTagNext(this));
607 }
608
609 /**********************************************************************************************************************************/
610 unsigned int
pckReadId(PackRead * this)611 pckReadId(PackRead *this)
612 {
613 FUNCTION_TEST_BEGIN();
614 FUNCTION_TEST_PARAM(PACK_READ, this);
615 FUNCTION_TEST_END();
616
617 ASSERT(this != NULL);
618
619 FUNCTION_TEST_RETURN(this->tagNextId);
620 }
621
622 /**********************************************************************************************************************************/
623 // Internal version of pckReadNull() that does not require a PackIdParam struct. Some functions already have an id variable so
624 // assigning that to a PackIdParam struct and then copying it back is wasteful.
625 static inline bool
pckReadNullInternal(PackRead * this,unsigned int * id)626 pckReadNullInternal(PackRead *this, unsigned int *id)
627 {
628 FUNCTION_TEST_BEGIN();
629 FUNCTION_TEST_PARAM(PACK_READ, this);
630 FUNCTION_TEST_PARAM_P(UINT, id);
631 FUNCTION_TEST_END();
632
633 ASSERT(this != NULL);
634 ASSERT(id != NULL);
635
636 // Read tag at specified id
637 pckReadTag(this, id, pckTypeMapUnknown, true);
638
639 // If the field is NULL then set idLast (to avoid rechecking the same id on the next call) and return true
640 if (*id < this->tagNextId)
641 {
642 this->tagStackTop->idLast = *id;
643 FUNCTION_TEST_RETURN(true);
644 }
645
646 // The field is not NULL
647 FUNCTION_TEST_RETURN(false);
648 }
649
650 bool
pckReadNull(PackRead * this,PackIdParam param)651 pckReadNull(PackRead *this, PackIdParam param)
652 {
653 FUNCTION_TEST_BEGIN();
654 FUNCTION_TEST_PARAM(PACK_READ, this);
655 FUNCTION_TEST_PARAM(UINT, param.id);
656 FUNCTION_TEST_END();
657
658 ASSERT(this != NULL);
659
660 FUNCTION_TEST_RETURN(pckReadNullInternal(this, ¶m.id));
661 }
662
663 /**********************************************************************************************************************************/
664 PackType
pckReadType(PackRead * this)665 pckReadType(PackRead *this)
666 {
667 FUNCTION_TEST_BEGIN();
668 FUNCTION_TEST_PARAM(PACK_READ, this);
669 FUNCTION_TEST_END();
670
671 ASSERT(this != NULL);
672
673 FUNCTION_TEST_RETURN(packTypeMapData[this->tagNextTypeMap].type);
674 }
675
676 /**********************************************************************************************************************************/
677 void
pckReadArrayBegin(PackRead * this,PackIdParam param)678 pckReadArrayBegin(PackRead *this, PackIdParam param)
679 {
680 FUNCTION_TEST_BEGIN();
681 FUNCTION_TEST_PARAM(PACK_READ, this);
682 FUNCTION_TEST_PARAM(UINT, param.id);
683 FUNCTION_TEST_END();
684
685 ASSERT(this != NULL);
686
687 // Read array begin
688 pckReadTag(this, ¶m.id, pckTypeMapArray, false);
689
690 // Add array to the tag stack so IDs can be tracked separately from the parent container
691 this->tagStackTop = lstAdd(this->tagStack, &(PackTagStack){.typeMap = pckTypeMapArray});
692
693 FUNCTION_TEST_RETURN_VOID();
694 }
695
696 void
pckReadArrayEnd(PackRead * this)697 pckReadArrayEnd(PackRead *this)
698 {
699 FUNCTION_TEST_BEGIN();
700 FUNCTION_TEST_PARAM(PACK_READ, this);
701 FUNCTION_TEST_END();
702
703 ASSERT(this != NULL);
704
705 if (lstSize(this->tagStack) == 1 || this->tagStackTop->typeMap != pckTypeMapArray)
706 THROW(FormatError, "not in array");
707
708 // Make sure we are at the end of the array
709 unsigned int id = UINT_MAX - 1;
710 pckReadTag(this, &id, pckTypeMapUnknown, true);
711
712 // Pop array off the stack
713 lstRemoveLast(this->tagStack);
714 this->tagStackTop = lstGetLast(this->tagStack);
715
716 // Reset tagNextId to keep reading
717 this->tagNextId = 0;
718
719 FUNCTION_TEST_RETURN_VOID();
720 }
721
722 /**********************************************************************************************************************************/
723 Buffer *
pckReadBin(PackRead * this,PckReadBinParam param)724 pckReadBin(PackRead *this, PckReadBinParam param)
725 {
726 FUNCTION_TEST_BEGIN();
727 FUNCTION_TEST_PARAM(PACK_READ, this);
728 FUNCTION_TEST_PARAM(UINT, param.id);
729 FUNCTION_TEST_END();
730
731 ASSERT(this != NULL);
732
733 if (pckReadNullInternal(this, ¶m.id))
734 FUNCTION_TEST_RETURN(NULL);
735
736 Buffer *result = NULL;
737
738 // If buffer size > 0
739 if (pckReadTag(this, ¶m.id, pckTypeMapBin, false))
740 {
741 // Get the buffer size
742 result = bufNew((size_t)pckReadU64Internal(this));
743
744 // Read the buffer out in chunks
745 while (bufUsed(result) < bufSize(result))
746 {
747 size_t size = pckReadBuffer(this, bufRemains(result));
748 bufCatC(result, this->bufferPtr, this->bufferPos, size);
749 this->bufferPos += size;
750 }
751 }
752 // Else return a zero-sized buffer
753 else
754 result = bufNew(0);
755
756 FUNCTION_TEST_RETURN(result);
757 }
758
759 /**********************************************************************************************************************************/
760 bool
pckReadBool(PackRead * this,PckReadBoolParam param)761 pckReadBool(PackRead *this, PckReadBoolParam param)
762 {
763 FUNCTION_TEST_BEGIN();
764 FUNCTION_TEST_PARAM(PACK_READ, this);
765 FUNCTION_TEST_PARAM(UINT, param.id);
766 FUNCTION_TEST_PARAM(BOOL, param.defaultValue);
767 FUNCTION_TEST_END();
768
769 ASSERT(this != NULL);
770
771 if (pckReadNullInternal(this, ¶m.id))
772 FUNCTION_TEST_RETURN(param.defaultValue);
773
774 FUNCTION_TEST_RETURN(pckReadTag(this, ¶m.id, pckTypeMapBool, false));
775 }
776
777 /**********************************************************************************************************************************/
778 int32_t
pckReadI32(PackRead * this,PckReadI32Param param)779 pckReadI32(PackRead *this, PckReadI32Param param)
780 {
781 FUNCTION_TEST_BEGIN();
782 FUNCTION_TEST_PARAM(PACK_READ, this);
783 FUNCTION_TEST_PARAM(UINT, param.id);
784 FUNCTION_TEST_PARAM(INT, param.defaultValue);
785 FUNCTION_TEST_END();
786
787 ASSERT(this != NULL);
788
789 if (pckReadNullInternal(this, ¶m.id))
790 FUNCTION_TEST_RETURN(param.defaultValue);
791
792 FUNCTION_TEST_RETURN(cvtInt32FromZigZag((uint32_t)pckReadTag(this, ¶m.id, pckTypeMapI32, false)));
793 }
794
795 /**********************************************************************************************************************************/
796 int64_t
pckReadI64(PackRead * this,PckReadI64Param param)797 pckReadI64(PackRead *this, PckReadI64Param param)
798 {
799 FUNCTION_TEST_BEGIN();
800 FUNCTION_TEST_PARAM(PACK_READ, this);
801 FUNCTION_TEST_PARAM(UINT, param.id);
802 FUNCTION_TEST_PARAM(INT64, param.defaultValue);
803 FUNCTION_TEST_END();
804
805 ASSERT(this != NULL);
806
807 if (pckReadNullInternal(this, ¶m.id))
808 FUNCTION_TEST_RETURN(param.defaultValue);
809
810 FUNCTION_TEST_RETURN(cvtInt64FromZigZag(pckReadTag(this, ¶m.id, pckTypeMapI64, false)));
811 }
812
813 /**********************************************************************************************************************************/
814 mode_t
pckReadMode(PackRead * this,PckReadModeParam param)815 pckReadMode(PackRead *this, PckReadModeParam param)
816 {
817 FUNCTION_TEST_BEGIN();
818 FUNCTION_TEST_PARAM(PACK_READ, this);
819 FUNCTION_TEST_PARAM(UINT, param.id);
820 FUNCTION_TEST_PARAM(MODE, param.defaultValue);
821 FUNCTION_TEST_END();
822
823 ASSERT(this != NULL);
824
825 if (pckReadNullInternal(this, ¶m.id))
826 FUNCTION_TEST_RETURN(param.defaultValue);
827
828 FUNCTION_TEST_RETURN((mode_t)pckReadTag(this, ¶m.id, pckTypeMapMode, false));
829 }
830
831 /**********************************************************************************************************************************/
832 void
pckReadObjBegin(PackRead * this,PackIdParam param)833 pckReadObjBegin(PackRead *this, PackIdParam param)
834 {
835 FUNCTION_TEST_BEGIN();
836 FUNCTION_TEST_PARAM(PACK_READ, this);
837 FUNCTION_TEST_PARAM(UINT, param.id);
838 FUNCTION_TEST_END();
839
840 ASSERT(this != NULL);
841
842 // Read object begin
843 pckReadTag(this, ¶m.id, pckTypeMapObj, false);
844
845 // Add object to the tag stack so IDs can be tracked separately from the parent container
846 this->tagStackTop = lstAdd(this->tagStack, &(PackTagStack){.typeMap = pckTypeMapObj});
847
848 FUNCTION_TEST_RETURN_VOID();
849 }
850
851 void
pckReadObjEnd(PackRead * this)852 pckReadObjEnd(PackRead *this)
853 {
854 FUNCTION_TEST_BEGIN();
855 FUNCTION_TEST_PARAM(PACK_READ, this);
856 FUNCTION_TEST_END();
857
858 ASSERT(this != NULL);
859
860 if (lstSize(this->tagStack) == 1 || ((PackTagStack *)lstGetLast(this->tagStack))->typeMap != pckTypeMapObj)
861 THROW(FormatError, "not in object");
862
863 // Make sure we are at the end of the object
864 unsigned id = UINT_MAX - 1;
865 pckReadTag(this, &id, pckTypeMapUnknown, true);
866
867 // Pop object off the stack
868 lstRemoveLast(this->tagStack);
869 this->tagStackTop = lstGetLast(this->tagStack);
870
871 // Reset tagNextId to keep reading
872 this->tagNextId = 0;
873
874 FUNCTION_TEST_RETURN_VOID();
875 }
876
877 /**********************************************************************************************************************************/
878 PackRead *
pckReadPack(PackRead * this,PckReadPackParam param)879 pckReadPack(PackRead *this, PckReadPackParam param)
880 {
881 FUNCTION_TEST_BEGIN();
882 FUNCTION_TEST_PARAM(PACK_READ, this);
883 FUNCTION_TEST_PARAM(UINT, param.id);
884 FUNCTION_TEST_END();
885
886 Buffer *const buffer = pckReadPackBuf(this, param);
887 PackRead *const result = pckReadNewBuf(buffer);
888
889 if (result != NULL)
890 bufMove(buffer, result->memContext);
891
892 FUNCTION_TEST_RETURN(result);
893 }
894
895 Buffer *
pckReadPackBuf(PackRead * this,PckReadPackParam param)896 pckReadPackBuf(PackRead *this, PckReadPackParam param)
897 {
898 FUNCTION_TEST_BEGIN();
899 FUNCTION_TEST_PARAM(PACK_READ, this);
900 FUNCTION_TEST_PARAM(UINT, param.id);
901 FUNCTION_TEST_END();
902
903 ASSERT(this != NULL);
904
905 if (pckReadNullInternal(this, ¶m.id))
906 FUNCTION_TEST_RETURN(NULL);
907
908 // Read the tag
909 pckReadTag(this, ¶m.id, pckTypeMapPack, false);
910
911 // Get the pack size
912 Buffer *result = bufNew((size_t)pckReadU64Internal(this));
913
914 // Read the pack out in chunks
915 while (bufUsed(result) < bufSize(result))
916 {
917 size_t size = pckReadBuffer(this, bufRemains(result));
918 bufCatC(result, this->bufferPtr, this->bufferPos, size);
919 this->bufferPos += size;
920 }
921
922 FUNCTION_TEST_RETURN(result);
923 }
924
925 /**********************************************************************************************************************************/
926 void *
pckReadPtr(PackRead * this,PckReadPtrParam param)927 pckReadPtr(PackRead *this, PckReadPtrParam param)
928 {
929 FUNCTION_TEST_BEGIN();
930 FUNCTION_TEST_PARAM(PACK_READ, this);
931 FUNCTION_TEST_PARAM(UINT, param.id);
932 FUNCTION_TEST_END();
933
934 ASSERT(this != NULL);
935
936 if (pckReadNullInternal(this, ¶m.id))
937 FUNCTION_TEST_RETURN(NULL);
938
939 FUNCTION_TEST_RETURN((void *)(uintptr_t)pckReadTag(this, ¶m.id, pckTypeMapPtr, false));
940 }
941
942 /**********************************************************************************************************************************/
943 String *
pckReadStr(PackRead * this,PckReadStrParam param)944 pckReadStr(PackRead *this, PckReadStrParam param)
945 {
946 FUNCTION_TEST_BEGIN();
947 FUNCTION_TEST_PARAM(PACK_READ, this);
948 FUNCTION_TEST_PARAM(UINT, param.id);
949 FUNCTION_TEST_PARAM(STRING, param.defaultValue);
950 FUNCTION_TEST_END();
951
952 ASSERT(this != NULL);
953
954 if (pckReadNullInternal(this, ¶m.id))
955 FUNCTION_TEST_RETURN(strDup(param.defaultValue));
956
957 String *result = NULL;
958
959 // If string size > 0
960 if (pckReadTag(this, ¶m.id, pckTypeMapStr, false))
961 {
962 // Read the string size
963 size_t sizeExpected = (size_t)pckReadU64Internal(this);
964
965 // Read the string out in chunks
966 result = strNew();
967
968 while (strSize(result) != sizeExpected)
969 {
970 size_t sizeRead = pckReadBuffer(this, sizeExpected - strSize(result));
971 strCatZN(result, (char *)this->bufferPtr + this->bufferPos, sizeRead);
972 this->bufferPos += sizeRead;
973 }
974 }
975 // Else return an empty string
976 else
977 result = strNew();
978
979 FUNCTION_TEST_RETURN(result);
980 }
981
982 /**********************************************************************************************************************************/
983 StringId
pckReadStrId(PackRead * this,PckReadStrIdParam param)984 pckReadStrId(PackRead *this, PckReadStrIdParam param)
985 {
986 FUNCTION_TEST_BEGIN();
987 FUNCTION_TEST_PARAM(PACK_READ, this);
988 FUNCTION_TEST_PARAM(UINT, param.id);
989 FUNCTION_TEST_PARAM(UINT64, param.defaultValue);
990 FUNCTION_TEST_END();
991
992 ASSERT(this != NULL);
993
994 if (pckReadNullInternal(this, ¶m.id))
995 FUNCTION_TEST_RETURN(param.defaultValue);
996
997 FUNCTION_TEST_RETURN(pckReadTag(this, ¶m.id, pckTypeMapStrId, false));
998 }
999
1000 /**********************************************************************************************************************************/
1001 StringList *
pckReadStrLst(PackRead * const this,PckReadStrLstParam param)1002 pckReadStrLst(PackRead *const this, PckReadStrLstParam param)
1003 {
1004 FUNCTION_TEST_BEGIN();
1005 FUNCTION_TEST_PARAM(PACK_READ, this);
1006 FUNCTION_TEST_PARAM(UINT, param.id);
1007 FUNCTION_TEST_END();
1008
1009 ASSERT(this != NULL);
1010
1011 if (pckReadNullInternal(this, ¶m.id))
1012 FUNCTION_TEST_RETURN(NULL);
1013
1014 pckReadArrayBeginP(this, .id = param.id);
1015
1016 StringList *const result = strLstNew();
1017
1018 while (!pckReadNullP(this))
1019 strLstAdd(result, pckReadStrP(this));
1020
1021 pckReadArrayEndP(this);
1022
1023 FUNCTION_TEST_RETURN(result);
1024 }
1025
1026 /**********************************************************************************************************************************/
1027 time_t
pckReadTime(PackRead * this,PckReadTimeParam param)1028 pckReadTime(PackRead *this, PckReadTimeParam param)
1029 {
1030 FUNCTION_TEST_BEGIN();
1031 FUNCTION_TEST_PARAM(PACK_READ, this);
1032 FUNCTION_TEST_PARAM(UINT, param.id);
1033 FUNCTION_TEST_PARAM(TIME, param.defaultValue);
1034 FUNCTION_TEST_END();
1035
1036 ASSERT(this != NULL);
1037
1038 if (pckReadNullInternal(this, ¶m.id))
1039 FUNCTION_TEST_RETURN(param.defaultValue);
1040
1041 FUNCTION_TEST_RETURN((time_t)cvtInt64FromZigZag(pckReadTag(this, ¶m.id, pckTypeMapTime, false)));
1042 }
1043
1044 /**********************************************************************************************************************************/
1045 uint32_t
pckReadU32(PackRead * this,PckReadU32Param param)1046 pckReadU32(PackRead *this, PckReadU32Param param)
1047 {
1048 FUNCTION_TEST_BEGIN();
1049 FUNCTION_TEST_PARAM(PACK_READ, this);
1050 FUNCTION_TEST_PARAM(UINT, param.id);
1051 FUNCTION_TEST_PARAM(UINT32, param.defaultValue);
1052 FUNCTION_TEST_END();
1053
1054 ASSERT(this != NULL);
1055
1056 if (pckReadNullInternal(this, ¶m.id))
1057 FUNCTION_TEST_RETURN(param.defaultValue);
1058
1059 FUNCTION_TEST_RETURN((uint32_t)pckReadTag(this, ¶m.id, pckTypeMapU32, false));
1060 }
1061
1062 /**********************************************************************************************************************************/
1063 uint64_t
pckReadU64(PackRead * this,PckReadU64Param param)1064 pckReadU64(PackRead *this, PckReadU64Param param)
1065 {
1066 FUNCTION_TEST_BEGIN();
1067 FUNCTION_TEST_PARAM(PACK_READ, this);
1068 FUNCTION_TEST_PARAM(UINT, param.id);
1069 FUNCTION_TEST_PARAM(UINT64, param.defaultValue);
1070 FUNCTION_TEST_END();
1071
1072 ASSERT(this != NULL);
1073
1074 if (pckReadNullInternal(this, ¶m.id))
1075 FUNCTION_TEST_RETURN(param.defaultValue);
1076
1077 FUNCTION_TEST_RETURN(pckReadTag(this, ¶m.id, pckTypeMapU64, false));
1078 }
1079
1080 /**********************************************************************************************************************************/
1081 void
pckReadEnd(PackRead * this)1082 pckReadEnd(PackRead *this)
1083 {
1084 FUNCTION_TEST_BEGIN();
1085 FUNCTION_TEST_PARAM(PACK_READ, this);
1086 FUNCTION_TEST_END();
1087
1088 ASSERT(this != NULL);
1089
1090 // Read object end markers
1091 while (!lstEmpty(this->tagStack))
1092 {
1093 // Make sure we are at the end of the container
1094 unsigned int id = UINT_MAX - 1;
1095 pckReadTag(this, &id, pckTypeMapUnknown, true);
1096
1097 // Remove from stack
1098 lstRemoveLast(this->tagStack);
1099 }
1100
1101 this->tagStackTop = NULL;
1102
1103 FUNCTION_TEST_RETURN_VOID();
1104 }
1105
1106 /**********************************************************************************************************************************/
1107 String *
pckReadToLog(const PackRead * this)1108 pckReadToLog(const PackRead *this)
1109 {
1110 return strNewFmt(
1111 "{depth: %u, idLast: %u, tagNextId: %u, tagNextType: %u, tagNextValue %" PRIu64 "}", lstSize(this->tagStack),
1112 this->tagStackTop->idLast, this->tagNextId, this->tagNextTypeMap, this->tagNextValue);
1113 }
1114
1115 /**********************************************************************************************************************************/
1116 // Helper to create common data
1117 static PackWrite *
pckWriteNewInternal(void)1118 pckWriteNewInternal(void)
1119 {
1120 FUNCTION_TEST_VOID();
1121
1122 PackWrite *this = NULL;
1123
1124 MEM_CONTEXT_NEW_BEGIN("PackWrite")
1125 {
1126 this = memNew(sizeof(PackWrite));
1127
1128 *this = (PackWrite)
1129 {
1130 .memContext = MEM_CONTEXT_NEW(),
1131 .tagStack = lstNewP(sizeof(PackTagStack)),
1132 };
1133
1134 this->tagStackTop = lstAdd(this->tagStack, &(PackTagStack){.typeMap = pckTypeMapObj});
1135 }
1136 MEM_CONTEXT_NEW_END();
1137
1138 FUNCTION_TEST_RETURN(this);
1139 }
1140
1141 PackWrite *
pckWriteNew(IoWrite * write)1142 pckWriteNew(IoWrite *write)
1143 {
1144 FUNCTION_TEST_BEGIN();
1145 FUNCTION_TEST_PARAM(IO_WRITE, write);
1146 FUNCTION_TEST_END();
1147
1148 ASSERT(write != NULL);
1149
1150 PackWrite *this = pckWriteNewInternal();
1151
1152 MEM_CONTEXT_BEGIN(this->memContext)
1153 {
1154 this->write = write;
1155 this->buffer = bufNew(ioBufferSize());
1156 }
1157 MEM_CONTEXT_END();
1158
1159 FUNCTION_TEST_RETURN(this);
1160 }
1161
1162 PackWrite *
pckWriteNewBuf(Buffer * buffer)1163 pckWriteNewBuf(Buffer *buffer)
1164 {
1165 FUNCTION_TEST_BEGIN();
1166 FUNCTION_TEST_PARAM(BUFFER, buffer);
1167 FUNCTION_TEST_END();
1168
1169 ASSERT(buffer != NULL);
1170
1171 PackWrite *this = pckWriteNewInternal();
1172 this->buffer = buffer;
1173
1174 FUNCTION_TEST_RETURN(this);
1175 }
1176
1177 /***********************************************************************************************************************************
1178 Write to io or buffer
1179 ***********************************************************************************************************************************/
1180 static void
pckWriteBuffer(PackWrite * this,const Buffer * buffer)1181 pckWriteBuffer(PackWrite *this, const Buffer *buffer)
1182 {
1183 FUNCTION_TEST_BEGIN();
1184 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1185 FUNCTION_TEST_PARAM(BUFFER, buffer);
1186 FUNCTION_TEST_END();
1187
1188 ASSERT(this != NULL);
1189
1190 // If writing directly to a buffer
1191 if (this->write == NULL)
1192 {
1193 // Add space in the buffer to write and add extra space so future writes won't always need to resize the buffer
1194 if (bufRemains(this->buffer) < bufUsed(buffer))
1195 bufResize(this->buffer, (bufSizeAlloc(this->buffer) + bufUsed(buffer)) + PACK_EXTRA_MIN);
1196
1197 // Write to the buffer
1198 bufCat(this->buffer, buffer);
1199 }
1200 // Else writing to io
1201 else
1202 {
1203 // If there's enough space to write to the internal buffer then do that
1204 if (bufRemains(this->buffer) >= bufUsed(buffer))
1205 bufCat(this->buffer, buffer);
1206 else
1207 {
1208 // Flush the internal buffer if it has data
1209 if (!bufEmpty(this->buffer))
1210 {
1211 ioWrite(this->write, this->buffer);
1212 bufUsedZero(this->buffer);
1213 }
1214
1215 // If there's enough space to write to the internal buffer then do that
1216 if (bufRemains(this->buffer) >= bufUsed(buffer))
1217 {
1218 bufCat(this->buffer, buffer);
1219 }
1220 // Else write directly to io
1221 else
1222 ioWrite(this->write, buffer);
1223 }
1224 }
1225
1226 FUNCTION_TEST_RETURN_VOID();
1227 }
1228
1229 /***********************************************************************************************************************************
1230 Pack an unsigned 64-bit integer to base-128 varint encoding
1231 ***********************************************************************************************************************************/
1232 static void
pckWriteU64Internal(PackWrite * this,uint64_t value)1233 pckWriteU64Internal(PackWrite *this, uint64_t value)
1234 {
1235 FUNCTION_TEST_BEGIN();
1236 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1237 FUNCTION_TEST_PARAM(UINT64, value);
1238 FUNCTION_TEST_END();
1239
1240 ASSERT(this != NULL);
1241
1242 unsigned char buffer[PACK_UINT64_SIZE_MAX];
1243 size_t size = 0;
1244
1245 // Convert uint64 to varint-128 encloding. Keep writing out bytes while the remaining value is greater than 7 bits.
1246 while (value >= 0x80)
1247 {
1248 // Encode the lower order 7 bits, adding the continuation bit to indicate there is more data
1249 buffer[size] = (unsigned char)value | 0x80;
1250
1251 // Shift the value to remove bits that have been encoded
1252 value >>= 7;
1253
1254 // Keep track of size so we know how many bytes to write out
1255 size++;
1256 }
1257
1258 // Encode the last 7 bits of value
1259 buffer[size] = (unsigned char)value;
1260
1261 // Write encoded bytes to the buffer
1262 pckWriteBuffer(this, BUF(buffer, size + 1));
1263
1264 FUNCTION_TEST_RETURN_VOID();
1265 }
1266
1267 /***********************************************************************************************************************************
1268 Write field tag
1269 ***********************************************************************************************************************************/
1270 static void
pckWriteTag(PackWrite * this,PackTypeMap typeMap,unsigned int id,uint64_t value)1271 pckWriteTag(PackWrite *this, PackTypeMap typeMap, unsigned int id, uint64_t value)
1272 {
1273 FUNCTION_TEST_BEGIN();
1274 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1275 FUNCTION_TEST_PARAM(ENUM, typeMap);
1276 FUNCTION_TEST_PARAM(UINT, id);
1277 FUNCTION_TEST_PARAM(UINT64, value);
1278 FUNCTION_TEST_END();
1279
1280 ASSERT(this != NULL);
1281
1282 // If id is not specified then add one to previous tag (and include all NULLs)
1283 if (id == 0)
1284 {
1285 id = this->tagStackTop->idLast + this->tagStackTop->nullTotal + 1;
1286 }
1287 // Else the id must be greater than the last one
1288 else
1289 CHECK(id > this->tagStackTop->idLast);
1290
1291 // Clear NULLs now that field id has been calculated
1292 this->tagStackTop->nullTotal = 0;
1293
1294 // Calculate field ID delta
1295 unsigned int tagId = id - this->tagStackTop->idLast - 1;
1296
1297 // Write field type map (e.g. int64, string)
1298 uint64_t tag = typeMap >= 0xF ? 0xF0 : typeMap << 4;
1299
1300 // If the value can contain multiple bits (e.g. integer)
1301 if (packTypeMapData[typeMap].valueMultiBit)
1302 {
1303 // If the value is stored in the tag (value == 1 bit)
1304 if (value < 2)
1305 {
1306 // Write low order bit of the value
1307 tag |= (value & 0x1) << 2;
1308 value >>= 1;
1309
1310 // Write low order bit of the field ID delta
1311 tag |= tagId & 0x1;
1312 tagId >>= 1;
1313
1314 // Set bit to indicate that high order bits of the field ID delta are be written after the tag
1315 if (tagId > 0)
1316 tag |= 0x2;
1317 }
1318 // Else the value is stored following the tag (value > 1 bit)
1319 else
1320 {
1321 // Set bit to indicate that the value is written after the tag
1322 tag |= 0x8;
1323
1324 // Write low order bits of the field ID delta
1325 tag |= tagId & 0x3;
1326 tagId >>= 2;
1327
1328 // Set bit to indicate that high order bits of the field ID delta are be written after the tag
1329 if (tagId > 0)
1330 tag |= 0x4;
1331 }
1332 }
1333 // Else the value is a single bit (e.g. boolean)
1334 else if (packTypeMapData[typeMap].valueSingleBit)
1335 {
1336 // Write value
1337 tag |= (value & 0x1) << 3;
1338 value >>= 1;
1339
1340 // Write low order bits of the field ID delta
1341 tag |= tagId & 0x3;
1342 tagId >>= 2;
1343
1344 // Set bit to indicate that high order bits of the field ID delta are be written after the tag
1345 if (tagId > 0)
1346 tag |= 0x4;
1347 }
1348 else
1349 {
1350 // No value expected
1351 ASSERT(value == 0);
1352
1353 // Write low order bits of the field ID delta
1354 tag |= tagId & 0x7;
1355 tagId >>= 3;
1356
1357 // Set bit to indicate that high order bits of the field ID delta must be written after the tag
1358 if (tagId > 0)
1359 tag |= 0x8;
1360 }
1361
1362 // Write tag
1363 uint8_t tagByte = (uint8_t)tag;
1364 pckWriteBuffer(this, BUF(&tagByte, 1));
1365
1366 // Write remaining type map
1367 if (typeMap >= 0xF)
1368 pckWriteU64Internal(this, typeMap - 0xF);
1369
1370 // Write low order bits of the field ID delta
1371 if (tagId > 0)
1372 pckWriteU64Internal(this, tagId);
1373
1374 // Write low order bits of the value
1375 if (value > 0)
1376 pckWriteU64Internal(this, value);
1377
1378 // Set last field id
1379 this->tagStackTop->idLast = id;
1380
1381 FUNCTION_TEST_RETURN_VOID();
1382 }
1383
1384 /***********************************************************************************************************************************
1385 Write a default as NULL (missing)
1386 ***********************************************************************************************************************************/
1387 static inline bool
pckWriteDefaultNull(PackWrite * this,bool defaultWrite,bool defaultEqual)1388 pckWriteDefaultNull(PackWrite *this, bool defaultWrite, bool defaultEqual)
1389 {
1390 FUNCTION_TEST_BEGIN();
1391 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1392 FUNCTION_TEST_PARAM(BOOL, defaultWrite);
1393 FUNCTION_TEST_PARAM(BOOL, defaultEqual);
1394 FUNCTION_TEST_END();
1395
1396 ASSERT(this != NULL);
1397
1398 // Write a NULL if not forcing the default to be written and the value passed equals the default
1399 if (!defaultWrite && defaultEqual)
1400 {
1401 this->tagStackTop->nullTotal++;
1402 FUNCTION_TEST_RETURN(true);
1403 }
1404
1405 // Let the caller know that it should write the value
1406 FUNCTION_TEST_RETURN(false);
1407 }
1408
1409 /**********************************************************************************************************************************/
1410 PackWrite *
pckWriteNull(PackWrite * this)1411 pckWriteNull(PackWrite *this)
1412 {
1413 FUNCTION_TEST_BEGIN();
1414 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1415 FUNCTION_TEST_END();
1416
1417 this->tagStackTop->nullTotal++;
1418
1419 FUNCTION_TEST_RETURN(this);
1420 }
1421
1422 /**********************************************************************************************************************************/
1423 PackWrite *
pckWriteArrayBegin(PackWrite * this,PackIdParam param)1424 pckWriteArrayBegin(PackWrite *this, PackIdParam param)
1425 {
1426 FUNCTION_TEST_BEGIN();
1427 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1428 FUNCTION_TEST_PARAM(UINT, param.id);
1429 FUNCTION_TEST_END();
1430
1431 ASSERT(this != NULL);
1432
1433 // Write the array tag
1434 pckWriteTag(this, pckTypeMapArray, param.id, 0);
1435
1436 // Add array to the tag stack so IDs can be tracked separately from the parent container
1437 this->tagStackTop = lstAdd(this->tagStack, &(PackTagStack){.typeMap = pckTypeMapArray});
1438
1439 FUNCTION_TEST_RETURN(this);
1440 }
1441
1442 PackWrite *
pckWriteArrayEnd(PackWrite * this)1443 pckWriteArrayEnd(PackWrite *this)
1444 {
1445 FUNCTION_TEST_BEGIN();
1446 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1447 FUNCTION_TEST_END();
1448
1449 ASSERT(this != NULL);
1450 ASSERT(lstSize(this->tagStack) != 1);
1451 ASSERT(((PackTagStack *)lstGetLast(this->tagStack))->typeMap == pckTypeMapArray);
1452
1453 // Write end of array tag
1454 pckWriteU64Internal(this, 0);
1455
1456 // Pop array off the stack to revert to ID tracking for the prior container
1457 lstRemoveLast(this->tagStack);
1458 this->tagStackTop = lstGetLast(this->tagStack);
1459
1460 FUNCTION_TEST_RETURN(this);
1461 }
1462
1463 /**********************************************************************************************************************************/
1464 PackWrite *
pckWriteBin(PackWrite * this,const Buffer * value,PckWriteBinParam param)1465 pckWriteBin(PackWrite *this, const Buffer *value, PckWriteBinParam param)
1466 {
1467 FUNCTION_TEST_BEGIN();
1468 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1469 FUNCTION_TEST_PARAM(BUFFER, value);
1470 FUNCTION_TEST_PARAM(UINT, param.id);
1471 FUNCTION_TEST_END();
1472
1473 ASSERT(this != NULL);
1474
1475 if (!pckWriteDefaultNull(this, false, value == NULL))
1476 {
1477 ASSERT(value != NULL);
1478
1479 // Write buffer size if > 0
1480 pckWriteTag(this, pckTypeMapBin, param.id, !bufEmpty(value));
1481
1482 // Write buffer data if size > 0
1483 if (!bufEmpty(value))
1484 {
1485 pckWriteU64Internal(this, bufUsed(value));
1486 pckWriteBuffer(this, value);
1487 }
1488 }
1489
1490 FUNCTION_TEST_RETURN(this);
1491 }
1492
1493 /**********************************************************************************************************************************/
1494 PackWrite *
pckWriteBool(PackWrite * this,bool value,PckWriteBoolParam param)1495 pckWriteBool(PackWrite *this, bool value, PckWriteBoolParam param)
1496 {
1497 FUNCTION_TEST_BEGIN();
1498 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1499 FUNCTION_TEST_PARAM(BOOL, value);
1500 FUNCTION_TEST_PARAM(UINT, param.id);
1501 FUNCTION_TEST_PARAM(BOOL, param.defaultWrite);
1502 FUNCTION_TEST_PARAM(BOOL, param.defaultValue);
1503 FUNCTION_TEST_END();
1504
1505 ASSERT(this != NULL);
1506
1507 if (!pckWriteDefaultNull(this, param.defaultWrite, value == param.defaultValue))
1508 pckWriteTag(this, pckTypeMapBool, param.id, value);
1509
1510 FUNCTION_TEST_RETURN(this);
1511 }
1512
1513 /**********************************************************************************************************************************/
1514 PackWrite *
pckWriteI32(PackWrite * this,int32_t value,PckWriteI32Param param)1515 pckWriteI32(PackWrite *this, int32_t value, PckWriteI32Param param)
1516 {
1517 FUNCTION_TEST_BEGIN();
1518 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1519 FUNCTION_TEST_PARAM(INT, value);
1520 FUNCTION_TEST_PARAM(UINT, param.id);
1521 FUNCTION_TEST_PARAM(BOOL, param.defaultWrite);
1522 FUNCTION_TEST_PARAM(INT, param.defaultValue);
1523 FUNCTION_TEST_END();
1524
1525 ASSERT(this != NULL);
1526
1527 if (!pckWriteDefaultNull(this, param.defaultWrite, value == param.defaultValue))
1528 pckWriteTag(this, pckTypeMapI32, param.id, cvtInt32ToZigZag(value));
1529
1530 FUNCTION_TEST_RETURN(this);
1531 }
1532
1533 /**********************************************************************************************************************************/
1534 PackWrite *
pckWriteI64(PackWrite * this,int64_t value,PckWriteI64Param param)1535 pckWriteI64(PackWrite *this, int64_t value, PckWriteI64Param param)
1536 {
1537 FUNCTION_TEST_BEGIN();
1538 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1539 FUNCTION_TEST_PARAM(INT64, value);
1540 FUNCTION_TEST_PARAM(UINT, param.id);
1541 FUNCTION_TEST_PARAM(BOOL, param.defaultWrite);
1542 FUNCTION_TEST_PARAM(INT64, param.defaultValue);
1543 FUNCTION_TEST_END();
1544
1545 ASSERT(this != NULL);
1546
1547 if (!pckWriteDefaultNull(this, param.defaultWrite, value == param.defaultValue))
1548 pckWriteTag(this, pckTypeMapI64, param.id, cvtInt64ToZigZag(value));
1549
1550 FUNCTION_TEST_RETURN(this);
1551 }
1552
1553 /**********************************************************************************************************************************/
1554 PackWrite *
pckWriteMode(PackWrite * this,mode_t value,PckWriteModeParam param)1555 pckWriteMode(PackWrite *this, mode_t value, PckWriteModeParam param)
1556 {
1557 FUNCTION_TEST_BEGIN();
1558 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1559 FUNCTION_TEST_PARAM(UINT32, value);
1560 FUNCTION_TEST_PARAM(UINT, param.id);
1561 FUNCTION_TEST_PARAM(BOOL, param.defaultWrite);
1562 FUNCTION_TEST_PARAM(MODE, param.defaultValue);
1563 FUNCTION_TEST_END();
1564
1565 ASSERT(this != NULL);
1566
1567 if (!pckWriteDefaultNull(this, param.defaultWrite, value == param.defaultValue))
1568 pckWriteTag(this, pckTypeMapMode, param.id, value);
1569
1570 FUNCTION_TEST_RETURN(this);
1571 }
1572
1573 /**********************************************************************************************************************************/
1574 PackWrite *
pckWriteObjBegin(PackWrite * this,PackIdParam param)1575 pckWriteObjBegin(PackWrite *this, PackIdParam param)
1576 {
1577 FUNCTION_TEST_BEGIN();
1578 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1579 FUNCTION_TEST_PARAM(UINT, param.id);
1580 FUNCTION_TEST_END();
1581
1582 ASSERT(this != NULL);
1583
1584 // Write the object tag
1585 pckWriteTag(this, pckTypeMapObj, param.id, 0);
1586
1587 // Add object to the tag stack so IDs can be tracked separately from the parent container
1588 this->tagStackTop = lstAdd(this->tagStack, &(PackTagStack){.typeMap = pckTypeMapObj});
1589
1590 FUNCTION_TEST_RETURN(this);
1591 }
1592
1593 PackWrite *
pckWriteObjEnd(PackWrite * this)1594 pckWriteObjEnd(PackWrite *this)
1595 {
1596 FUNCTION_TEST_BEGIN();
1597 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1598 FUNCTION_TEST_END();
1599
1600 ASSERT(this != NULL);
1601 ASSERT(lstSize(this->tagStack) != 1);
1602 ASSERT(((PackTagStack *)lstGetLast(this->tagStack))->typeMap == pckTypeMapObj);
1603
1604 // Write end of object tag
1605 pckWriteU64Internal(this, 0);
1606
1607 // Pop object off the stack to revert to ID tracking for the prior container
1608 lstRemoveLast(this->tagStack);
1609 this->tagStackTop = lstGetLast(this->tagStack);
1610
1611 FUNCTION_TEST_RETURN(this);
1612 }
1613
1614 /**********************************************************************************************************************************/
1615 PackWrite *
pckWritePack(PackWrite * this,const PackWrite * value,PckWritePackParam param)1616 pckWritePack(PackWrite *this, const PackWrite *value, PckWritePackParam param)
1617 {
1618 FUNCTION_TEST_BEGIN();
1619 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1620 FUNCTION_TEST_PARAM(PACK_WRITE, value);
1621 FUNCTION_TEST_PARAM(UINT, param.id);
1622 FUNCTION_TEST_PARAM(BOOL, param.defaultWrite);
1623 FUNCTION_TEST_END();
1624
1625 ASSERT(this != NULL);
1626
1627 if (!pckWriteDefaultNull(this, false, value == NULL))
1628 {
1629 ASSERT(value != NULL);
1630
1631 // Write pack size
1632 pckWriteTag(this, pckTypeMapPack, param.id, 0);
1633
1634 // Write pack data
1635 const Buffer *packBuffer = pckWriteBuf(value);
1636
1637 pckWriteU64Internal(this, bufUsed(packBuffer));
1638 pckWriteBuffer(this, packBuffer);
1639 }
1640
1641 FUNCTION_TEST_RETURN(this);
1642 }
1643
1644 /**********************************************************************************************************************************/
1645 PackWrite *
pckWritePtr(PackWrite * this,const void * value,PckWritePtrParam param)1646 pckWritePtr(PackWrite *this, const void *value, PckWritePtrParam param)
1647 {
1648 FUNCTION_TEST_BEGIN();
1649 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1650 FUNCTION_TEST_PARAM_P(VOID, value);
1651 FUNCTION_TEST_PARAM(UINT, param.id);
1652 FUNCTION_TEST_PARAM(BOOL, param.defaultWrite);
1653 FUNCTION_TEST_END();
1654
1655 ASSERT(this != NULL);
1656
1657 if (!pckWriteDefaultNull(this, param.defaultWrite, value == NULL))
1658 pckWriteTag(this, pckTypeMapPtr, param.id, (uintptr_t)value);
1659
1660 FUNCTION_TEST_RETURN(this);
1661 }
1662
1663 /**********************************************************************************************************************************/
1664 PackWrite *
pckWriteStr(PackWrite * this,const String * value,PckWriteStrParam param)1665 pckWriteStr(PackWrite *this, const String *value, PckWriteStrParam param)
1666 {
1667 FUNCTION_TEST_BEGIN();
1668 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1669 FUNCTION_TEST_PARAM(STRING, value);
1670 FUNCTION_TEST_PARAM(UINT, param.id);
1671 FUNCTION_TEST_PARAM(BOOL, param.defaultWrite);
1672 FUNCTION_TEST_PARAM(STRING, param.defaultValue);
1673 FUNCTION_TEST_END();
1674
1675 if (!pckWriteDefaultNull(this, param.defaultWrite, strEq(value, param.defaultValue)))
1676 {
1677 ASSERT(value != NULL);
1678
1679 // Write string size if > 0
1680 pckWriteTag(this, pckTypeMapStr, param.id, strSize(value) > 0);
1681
1682 // Write string data if size > 0
1683 if (strSize(value) > 0)
1684 {
1685 pckWriteU64Internal(this, strSize(value));
1686 pckWriteBuffer(this, BUF(strZ(value), strSize(value)));
1687 }
1688 }
1689
1690 FUNCTION_TEST_RETURN(this);
1691 }
1692
1693 /**********************************************************************************************************************************/
1694 PackWrite *
pckWriteStrId(PackWrite * this,uint64_t value,PckWriteStrIdParam param)1695 pckWriteStrId(PackWrite *this, uint64_t value, PckWriteStrIdParam param)
1696 {
1697 FUNCTION_TEST_BEGIN();
1698 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1699 FUNCTION_TEST_PARAM(UINT64, value);
1700 FUNCTION_TEST_PARAM(UINT, param.id);
1701 FUNCTION_TEST_PARAM(BOOL, param.defaultWrite);
1702 FUNCTION_TEST_PARAM(UINT64, param.defaultValue);
1703 FUNCTION_TEST_END();
1704
1705 ASSERT(this != NULL);
1706
1707 if (!pckWriteDefaultNull(this, param.defaultWrite, value == param.defaultValue))
1708 pckWriteTag(this, pckTypeMapStrId, param.id, value);
1709
1710 FUNCTION_TEST_RETURN(this);
1711 }
1712
1713 /**********************************************************************************************************************************/
1714 PackWrite *
pckWriteStrLst(PackWrite * const this,const StringList * const value,const PckWriteStrLstParam param)1715 pckWriteStrLst(PackWrite *const this, const StringList *const value, const PckWriteStrLstParam param)
1716 {
1717 FUNCTION_TEST_BEGIN();
1718 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1719 FUNCTION_TEST_PARAM(STRING_LIST, value);
1720 FUNCTION_TEST_PARAM(UINT, param.id);
1721 FUNCTION_TEST_END();
1722
1723 ASSERT(this != NULL);
1724
1725 if (!pckWriteDefaultNull(this, false, value == NULL))
1726 {
1727 ASSERT(value != NULL);
1728
1729 pckWriteArrayBeginP(this, .id = param.id);
1730
1731 for (unsigned int valueIdx = 0; valueIdx < strLstSize(value); valueIdx++)
1732 pckWriteStrP(this, strLstGet(value, valueIdx));
1733
1734 pckWriteArrayEndP(this);
1735 }
1736
1737 FUNCTION_TEST_RETURN(this);
1738 }
1739
1740 /**********************************************************************************************************************************/
1741 PackWrite *
pckWriteTime(PackWrite * this,time_t value,PckWriteTimeParam param)1742 pckWriteTime(PackWrite *this, time_t value, PckWriteTimeParam param)
1743 {
1744 FUNCTION_TEST_BEGIN();
1745 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1746 FUNCTION_TEST_PARAM(TIME, value);
1747 FUNCTION_TEST_PARAM(UINT, param.id);
1748 FUNCTION_TEST_PARAM(BOOL, param.defaultWrite);
1749 FUNCTION_TEST_PARAM(TIME, param.defaultValue);
1750 FUNCTION_TEST_END();
1751
1752 ASSERT(this != NULL);
1753
1754 if (!pckWriteDefaultNull(this, param.defaultWrite, value == param.defaultValue))
1755 pckWriteTag(this, pckTypeMapTime, param.id, cvtInt64ToZigZag(value));
1756
1757 FUNCTION_TEST_RETURN(this);
1758 }
1759
1760 /**********************************************************************************************************************************/
1761 PackWrite *
pckWriteU32(PackWrite * this,uint32_t value,PckWriteU32Param param)1762 pckWriteU32(PackWrite *this, uint32_t value, PckWriteU32Param param)
1763 {
1764 FUNCTION_TEST_BEGIN();
1765 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1766 FUNCTION_TEST_PARAM(UINT32, value);
1767 FUNCTION_TEST_PARAM(UINT, param.id);
1768 FUNCTION_TEST_PARAM(BOOL, param.defaultWrite);
1769 FUNCTION_TEST_PARAM(UINT32, param.defaultValue);
1770 FUNCTION_TEST_END();
1771
1772 ASSERT(this != NULL);
1773
1774 if (!pckWriteDefaultNull(this, param.defaultWrite, value == param.defaultValue))
1775 pckWriteTag(this, pckTypeMapU32, param.id, value);
1776
1777 FUNCTION_TEST_RETURN(this);
1778 }
1779
1780 /**********************************************************************************************************************************/
1781 PackWrite *
pckWriteU64(PackWrite * this,uint64_t value,PckWriteU64Param param)1782 pckWriteU64(PackWrite *this, uint64_t value, PckWriteU64Param param)
1783 {
1784 FUNCTION_TEST_BEGIN();
1785 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1786 FUNCTION_TEST_PARAM(UINT64, value);
1787 FUNCTION_TEST_PARAM(UINT, param.id);
1788 FUNCTION_TEST_PARAM(BOOL, param.defaultWrite);
1789 FUNCTION_TEST_PARAM(UINT64, param.defaultValue);
1790 FUNCTION_TEST_END();
1791
1792 ASSERT(this != NULL);
1793
1794 if (!pckWriteDefaultNull(this, param.defaultWrite, value == param.defaultValue))
1795 pckWriteTag(this, pckTypeMapU64, param.id, value);
1796
1797 FUNCTION_TEST_RETURN(this);
1798 }
1799
1800 /**********************************************************************************************************************************/
1801 PackWrite *
pckWriteEnd(PackWrite * this)1802 pckWriteEnd(PackWrite *this)
1803 {
1804 FUNCTION_TEST_BEGIN();
1805 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1806 FUNCTION_TEST_END();
1807
1808 ASSERT(this != NULL);
1809 ASSERT(lstSize(this->tagStack) == 1);
1810
1811 pckWriteU64Internal(this, 0);
1812 this->tagStackTop = NULL;
1813
1814 // If writing to io flush the internal buffer
1815 if (this->write != NULL)
1816 {
1817 if (!bufEmpty(this->buffer))
1818 ioWrite(this->write, this->buffer);
1819 }
1820 // Else resize the external buffer to trim off extra space added during processing
1821 else
1822 bufResize(this->buffer, bufUsed(this->buffer));
1823
1824 FUNCTION_TEST_RETURN(this);
1825 }
1826
1827 /**********************************************************************************************************************************/
1828 const Buffer *
pckWriteBuf(const PackWrite * this)1829 pckWriteBuf(const PackWrite *this)
1830 {
1831 FUNCTION_TEST_BEGIN();
1832 FUNCTION_TEST_PARAM(PACK_WRITE, this);
1833 FUNCTION_TEST_END();
1834
1835 ASSERT(this != NULL);
1836 ASSERT(this->tagStackTop == NULL);
1837
1838 FUNCTION_TEST_RETURN(this->buffer);
1839 }
1840
1841 /**********************************************************************************************************************************/
1842 String *
pckWriteToLog(const PackWrite * this)1843 pckWriteToLog(const PackWrite *this)
1844 {
1845 return strNewFmt("{depth: %u, idLast: %u}", lstSize(this->tagStack), this->tagStackTop == NULL ? 0 : this->tagStackTop->idLast);
1846 }
1847