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