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  size_t bytesCopied;
367 
368  if (*sizePtr < (maxStringCount + sizeof(uint32_t)))
369  {
370  return false;
371  }
372 
373  if (!stringPtr)
374  {
375  return false;
376  }
377 
378 #if defined(__KLOCWORK__)
379  // Just like strncpy & strnlen, this function doesn't know the size of the source string,
380  // which can potentially result in a read overflow on the input buffer.
381  // Static code analyzer can report this issue, which is deliberatly ignored since this
382  // function expects NULL-terminated strings.
383  maxStringCount = strnlen(stringPtr, maxStringCount);
384 #endif
385 
386  // First copy in the string -- up to maxStringCount bytes, allowing enough
387  // space at the begining for a uint32.
388  for (bytesCopied = 0;
389  (bytesCopied < maxStringCount) && (stringPtr[bytesCopied] != '\0');
390  ++bytesCopied)
391  {
392  (*bufferPtr)[bytesCopied + sizeof(uint32_t)] = stringPtr[bytesCopied];
393  }
394 
395  // String was too long to fit in the buffer -- return false.
396  if (stringPtr[bytesCopied] != '\0')
397  {
398  return false;
399  }
400 
401  // Then go back and copy string size. No loss of precision packing into a uint32
402  // because maxStringCount is a uint32 or less.
403  bool packResult = le_pack_PackUint32(bufferPtr, sizePtr, bytesCopied);
404  LE_ASSERT(packResult); // Should not fail -- have checked there's enough space above.
405 
406  // Now increment buffer by size of string actually copied, and decrement available
407  // space by max which could have been copied. This ensures out of space errors will
408  // be caught as soon as possible.
409  *bufferPtr = *bufferPtr + bytesCopied;
410  *sizePtr -= maxStringCount;
411 
412  return true;
413 }
414 
415 //--------------------------------------------------------------------------------------------------
416 /**
417  * Pack the size information for an array into a buffer, incrementing the buffer pointer and
418  * decrementing the available size.
419  *
420  * @note Users of this API should generally use LE_PACK_PACKARRAY macro instead which also
421  * packs the array data.
422  */
423 //--------------------------------------------------------------------------------------------------
424 static inline bool le_pack_PackArrayHeader
425 (
426  uint8_t **bufferPtr,
427  size_t *sizePtr,
428  const void *arrayPtr,
429  size_t elementSize,
430  size_t arrayCount,
431  size_t arrayMaxCount
432 )
433 {
434  if ((*sizePtr < arrayMaxCount*elementSize + sizeof(uint32_t)) ||
435  (arrayCount > arrayMaxCount))
436  {
437  return false;
438  }
439 
440  LE_ASSERT(le_pack_PackSize(bufferPtr, sizePtr, arrayCount));
441 
442  return true;
443 }
444 
445 //--------------------------------------------------------------------------------------------------
446 /**
447  * Pack an array into a buffer, incrementing the buffer pointer and decrementing the available
448  * size.
449  *
450  * @note Always decrements available size according to the max possible size used, not actual size
451  * used. Will assert if the resulted array exceeds the maximum size allowed.
452  */
453 //--------------------------------------------------------------------------------------------------
454 #define LE_PACK_PACKARRAY(bufferPtr, \
455  sizePtr, \
456  arrayPtr, \
457  arrayCount, \
458  arrayMaxCount, \
459  packFunc, \
460  resultPtr) \
461  do { \
462  *(resultPtr) = le_pack_PackArrayHeader((bufferPtr), (sizePtr), \
463  (arrayPtr), sizeof((arrayPtr)[0]), \
464  (arrayCount), (arrayMaxCount)); \
465  if (*(resultPtr)) \
466  { \
467  uint32_t i; \
468  size_t newSizePtr = *(sizePtr) - sizeof((arrayPtr)[0])*(arrayMaxCount); \
469  for (i = 0; i < (arrayCount); ++i) \
470  { \
471  LE_ASSERT(packFunc((bufferPtr), (sizePtr), (arrayPtr)[i])); \
472  } \
473  LE_ASSERT(*(sizePtr) >= newSizePtr); \
474  *(sizePtr) = newSizePtr; \
475  *(resultPtr) = true; \
476  } \
477  } while (0)
478 
479 //--------------------------------------------------------------------------------------------------
480 /**
481  * Pack an array of struct into a buffer, incrementing the buffer pointer and decrementing the
482  * available size.
483  *
484  * @note Always decrements available size according to the max possible size used, not actual size
485  * used. Will assert if the resulted array of struct exceeds the maximum size allowed.
486  */
487 //--------------------------------------------------------------------------------------------------
488 #define LE_PACK_PACKSTRUCTARRAY(bufferPtr, \
489  sizePtr, \
490  arrayPtr, \
491  arrayCount, \
492  arrayMaxCount, \
493  packFunc, \
494  resultPtr) \
495  do { \
496  *(resultPtr) = le_pack_PackArrayHeader((bufferPtr), (sizePtr), \
497  (arrayPtr), sizeof((arrayPtr)[0]), \
498  (arrayCount), (arrayMaxCount)); \
499  if (*(resultPtr)) \
500  { \
501  uint32_t i; \
502  size_t newSizePtr = *(sizePtr) - sizeof((arrayPtr)[0])*(arrayMaxCount); \
503  for (i = 0; i < (arrayCount); ++i) \
504  { \
505  LE_ASSERT(packFunc((bufferPtr), (sizePtr), &((arrayPtr)[i]))); \
506  } \
507  LE_ASSERT(*(sizePtr) >= newSizePtr); \
508  *(sizePtr) = newSizePtr; \
509  *(resultPtr) = true; \
510  } \
511  } while (0)
512 
513 //--------------------------------------------------------------------------------------------------
514 // Unpack functions
515 //--------------------------------------------------------------------------------------------------
516 
517 #define LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr) \
518  if (*sizePtr < sizeof(*(valuePtr))) \
519  { \
520  return false; \
521  } \
522  \
523  memcpy((valuePtr), *bufferPtr, sizeof(*(valuePtr))); \
524  \
525  *bufferPtr = (*bufferPtr) + sizeof(*(valuePtr)); \
526  *sizePtr -= sizeof(*(valuePtr)); \
527  \
528  return true
529 
530 //--------------------------------------------------------------------------------------------------
531 /**
532  * Unpack a uint8_t from a buffer, incrementing the buffer pointer and decrementing the
533  * available size, as appropriate.
534  *
535  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
536  * size is known at compile time.
537  */
538 //--------------------------------------------------------------------------------------------------
539 static inline bool le_pack_UnpackUint8
540 (
541  uint8_t** bufferPtr,
542  size_t* sizePtr,
543  uint8_t* valuePtr
544 )
545 {
546  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
547 }
548 
549 //--------------------------------------------------------------------------------------------------
550 /**
551  * Unpack a uint16_t from a buffer, incrementing the buffer pointer and decrementing the
552  * available size, as appropriate.
553  *
554  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
555  * size is known at compile time.
556  */
557 //--------------------------------------------------------------------------------------------------
558 static inline bool le_pack_UnpackUint16
559 (
560  uint8_t** bufferPtr,
561  size_t* sizePtr,
562  uint16_t* valuePtr
563 )
564 {
565  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
566 }
567 
568 //--------------------------------------------------------------------------------------------------
569 /**
570  * Unpack a uint32_t from a buffer, incrementing the buffer pointer and decrementing the
571  * available size, as appropriate.
572  *
573  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
574  * size is known at compile time.
575  */
576 //--------------------------------------------------------------------------------------------------
577 static inline bool le_pack_UnpackUint32
578 (
579  uint8_t** bufferPtr,
580  size_t* sizePtr,
581  uint32_t* valuePtr
582 )
583 {
584  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
585 }
586 
587 //--------------------------------------------------------------------------------------------------
588 /**
589  * Unpack a uint64_t from a buffer, incrementing the buffer pointer and decrementing the
590  * available size, as appropriate.
591  *
592  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
593  * size is known at compile time.
594  */
595 //--------------------------------------------------------------------------------------------------
596 static inline bool le_pack_UnpackUint64
597 (
598  uint8_t** bufferPtr,
599  size_t* sizePtr,
600  uint64_t* valuePtr
601 )
602 {
603  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
604 }
605 
606 //--------------------------------------------------------------------------------------------------
607 /**
608  * Unpack a int8_t from a buffer, incrementing the buffer pointer and decrementing the
609  * available size, as appropriate.
610  *
611  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
612  * size is known at compile time.
613  */
614 //--------------------------------------------------------------------------------------------------
615 static inline bool le_pack_UnpackInt8
616 (
617  uint8_t** bufferPtr,
618  size_t* sizePtr,
619  int8_t* valuePtr
620 )
621 {
622  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
623 }
624 
625 //--------------------------------------------------------------------------------------------------
626 /**
627  * Unpack a int16_t from a buffer, incrementing the buffer pointer and decrementing the
628  * available size, as appropriate.
629  *
630  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
631  * size is known at compile time.
632  */
633 //--------------------------------------------------------------------------------------------------
634 static inline bool le_pack_UnpackInt16
635 (
636  uint8_t** bufferPtr,
637  size_t* sizePtr,
638  int16_t* valuePtr
639 )
640 {
641  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
642 }
643 
644 //--------------------------------------------------------------------------------------------------
645 /**
646  * Unpack a int32_t from a buffer, incrementing the buffer pointer and decrementing the
647  * available size, as appropriate.
648  *
649  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
650  * size is known at compile time.
651  */
652 //--------------------------------------------------------------------------------------------------
653 static inline bool le_pack_UnpackInt32
654 (
655  uint8_t** bufferPtr,
656  size_t* sizePtr,
657  int32_t* valuePtr
658 )
659 {
660  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
661 }
662 
663 //--------------------------------------------------------------------------------------------------
664 /**
665  * Unpack a int64_t from a buffer, incrementing the buffer pointer and decrementing the
666  * available size, as appropriate.
667  *
668  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
669  * size is known at compile time.
670  */
671 //--------------------------------------------------------------------------------------------------
672 static inline bool le_pack_UnpackInt64
673 (
674  uint8_t** bufferPtr,
675  size_t* sizePtr,
676  int64_t* valuePtr
677 )
678 {
679  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
680 }
681 
682 /**
683  * Pack a size_t into a buffer, incrementing the buffer pointer and decrementing the
684  * available size, as appropriate.
685  *
686  * @note Packed sizes are limited to 2^32-1, regardless of platform
687  */
688 //--------------------------------------------------------------------------------------------------
689 static inline bool le_pack_UnpackSize
690 (
691  uint8_t **bufferPtr,
692  size_t *sizePtr,
693  size_t *valuePtr
694 )
695 {
696  uint32_t rawValue;
697 
698  if (!le_pack_UnpackUint32(bufferPtr, sizePtr, &rawValue))
699  {
700  return false;
701  }
702 
703  *valuePtr = rawValue;
704 
705  return true;
706 }
707 
708 //--------------------------------------------------------------------------------------------------
709 /**
710  * Unpack a bool from a buffer, incrementing the buffer pointer and decrementing the
711  * available size, as appropriate.
712  *
713  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
714  * size is known at compile time.
715  */
716 //--------------------------------------------------------------------------------------------------
717 static inline bool le_pack_UnpackBool
718 (
719  uint8_t** bufferPtr,
720  size_t* sizePtr,
721  bool* valuePtr
722 )
723 {
724  // Treat boolean as uint8_t for packing, regarldess of underlying OS type.
725  // Underlying type has been int on some platforms in the past.
726  uint8_t simpleValue;
727  if (*sizePtr < sizeof(simpleValue))
728  {
729  return false;
730  }
731 
732  memcpy(&simpleValue, *bufferPtr, sizeof(simpleValue));
733 
734  *bufferPtr = ((uint8_t* )*bufferPtr) + sizeof(simpleValue);
735  *sizePtr -= sizeof(simpleValue);
736 
737  // force to true or false
738  *valuePtr = !!simpleValue;
739 
740  return true;
741 }
742 
743 //--------------------------------------------------------------------------------------------------
744 /**
745  * Unpack a char from a buffer, incrementing the buffer pointer and decrementing the
746  * available size, as appropriate.
747  *
748  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
749  * size is known at compile time.
750  */
751 //--------------------------------------------------------------------------------------------------
752 static inline bool le_pack_UnpackChar
753 (
754  uint8_t** bufferPtr,
755  size_t* sizePtr,
756  char* valuePtr
757 )
758 {
759  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
760 }
761 
762 //--------------------------------------------------------------------------------------------------
763 /**
764  * Unpack a double from a buffer, incrementing the buffer pointer and decrementing the
765  * available size, as appropriate.
766  *
767  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
768  * size is known at compile time.
769  */
770 //--------------------------------------------------------------------------------------------------
771 static inline bool le_pack_UnpackDouble
772 (
773  uint8_t** bufferPtr,
774  size_t* sizePtr,
775  double* valuePtr
776 )
777 {
778  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
779 }
780 
781 //--------------------------------------------------------------------------------------------------
782 /**
783  * Unpack a le_result_t from a buffer, incrementing the buffer pointer and decrementing the
784  * available size, as appropriate.
785  *
786  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
787  * size is known at compile time.
788  */
789 //--------------------------------------------------------------------------------------------------
790 static inline bool le_pack_UnpackResult
791 (
792  uint8_t** bufferPtr,
793  size_t* sizePtr,
794  le_result_t* valuePtr
795 )
796 {
797  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
798 }
799 
800 //--------------------------------------------------------------------------------------------------
801 /**
802  * Pack le_onoff_t into a buffer, incrementing the buffer pointer and decrementing the
803  * available size, as appropriate.
804  *
805  * @note By making this an inline function, gcc can often optimize out the size check if the buffer
806  * size is known at compile time.
807  */
808 //--------------------------------------------------------------------------------------------------
809 static inline bool le_pack_UnpackOnOff
810 (
811  uint8_t** bufferPtr,
812  size_t* sizePtr,
813  le_onoff_t* valuePtr
814 )
815 {
816  LE_PACK_UNPACK_SIMPLE_VALUE(valuePtr);
817 }
818 
819 #undef LE_PACK_UNPACK_SIMPLE_VALUE
820 
821 //--------------------------------------------------------------------------------------------------
822 /**
823  * Unpack a reference from a buffer, incrementing the buffer pointer and decrementing the available
824  * size.
825  */
826 //--------------------------------------------------------------------------------------------------
827 static inline bool le_pack_UnpackReference
828 (
829  uint8_t** bufferPtr,
830  size_t* sizePtr,
831  void* refPtr ///< Pointer to the reference. Declared as void * to allow implicit
832  ///< conversion from pointer to reference types.
833 )
834 {
835  uint32_t refAsInt;
836 
837  if (!le_pack_UnpackUint32(bufferPtr, sizePtr, &refAsInt))
838  {
839  return false;
840  }
841 
842  // All references passed through an API must be safe references, so
843  // 0-bit will be set. Check that here to be safe.
844  if ((refAsInt & 0x01) ||
845  (!refAsInt))
846  {
847  // Double cast to avoid warnings.
848  *(void **)refPtr = (void *)(size_t)refAsInt;
849  return true;
850  }
851  else
852  {
853  return false;
854  }
855 }
856 
857 //--------------------------------------------------------------------------------------------------
858 /**
859  * Unpack a string from a buffer, incrementing the buffer pointer and decrementing the available
860  * size.
861  *
862  * @note Always decrements available size according to the max possible size used, not actual size
863  * used. Will assert if provided string is larger than maximum allowable string.
864  */
865 //--------------------------------------------------------------------------------------------------
866 static inline bool le_pack_UnpackString
867 (
868  uint8_t** bufferPtr,
869  size_t* sizePtr,
870  char *stringPtr,
871  uint32_t bufferSize,
872  uint32_t maxStringCount
873 )
874 {
875  uint32_t stringSize;
876 
877  if (*sizePtr < (maxStringCount + sizeof(uint32_t)))
878  {
879  return false;
880  }
881 
882  // First get string size
883  if (!le_pack_UnpackUint32(bufferPtr, sizePtr, &stringSize))
884  {
885  return false;
886  }
887 
888  if ((stringSize > maxStringCount) ||
889  (stringSize > bufferSize))
890  {
891  return false;
892  }
893 
894  if (!stringPtr)
895  {
896  // Only allow unpacking into no output buffer if the string is zero sized.
897  // Otherwise an output buffer is required.
898  if (stringSize)
899  {
900  return false;
901  }
902  else
903  {
904  return true;
905  }
906  }
907 
908  memcpy(stringPtr, *bufferPtr, stringSize);
909  stringPtr[stringSize] = '\0';
910 
911  *bufferPtr = *bufferPtr + stringSize;
912  *sizePtr -= maxStringCount;
913 
914  return true;
915 }
916 
917 //--------------------------------------------------------------------------------------------------
918 /**
919  * Pack the size information for an array into a buffer, incrementing the buffer pointer and
920  * decrementing the available size.
921  *
922  * @note Users of this API should generally use LE_PACK_PACKARRAY macro instead which also
923  * packs the array data.
924  */
925 //--------------------------------------------------------------------------------------------------
926 static inline bool le_pack_UnpackArrayHeader
927 (
928  uint8_t **bufferPtr,
929  size_t *sizePtr,
930  const void *arrayPtr,
931  size_t elementSize,
932  size_t *arrayCountPtr,
933  size_t arrayMaxCount
934 )
935 {
936  if (*sizePtr < (arrayMaxCount*elementSize + sizeof(uint32_t)))
937  {
938  return false;
939  }
940 
941  LE_ASSERT(le_pack_UnpackSize(bufferPtr, sizePtr, arrayCountPtr));
942  if (*arrayCountPtr > arrayMaxCount)
943  {
944  return false;
945  }
946  else if (!arrayPtr)
947  {
948  // Missing array pointer must match zero sized array.
949  return (*arrayCountPtr == 0);
950  }
951 
952  return true;
953 }
954 
955 //--------------------------------------------------------------------------------------------------
956 /**
957  * Unpack an array into from buffer, incrementing the buffer pointer and decrementing the available
958  * size.
959  *
960  * @note Always decrements available size according to the max possible size used, not actual size
961  * used. Will assert if the resulted array exceeds the maximum size allowed.
962  */
963 //--------------------------------------------------------------------------------------------------
964 #define LE_PACK_UNPACKARRAY(bufferPtr, \
965  sizePtr, \
966  arrayPtr, \
967  arrayCountPtr, \
968  arrayMaxCount, \
969  unpackFunc, \
970  resultPtr) \
971  do { \
972  if (!le_pack_UnpackArrayHeader((bufferPtr), (sizePtr), \
973  (arrayPtr), sizeof((arrayPtr)[0]), \
974  (arrayCountPtr), (arrayMaxCount))) \
975  { \
976  *(resultPtr) = false; \
977  } \
978  else \
979  { \
980  uint32_t i; \
981  size_t newSizePtr = *(sizePtr) - sizeof((arrayPtr)[0])*(arrayMaxCount); \
982  for (i = 0; i < *(arrayCountPtr); ++i) \
983  { \
984  LE_ASSERT(unpackFunc((bufferPtr), (sizePtr), &(arrayPtr)[i])); \
985  } \
986  LE_ASSERT(*(sizePtr) >= newSizePtr); \
987  *(sizePtr) = newSizePtr; \
988  *(resultPtr) = true; \
989  } \
990  } while (0)
991 
992 
993 //--------------------------------------------------------------------------------------------------
994 /**
995  * Unpack an array of struct from buffer. Since its logic is the same as that for unpacking an
996  * array, here it calls LE_PACK_UNPACKSTRUCTARRAY() to do the work.
997  */
998 //--------------------------------------------------------------------------------------------------
999 #define LE_PACK_UNPACKSTRUCTARRAY(bufferPtr, \
1000  sizePtr, \
1001  arrayPtr, \
1002  arrayCountPtr, \
1003  arrayMaxCount, \
1004  unpackFunc, \
1005  resultPtr) \
1006  LE_PACK_UNPACKARRAY((bufferPtr), (sizePtr), (arrayPtr), (arrayCountPtr), \
1007  (arrayMaxCount), (unpackFunc), (resultPtr))
1008 
1009 #endif /* LE_PACK_H_INCLUDE_GUARD */
le_result_t
Definition: le_basics.h:35
#define LE_ASSERT(condition)
Definition: le_log.h:560
le_onoff_t
Definition: le_basics.h:70