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, &param.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, &param.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, &param.id))
734         FUNCTION_TEST_RETURN(NULL);
735 
736     Buffer *result = NULL;
737 
738     // If buffer size > 0
739     if (pckReadTag(this, &param.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, &param.id))
772         FUNCTION_TEST_RETURN(param.defaultValue);
773 
774     FUNCTION_TEST_RETURN(pckReadTag(this, &param.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, &param.id))
790         FUNCTION_TEST_RETURN(param.defaultValue);
791 
792     FUNCTION_TEST_RETURN(cvtInt32FromZigZag((uint32_t)pckReadTag(this, &param.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, &param.id))
808         FUNCTION_TEST_RETURN(param.defaultValue);
809 
810     FUNCTION_TEST_RETURN(cvtInt64FromZigZag(pckReadTag(this, &param.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, &param.id))
826         FUNCTION_TEST_RETURN(param.defaultValue);
827 
828     FUNCTION_TEST_RETURN((mode_t)pckReadTag(this, &param.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, &param.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, &param.id))
906         FUNCTION_TEST_RETURN(NULL);
907 
908     // Read the tag
909     pckReadTag(this, &param.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, &param.id))
937         FUNCTION_TEST_RETURN(NULL);
938 
939     FUNCTION_TEST_RETURN((void *)(uintptr_t)pckReadTag(this, &param.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, &param.id))
955         FUNCTION_TEST_RETURN(strDup(param.defaultValue));
956 
957     String *result = NULL;
958 
959     // If string size > 0
960     if (pckReadTag(this, &param.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, &param.id))
995         FUNCTION_TEST_RETURN(param.defaultValue);
996 
997     FUNCTION_TEST_RETURN(pckReadTag(this, &param.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, &param.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, &param.id))
1039         FUNCTION_TEST_RETURN(param.defaultValue);
1040 
1041     FUNCTION_TEST_RETURN((time_t)cvtInt64FromZigZag(pckReadTag(this, &param.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, &param.id))
1057         FUNCTION_TEST_RETURN(param.defaultValue);
1058 
1059     FUNCTION_TEST_RETURN((uint32_t)pckReadTag(this, &param.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, &param.id))
1075         FUNCTION_TEST_RETURN(param.defaultValue);
1076 
1077     FUNCTION_TEST_RETURN(pckReadTag(this, &param.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