le_pack.h

1 /**
2  * @page c_pack Low-level Pack/Unpack API
3  *
4  * @ref le_pack.h "API Reference"
5  *
6  * <HR>
7  *
8  * This low-level pack/unpack API is intended to support the higher level
9  * IPC messaging system, specifically code generated by ifgen. But it
10  * can also be used to hand-pack messages if using the @ref c_messaging
11  * API directly.
12  *
13  * This low-level pack/unpack API supports:
14  * - Packing basic types supported by the IPC system.
15  * - Packing reference types
16  * - Packing arrays of the above types
17  * - Packing strings.
18  * It also supports unpacking any of the above.
19  */
20 
21 #ifndef LE_PACK_H_INCLUDE_GUARD
22 #define LE_PACK_H_INCLUDE_GUARD
23 
24 //--------------------------------------------------------------------------------------------------
25 // Pack functions
26 //--------------------------------------------------------------------------------------------------
27 
28 // Packing a simple value is basically the same regardless of type. But don't use this macro
29 // directly to get better verification that we're only packing the types we expect
30 #define LE_PACK_PACK_SIMPLE_VALUE(value) \
31  if (*sizePtr < sizeof(value)) \
32  { \
33  return false; \
34  } \
35  \
36  memcpy(*bufferPtr, &(value), sizeof(value)); \
37  \
38  *bufferPtr = *bufferPtr + sizeof(value); \
39  *sizePtr -= sizeof(value); \
40  \
41  return true
42 
43 
44 //--------------------------------------------------------------------------------------------------
45 /**
46  * Pack a uint8_t into a buffer, incrementing the buffer pointer and decrementing the
47  * available size, as appropriate.
48  *
49  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
50  * size is known at compile time.
51  */
52 //--------------------------------------------------------------------------------------------------
53 static inline bool le_pack_PackUint8
54 (
55  uint8_t** bufferPtr,
56  size_t* sizePtr,
57  uint8_t value
58 )
59 {
60  LE_PACK_PACK_SIMPLE_VALUE(value);
61 }
62 
63 //--------------------------------------------------------------------------------------------------
64 /**
65  * Pack a uint16_t into a buffer, incrementing the buffer pointer and decrementing the
66  * available size, as appropriate.
67  *
68  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
69  * size is known at compile time.
70  */
71 //--------------------------------------------------------------------------------------------------
72 static inline bool le_pack_PackUint16
73 (
74  uint8_t** bufferPtr,
75  size_t* sizePtr,
76  uint16_t value
77 )
78 {
79  LE_PACK_PACK_SIMPLE_VALUE(value);
80 }
81 
82 //--------------------------------------------------------------------------------------------------
83 /**
84  * Pack a uint32_t into a buffer, incrementing the buffer pointer and decrementing the
85  * available size, as appropriate.
86  *
87  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
88  * size is known at compile time.
89  */
90 //--------------------------------------------------------------------------------------------------
91 static inline bool le_pack_PackUint32
92 (
93  uint8_t** bufferPtr,
94  size_t* sizePtr,
95  uint32_t value
96 )
97 {
98  LE_PACK_PACK_SIMPLE_VALUE(value);
99 }
100 
101 //--------------------------------------------------------------------------------------------------
102 /**
103  * Pack a uint64_t into a buffer, incrementing the buffer pointer and decrementing the
104  * available size, as appropriate.
105  *
106  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
107  * size is known at compile time.
108  */
109 //--------------------------------------------------------------------------------------------------
110 static inline bool le_pack_PackUint64
111 (
112  uint8_t** bufferPtr,
113  size_t* sizePtr,
114  uint64_t value
115 )
116 {
117  LE_PACK_PACK_SIMPLE_VALUE(value);
118 }
119 
120 //--------------------------------------------------------------------------------------------------
121 /**
122  * Pack a int8_t into a buffer, incrementing the buffer pointer and decrementing the
123  * available size, as appropriate.
124  *
125  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
126  * size is known at compile time.
127  */
128 //--------------------------------------------------------------------------------------------------
129 static inline bool le_pack_PackInt8
130 (
131  uint8_t** bufferPtr,
132  size_t* sizePtr,
133  int8_t value
134 )
135 {
136  LE_PACK_PACK_SIMPLE_VALUE(value);
137 }
138 
139 //--------------------------------------------------------------------------------------------------
140 /**
141  * Pack a int16_t into a buffer, incrementing the buffer pointer and decrementing the
142  * available size, as appropriate.
143  *
144  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
145  * size is known at compile time.
146  */
147 //--------------------------------------------------------------------------------------------------
148 static inline bool le_pack_PackInt16
149 (
150  uint8_t** bufferPtr,
151  size_t* sizePtr,
152  int16_t value
153 )
154 {
155  LE_PACK_PACK_SIMPLE_VALUE(value);
156 }
157 
158 //--------------------------------------------------------------------------------------------------
159 /**
160  * Pack a int32_t into a buffer, incrementing the buffer pointer and decrementing the
161  * available size, as appropriate.
162  *
163  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
164  * size is known at compile time.
165  */
166 //--------------------------------------------------------------------------------------------------
167 static inline bool le_pack_PackInt32
168 (
169  uint8_t** bufferPtr,
170  size_t* sizePtr,
171  int32_t value
172 )
173 {
174  LE_PACK_PACK_SIMPLE_VALUE(value);
175 }
176 
177 //--------------------------------------------------------------------------------------------------
178 /**
179  * Pack a int64_t into a buffer, incrementing the buffer pointer and decrementing the
180  * available size, as appropriate.
181  *
182  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
183  * size is known at compile time.
184  */
185 //--------------------------------------------------------------------------------------------------
186 static inline bool le_pack_PackInt64
187 (
188  uint8_t** bufferPtr,
189  size_t* sizePtr,
190  int64_t value
191 )
192 {
193  LE_PACK_PACK_SIMPLE_VALUE(value);
194 }
195 
196 //--------------------------------------------------------------------------------------------------
197 /**
198  * Pack a size_t into a buffer, incrementing the buffer pointer and decrementing the
199  * available size, as appropriate.
200  *
201  * @note Packed sizes are limited to 2^32-1, regardless of platform
202  */
203 //--------------------------------------------------------------------------------------------------
204 static inline bool le_pack_PackSize
205 (
206  uint8_t **bufferPtr,
207  size_t *sizePtr,
208  size_t value
209 )
210 {
211  if (value > UINT32_MAX)
212  {
213  return false;
214  }
215 
216  return le_pack_PackUint32(bufferPtr, sizePtr, value);
217 }
218 
219 //--------------------------------------------------------------------------------------------------
220 /**
221  * Pack a bool into a buffer, incrementing the buffer pointer and decrementing the
222  * available size, as appropriate.
223  *
224  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
225  * size is known at compile time.
226  */
227 //--------------------------------------------------------------------------------------------------
228 static inline bool le_pack_PackBool
229 (
230  uint8_t** bufferPtr,
231  size_t* sizePtr,
232  bool value
233 )
234 {
235  // Force boolean to uint8_t 0 or 1 for packing, regarldess of underlying OS type.
236  // Underlying type has been int on some platforms in the past.
237  uint8_t simpleValue = ((value)?1:0);
238  LE_PACK_PACK_SIMPLE_VALUE(simpleValue);
239 }
240 
241 //--------------------------------------------------------------------------------------------------
242 /**
243  * Pack a char into a buffer, incrementing the buffer pointer and decrementing the
244  * available size, as appropriate.
245  *
246  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
247  * size is known at compile time.
248  */
249 //--------------------------------------------------------------------------------------------------
250 static inline bool le_pack_PackChar
251 (
252  uint8_t** bufferPtr,
253  size_t* sizePtr,
254  char value
255 )
256 {
257  LE_PACK_PACK_SIMPLE_VALUE(value);
258 }
259 
260 //--------------------------------------------------------------------------------------------------
261 /**
262  * Pack a double into a buffer, incrementing the buffer pointer and decrementing the
263  * available size, as appropriate.
264  *
265  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
266  * size is known at compile time.
267  */
268 //--------------------------------------------------------------------------------------------------
269 static inline bool le_pack_PackDouble
270 (
271  uint8_t** bufferPtr,
272  size_t* sizePtr,
273  double value
274 )
275 {
276  LE_PACK_PACK_SIMPLE_VALUE(value);
277 }
278 
279 //--------------------------------------------------------------------------------------------------
280 /**
281  * Pack a le_result_t into a buffer, incrementing the buffer pointer and decrementing the
282  * available size, as appropriate.
283  *
284  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
285  * size is known at compile time.
286  */
287 //--------------------------------------------------------------------------------------------------
288 static inline bool le_pack_PackResult
289 (
290  uint8_t** bufferPtr,
291  size_t* sizePtr,
292  le_result_t value
293 )
294 {
295  LE_PACK_PACK_SIMPLE_VALUE(value);
296 }
297 
298 //--------------------------------------------------------------------------------------------------
299 /**
300  * Pack le_onoff_t into a buffer, incrementing the buffer pointer and decrementing the
301  * available size, as appropriate.
302  *
303  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
304  * size is known at compile time.
305  */
306 //--------------------------------------------------------------------------------------------------
307 static inline bool le_pack_PackOnOff
308 (
309  uint8_t** bufferPtr,
310  size_t* sizePtr,
311  le_onoff_t value
312 )
313 {
314  LE_PACK_PACK_SIMPLE_VALUE(value);
315 }
316 
317 #undef LE_PACK_PACK_SIMPLE_VALUE
318 
319 //--------------------------------------------------------------------------------------------------
320 /**
321  * Pack a reference into a buffer, incrementing the buffer pointer and decrementing the available
322  * size.
323  */
324 //--------------------------------------------------------------------------------------------------
325 static inline bool le_pack_PackReference
326 (
327  uint8_t** bufferPtr,
328  size_t* sizePtr,
329  const void* ref
330 )
331 {
332  size_t refAsInt = (size_t)ref;
333 
334  // All references passed through an API must be safe references (or NULL), so
335  // 0-bit will be set and reference will be <= UINT32_MAX. Size check
336  // is performed in pack function.
337  if ((refAsInt <= UINT32_MAX) &&
338  ((refAsInt & 0x01) ||
339  !refAsInt))
340  {
341  return le_pack_PackUint32(bufferPtr, sizePtr, (uint32_t)refAsInt);
342  }
343  else
344  {
345  return false;
346  }
347 }
348 
349 //--------------------------------------------------------------------------------------------------
350 /**
351  * Pack a string into a buffer, incrementing the buffer pointer and decrementing the available
352  * size.
353  *
354  * @note Always decrements available size according to the max possible size used, not actual size
355  * used. Will assert if provided string is larger than maximum allowable string.
356  */
357 //--------------------------------------------------------------------------------------------------
358 static inline bool le_pack_PackString
359 (
360  uint8_t** bufferPtr,
361  size_t* sizePtr,
362  const char *stringPtr,
363  uint32_t maxStringCount
364 )
365 {
366  uint8_t* newBufferPtr;
367  uint8_t* stringStartPtr;
368  size_t bytesCopied;
369 
370  if (*sizePtr < (maxStringCount + sizeof(uint32_t)))
371  {
372  return false;
373  }
374 
375  // First copy in the string
376  stringStartPtr = *bufferPtr + sizeof(uint32_t);
377 
378  if (!stringPtr)
379  {
380  // No string is treated as empty string here.
381  newBufferPtr = NULL;
382  bytesCopied = 0;
383  }
384  else if (*sizePtr < maxStringCount)
385  {
386  newBufferPtr = (uint8_t *)memccpy(stringStartPtr, stringPtr, '\0', *sizePtr);
387  bytesCopied = *sizePtr;
388  }
389  else
390  {
391  newBufferPtr = (uint8_t *)memccpy(stringStartPtr, stringPtr, '\0', maxStringCount);
392  bytesCopied = maxStringCount;
393  }
394 
395  if (newBufferPtr)
396  {
397  // Bytes copied, excluding terminating NULL which is being stripped.
398  bytesCopied = newBufferPtr - stringStartPtr - 1;
399  }
400 
401  // Check entire string was copied, less than max possible string size.
402  if (!(stringPtr && (bytesCopied <= maxStringCount) && ('\0' == stringPtr[bytesCopied])))
403  {
404  return false;
405  }
406 
407  // Then go back and copy string size. No loss of precision packing into a uint32
408  // because maxStringCount is a uint32 or less.
409  bool packResult = le_pack_PackUint32(bufferPtr, sizePtr, bytesCopied);
410  LE_ASSERT(packResult); // Should not fail -- have checked there's enough space above.
411 
412  // Now increment buffer by size of string actually copied, and decrement available
413  // space by max which could have been copied. This ensures out of space errors will
414  // be caught as soon as possible.
415  *bufferPtr = *bufferPtr + bytesCopied;
416  *sizePtr -= maxStringCount;
417 
418  return true;
419 }
420 
421 //--------------------------------------------------------------------------------------------------
422 /**
423  * Pack the size information for an array into a buffer, incrementing the buffer pointer and
424  * decrementing the available size.
425  *
426  * @note Users of this API should generally use LE_PACK_PACKARRAY macro instead which also
427  * packs the array data.
428  */
429 //--------------------------------------------------------------------------------------------------
430 static inline bool le_pack_PackArrayHeader
431 (
432  uint8_t **bufferPtr,
433  size_t *sizePtr,
434  const void *arrayPtr,
435  size_t elementSize,
436  size_t arrayCount,
437  size_t arrayMaxCount
438 )
439 {
440  if ((*sizePtr < arrayMaxCount*elementSize + sizeof(uint32_t)) ||
441  (arrayCount > arrayMaxCount))
442  {
443  return false;
444  }
445 
446  LE_ASSERT(le_pack_PackSize(bufferPtr, sizePtr, arrayCount));
447 
448  return true;
449 }
450 
451 //--------------------------------------------------------------------------------------------------
452 /**
453  * Pack an array into a buffer, incrementing the buffer pointer and decrementing the available
454  * size.
455  *
456  * @note Always decrements available size according to the max possible size used, not actual size
457  * used. Will assert if provided string is larger than maximum allowable string.
458  */
459 //--------------------------------------------------------------------------------------------------
460 #define LE_PACK_PACKARRAY(bufferPtr, \
461  sizePtr, \
462  arrayPtr, \
463  arrayCount, \
464  arrayMaxCount, \
465  packFunc, \
466  resultPtr) \
467  do { \
468  *(resultPtr) = le_pack_PackArrayHeader((bufferPtr), (sizePtr), \
469  (arrayPtr), sizeof((arrayPtr)[0]), \
470  (arrayCount), (arrayMaxCount)); \
471  if (*(resultPtr)) \
472  { \
473  uint32_t i; \
474  size_t newSizePtr = *(sizePtr) - sizeof((arrayPtr)[0])*(arrayMaxCount); \
475  for (i = 0; i < (arrayCount); ++i) \
476  { \
477  LE_ASSERT(packFunc((bufferPtr), (sizePtr), (arrayPtr)[i])); \
478  } \
479  LE_ASSERT(*(sizePtr) >= newSizePtr); \
480  *(sizePtr) = newSizePtr; \
481  *(resultPtr) = true; \
482  } \
483  } while (0)
484 
485 //--------------------------------------------------------------------------------------------------
486 // Unpack functions
487 //--------------------------------------------------------------------------------------------------
488 
489 #define LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr) \
490  if (*sizePtr < sizeof(*(valuePtr))) \
491  { \
492  return false; \
493  } \
494  \
495  memcpy((valuePtr), *bufferPtr, sizeof(*(valuePtr))); \
496  \
497  *bufferPtr = (*bufferPtr) + sizeof(*(valuePtr)); \
498  *sizePtr -= sizeof(*(valuePtr)); \
499  \
500  return true
501 
502 //--------------------------------------------------------------------------------------------------
503 /**
504  * Unpack a uint8_t from a buffer, incrementing the buffer pointer and decrementing the
505  * available size, as appropriate.
506  *
507  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
508  * size is known at compile time.
509  */
510 //--------------------------------------------------------------------------------------------------
511 static inline bool le_pack_UnpackUint8
512 (
513  uint8_t** bufferPtr,
514  size_t* sizePtr,
515  uint8_t* valuePtr
516 )
517 {
518  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
519 }
520 
521 //--------------------------------------------------------------------------------------------------
522 /**
523  * Unpack a uint16_t from a buffer, incrementing the buffer pointer and decrementing the
524  * available size, as appropriate.
525  *
526  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
527  * size is known at compile time.
528  */
529 //--------------------------------------------------------------------------------------------------
530 static inline bool le_pack_UnpackUint16
531 (
532  uint8_t** bufferPtr,
533  size_t* sizePtr,
534  uint16_t* valuePtr
535 )
536 {
537  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
538 }
539 
540 //--------------------------------------------------------------------------------------------------
541 /**
542  * Unpack a uint32_t from a buffer, incrementing the buffer pointer and decrementing the
543  * available size, as appropriate.
544  *
545  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
546  * size is known at compile time.
547  */
548 //--------------------------------------------------------------------------------------------------
549 static inline bool le_pack_UnpackUint32
550 (
551  uint8_t** bufferPtr,
552  size_t* sizePtr,
553  uint32_t* valuePtr
554 )
555 {
556  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
557 }
558 
559 //--------------------------------------------------------------------------------------------------
560 /**
561  * Unpack a uint64_t from a buffer, incrementing the buffer pointer and decrementing the
562  * available size, as appropriate.
563  *
564  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
565  * size is known at compile time.
566  */
567 //--------------------------------------------------------------------------------------------------
568 static inline bool le_pack_UnpackUint64
569 (
570  uint8_t** bufferPtr,
571  size_t* sizePtr,
572  uint64_t* valuePtr
573 )
574 {
575  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
576 }
577 
578 //--------------------------------------------------------------------------------------------------
579 /**
580  * Unpack a int8_t from a buffer, incrementing the buffer pointer and decrementing the
581  * available size, as appropriate.
582  *
583  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
584  * size is known at compile time.
585  */
586 //--------------------------------------------------------------------------------------------------
587 static inline bool le_pack_UnpackInt8
588 (
589  uint8_t** bufferPtr,
590  size_t* sizePtr,
591  int8_t* valuePtr
592 )
593 {
594  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
595 }
596 
597 //--------------------------------------------------------------------------------------------------
598 /**
599  * Unpack a int16_t from a buffer, incrementing the buffer pointer and decrementing the
600  * available size, as appropriate.
601  *
602  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
603  * size is known at compile time.
604  */
605 //--------------------------------------------------------------------------------------------------
606 static inline bool le_pack_UnpackInt16
607 (
608  uint8_t** bufferPtr,
609  size_t* sizePtr,
610  int16_t* valuePtr
611 )
612 {
613  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
614 }
615 
616 //--------------------------------------------------------------------------------------------------
617 /**
618  * Unpack a int32_t from a buffer, incrementing the buffer pointer and decrementing the
619  * available size, as appropriate.
620  *
621  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
622  * size is known at compile time.
623  */
624 //--------------------------------------------------------------------------------------------------
625 static inline bool le_pack_UnpackInt32
626 (
627  uint8_t** bufferPtr,
628  size_t* sizePtr,
629  int32_t* valuePtr
630 )
631 {
632  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
633 }
634 
635 //--------------------------------------------------------------------------------------------------
636 /**
637  * Unpack a int64_t from a buffer, incrementing the buffer pointer and decrementing the
638  * available size, as appropriate.
639  *
640  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
641  * size is known at compile time.
642  */
643 //--------------------------------------------------------------------------------------------------
644 static inline bool le_pack_UnpackInt64
645 (
646  uint8_t** bufferPtr,
647  size_t* sizePtr,
648  int64_t* valuePtr
649 )
650 {
651  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
652 }
653 
654 /**
655  * Pack a size_t into a buffer, incrementing the buffer pointer and decrementing the
656  * available size, as appropriate.
657  *
658  * @note Packed sizes are limited to 2^32-1, regardless of platform
659  */
660 //--------------------------------------------------------------------------------------------------
661 static inline bool le_pack_UnpackSize
662 (
663  uint8_t **bufferPtr,
664  size_t *sizePtr,
665  size_t *valuePtr
666 )
667 {
668  uint32_t rawValue;
669 
670  if (!le_pack_UnpackUint32(bufferPtr, sizePtr, &rawValue))
671  {
672  return false;
673  }
674 
675  *valuePtr = rawValue;
676 
677  return true;
678 }
679 
680 //--------------------------------------------------------------------------------------------------
681 /**
682  * Unpack a bool from a buffer, incrementing the buffer pointer and decrementing the
683  * available size, as appropriate.
684  *
685  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
686  * size is known at compile time.
687  */
688 //--------------------------------------------------------------------------------------------------
689 static inline bool le_pack_UnpackBool
690 (
691  uint8_t** bufferPtr,
692  size_t* sizePtr,
693  bool* valuePtr
694 )
695 {
696  // Treat boolean as uint8_t for packing, regarldess of underlying OS type.
697  // Underlying type has been int on some platforms in the past.
698  uint8_t simpleValue;
699  if (*sizePtr < sizeof(simpleValue))
700  {
701  return false;
702  }
703 
704  memcpy(&simpleValue, *bufferPtr, sizeof(simpleValue));
705 
706  *bufferPtr = ((uint8_t* )*bufferPtr) + sizeof(simpleValue);
707  *sizePtr -= sizeof(simpleValue);
708 
709  // force to true or false
710  *valuePtr = !!simpleValue;
711 
712  return true;
713 }
714 
715 //--------------------------------------------------------------------------------------------------
716 /**
717  * Unpack a char from a buffer, incrementing the buffer pointer and decrementing the
718  * available size, as appropriate.
719  *
720  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
721  * size is known at compile time.
722  */
723 //--------------------------------------------------------------------------------------------------
724 static inline bool le_pack_UnpackChar
725 (
726  uint8_t** bufferPtr,
727  size_t* sizePtr,
728  char* valuePtr
729 )
730 {
731  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
732 }
733 
734 //--------------------------------------------------------------------------------------------------
735 /**
736  * Unpack a double from a buffer, incrementing the buffer pointer and decrementing the
737  * available size, as appropriate.
738  *
739  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
740  * size is known at compile time.
741  */
742 //--------------------------------------------------------------------------------------------------
743 static inline bool le_pack_UnpackDouble
744 (
745  uint8_t** bufferPtr,
746  size_t* sizePtr,
747  double* valuePtr
748 )
749 {
750  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
751 }
752 
753 //--------------------------------------------------------------------------------------------------
754 /**
755  * Unpack a le_result_t from a buffer, incrementing the buffer pointer and decrementing the
756  * available size, as appropriate.
757  *
758  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
759  * size is known at compile time.
760  */
761 //--------------------------------------------------------------------------------------------------
762 static inline bool le_pack_UnpackResult
763 (
764  uint8_t** bufferPtr,
765  size_t* sizePtr,
766  le_result_t* valuePtr
767 )
768 {
769  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
770 }
771 
772 //--------------------------------------------------------------------------------------------------
773 /**
774  * Pack le_onoff_t into a buffer, incrementing the buffer pointer and decrementing the
775  * available size, as appropriate.
776  *
777  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
778  * size is known at compile time.
779  */
780 //--------------------------------------------------------------------------------------------------
781 static inline bool le_pack_UnpackOnOff
782 (
783  uint8_t** bufferPtr,
784  size_t* sizePtr,
785  le_onoff_t* valuePtr
786 )
787 {
788  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
789 }
790 
791 #undef LE_PACK_UNPACK_SIMPLE_VALUE
792 
793 //--------------------------------------------------------------------------------------------------
794 /**
795  * Unpack a reference from a buffer, incrementing the buffer pointer and decrementing the available
796  * size.
797  */
798 //--------------------------------------------------------------------------------------------------
799 static inline bool le_pack_UnpackReference
800 (
801  uint8_t** bufferPtr,
802  size_t* sizePtr,
803  void* refPtr ///< Pointer to the reference. Declared as void * to allow implicit
804  ///< conversion from pointer to reference types.
805 )
806 {
807  uint32_t refAsInt;
808 
809  if (!le_pack_UnpackUint32(bufferPtr, sizePtr, &refAsInt))
810  {
811  return false;
812  }
813 
814  // All references passed through an API must be safe references, so
815  // 0-bit will be set. Check that here to be safe.
816  if ((refAsInt & 0x01) ||
817  (!refAsInt))
818  {
819  // Double cast to avoid warnings.
820  *(void **)refPtr = (void *)(size_t)refAsInt;
821  return true;
822  }
823  else
824  {
825  return false;
826  }
827 }
828 
829 //--------------------------------------------------------------------------------------------------
830 /**
831  * Unpack a string from a buffer, incrementing the buffer pointer and decrementing the available
832  * size.
833  *
834  * @note Always decrements available size according to the max possible size used, not actual size
835  * used. Will assert if provided string is larger than maximum allowable string.
836  */
837 //--------------------------------------------------------------------------------------------------
838 static inline bool le_pack_UnpackString
839 (
840  uint8_t** bufferPtr,
841  size_t* sizePtr,
842  char *stringPtr,
843  uint32_t maxStringCount
844 )
845 {
846  uint32_t stringSize;
847 
848  if (*sizePtr < (maxStringCount + sizeof(uint32_t)))
849  {
850  return false;
851  }
852 
853  // First get string size
854  if (!le_pack_UnpackUint32(bufferPtr, sizePtr, &stringSize))
855  {
856  return false;
857  }
858 
859  if (stringSize > maxStringCount)
860  {
861  return false;
862  }
863 
864  if (!stringPtr)
865  {
866  // Only allow unpacking into no output buffer if the string is zero sized.
867  // Otherwise an output buffer is required.
868  if (stringSize)
869  {
870  return false;
871  }
872  else
873  {
874  return true;
875  }
876  }
877 
878  memcpy(stringPtr, *bufferPtr, stringSize);
879  stringPtr[stringSize] = '\0';
880 
881  *bufferPtr = *bufferPtr + stringSize;
882  *sizePtr -= maxStringCount;
883 
884  return true;
885 }
886 
887 //--------------------------------------------------------------------------------------------------
888 /**
889  * Pack the size information for an array into a buffer, incrementing the buffer pointer and
890  * decrementing the available size.
891  *
892  * @note Users of this API should generally use LE_PACK_PACKARRAY macro instead which also
893  * packs the array data.
894  */
895 //--------------------------------------------------------------------------------------------------
896 static inline bool le_pack_UnpackArrayHeader
897 (
898  uint8_t **bufferPtr,
899  size_t *sizePtr,
900  const void *arrayPtr,
901  size_t elementSize,
902  size_t *arrayCountPtr,
903  size_t arrayMaxCount
904 )
905 {
906  if (*sizePtr < (arrayMaxCount*elementSize + sizeof(uint32_t)))
907  {
908  return false;
909  }
910 
911  LE_ASSERT(le_pack_UnpackSize(bufferPtr, sizePtr, arrayCountPtr));
912  if (*arrayCountPtr > arrayMaxCount)
913  {
914  return false;
915  }
916  else if (!arrayPtr)
917  {
918  // Missing array pointer must match zero sized array.
919  return (*arrayCountPtr == 0);
920  }
921 
922  return true;
923 }
924 
925 //--------------------------------------------------------------------------------------------------
926 /**
927  * Unpack an array into from buffer, incrementing the buffer pointer and decrementing the available
928  * size.
929  *
930  * @note Always decrements available size according to the max possible size used, not actual size
931  * used. Will assert if provided string is larger than maximum allowable string.
932  */
933 //--------------------------------------------------------------------------------------------------
934 #define LE_PACK_UNPACKARRAY(bufferPtr, \
935  sizePtr, \
936  arrayPtr, \
937  arrayCountPtr, \
938  arrayMaxCount, \
939  unpackFunc, \
940  resultPtr) \
941  do { \
942  if (!le_pack_UnpackArrayHeader((bufferPtr), (sizePtr), \
943  (arrayPtr), sizeof((arrayPtr)[0]), \
944  (arrayCountPtr), (arrayMaxCount))) \
945  { \
946  *(resultPtr) = false; \
947  } \
948  else \
949  { \
950  uint32_t i; \
951  size_t newSizePtr = *(sizePtr) - sizeof((arrayPtr)[0])*(arrayMaxCount); \
952  for (i = 0; i < *(arrayCountPtr); ++i) \
953  { \
954  LE_ASSERT(unpackFunc((bufferPtr), (sizePtr), &(arrayPtr)[i])); \
955  } \
956  LE_ASSERT(*(sizePtr) >= newSizePtr); \
957  *(sizePtr) = newSizePtr; \
958  *(resultPtr) = true; \
959  } \
960  } while (0)
961 
962 #endif /* LE_PACK_H_INCLUDE_GUARD */
le_result_t
Definition: le_basics.h:35
#define LE_ASSERT(condition)
Definition: le_log.h:562
le_onoff_t
Definition: le_basics.h:70