xref: /freebsd/contrib/libcbor/src/cbor/encoding.c (revision b5b9517bfe394e55088f5a05882eabae7e9b7b29)
1 /*
2  * Copyright (c) 2014-2020 Pavel Kalvoda <me@pavelkalvoda.com>
3  *
4  * libcbor is free software; you can redistribute it and/or modify
5  * it under the terms of the MIT license. See LICENSE for details.
6  */
7 
8 #include "encoding.h"
9 
10 #include <math.h>
11 
12 #include "internal/encoders.h"
13 
cbor_encode_uint8(uint8_t value,unsigned char * buffer,size_t buffer_size)14 size_t cbor_encode_uint8(uint8_t value, unsigned char* buffer,
15                          size_t buffer_size) {
16   return _cbor_encode_uint8(value, buffer, buffer_size, 0x00);
17 }
18 
cbor_encode_uint16(uint16_t value,unsigned char * buffer,size_t buffer_size)19 size_t cbor_encode_uint16(uint16_t value, unsigned char* buffer,
20                           size_t buffer_size) {
21   return _cbor_encode_uint16(value, buffer, buffer_size, 0x00);
22 }
23 
cbor_encode_uint32(uint32_t value,unsigned char * buffer,size_t buffer_size)24 size_t cbor_encode_uint32(uint32_t value, unsigned char* buffer,
25                           size_t buffer_size) {
26   return _cbor_encode_uint32(value, buffer, buffer_size, 0x00);
27 }
28 
cbor_encode_uint64(uint64_t value,unsigned char * buffer,size_t buffer_size)29 size_t cbor_encode_uint64(uint64_t value, unsigned char* buffer,
30                           size_t buffer_size) {
31   return _cbor_encode_uint64(value, buffer, buffer_size, 0x00);
32 }
33 
cbor_encode_uint(uint64_t value,unsigned char * buffer,size_t buffer_size)34 size_t cbor_encode_uint(uint64_t value, unsigned char* buffer,
35                         size_t buffer_size) {
36   return _cbor_encode_uint(value, buffer, buffer_size, 0x00);
37 }
38 
cbor_encode_negint8(uint8_t value,unsigned char * buffer,size_t buffer_size)39 size_t cbor_encode_negint8(uint8_t value, unsigned char* buffer,
40                            size_t buffer_size) {
41   return _cbor_encode_uint8(value, buffer, buffer_size, 0x20);
42 }
43 
cbor_encode_negint16(uint16_t value,unsigned char * buffer,size_t buffer_size)44 size_t cbor_encode_negint16(uint16_t value, unsigned char* buffer,
45                             size_t buffer_size) {
46   return _cbor_encode_uint16(value, buffer, buffer_size, 0x20);
47 }
48 
cbor_encode_negint32(uint32_t value,unsigned char * buffer,size_t buffer_size)49 size_t cbor_encode_negint32(uint32_t value, unsigned char* buffer,
50                             size_t buffer_size) {
51   return _cbor_encode_uint32(value, buffer, buffer_size, 0x20);
52 }
53 
cbor_encode_negint64(uint64_t value,unsigned char * buffer,size_t buffer_size)54 size_t cbor_encode_negint64(uint64_t value, unsigned char* buffer,
55                             size_t buffer_size) {
56   return _cbor_encode_uint64(value, buffer, buffer_size, 0x20);
57 }
58 
cbor_encode_negint(uint64_t value,unsigned char * buffer,size_t buffer_size)59 size_t cbor_encode_negint(uint64_t value, unsigned char* buffer,
60                           size_t buffer_size) {
61   return _cbor_encode_uint(value, buffer, buffer_size, 0x20);
62 }
63 
cbor_encode_bytestring_start(size_t length,unsigned char * buffer,size_t buffer_size)64 size_t cbor_encode_bytestring_start(size_t length, unsigned char* buffer,
65                                     size_t buffer_size) {
66   return _cbor_encode_uint((size_t)length, buffer, buffer_size, 0x40);
67 }
68 
_cbor_encode_byte(uint8_t value,unsigned char * buffer,size_t buffer_size)69 size_t _cbor_encode_byte(uint8_t value, unsigned char* buffer,
70                          size_t buffer_size) {
71   if (buffer_size >= 1) {
72     buffer[0] = value;
73     return 1;
74   } else
75     return 0;
76 }
77 
cbor_encode_indef_bytestring_start(unsigned char * buffer,size_t buffer_size)78 size_t cbor_encode_indef_bytestring_start(unsigned char* buffer,
79                                           size_t buffer_size) {
80   return _cbor_encode_byte(0x5F, buffer, buffer_size);
81 }
82 
cbor_encode_string_start(size_t length,unsigned char * buffer,size_t buffer_size)83 size_t cbor_encode_string_start(size_t length, unsigned char* buffer,
84                                 size_t buffer_size) {
85   return _cbor_encode_uint((size_t)length, buffer, buffer_size, 0x60);
86 }
87 
cbor_encode_indef_string_start(unsigned char * buffer,size_t buffer_size)88 size_t cbor_encode_indef_string_start(unsigned char* buffer,
89                                       size_t buffer_size) {
90   return _cbor_encode_byte(0x7F, buffer, buffer_size);
91 }
92 
cbor_encode_array_start(size_t length,unsigned char * buffer,size_t buffer_size)93 size_t cbor_encode_array_start(size_t length, unsigned char* buffer,
94                                size_t buffer_size) {
95   return _cbor_encode_uint((size_t)length, buffer, buffer_size, 0x80);
96 }
97 
cbor_encode_indef_array_start(unsigned char * buffer,size_t buffer_size)98 size_t cbor_encode_indef_array_start(unsigned char* buffer,
99                                      size_t buffer_size) {
100   return _cbor_encode_byte(0x9F, buffer, buffer_size);
101 }
102 
cbor_encode_map_start(size_t length,unsigned char * buffer,size_t buffer_size)103 size_t cbor_encode_map_start(size_t length, unsigned char* buffer,
104                              size_t buffer_size) {
105   return _cbor_encode_uint((size_t)length, buffer, buffer_size, 0xA0);
106 }
107 
cbor_encode_indef_map_start(unsigned char * buffer,size_t buffer_size)108 size_t cbor_encode_indef_map_start(unsigned char* buffer, size_t buffer_size) {
109   return _cbor_encode_byte(0xBF, buffer, buffer_size);
110 }
111 
cbor_encode_tag(uint64_t value,unsigned char * buffer,size_t buffer_size)112 size_t cbor_encode_tag(uint64_t value, unsigned char* buffer,
113                        size_t buffer_size) {
114   return _cbor_encode_uint(value, buffer, buffer_size, 0xC0);
115 }
116 
cbor_encode_bool(bool value,unsigned char * buffer,size_t buffer_size)117 size_t cbor_encode_bool(bool value, unsigned char* buffer, size_t buffer_size) {
118   return value ? _cbor_encode_byte(0xF5, buffer, buffer_size)
119                : _cbor_encode_byte(0xF4, buffer, buffer_size);
120 }
121 
cbor_encode_null(unsigned char * buffer,size_t buffer_size)122 size_t cbor_encode_null(unsigned char* buffer, size_t buffer_size) {
123   return _cbor_encode_byte(0xF6, buffer, buffer_size);
124 }
125 
cbor_encode_undef(unsigned char * buffer,size_t buffer_size)126 size_t cbor_encode_undef(unsigned char* buffer, size_t buffer_size) {
127   return _cbor_encode_byte(0xF7, buffer, buffer_size);
128 }
129 
cbor_encode_half(float value,unsigned char * buffer,size_t buffer_size)130 size_t cbor_encode_half(float value, unsigned char* buffer,
131                         size_t buffer_size) {
132   // TODO: Broken on systems that do not use IEEE 754
133   /* Assuming value is normalized */
134   uint32_t val = ((union _cbor_float_helper){.as_float = value}).as_uint;
135   uint16_t res;
136   uint8_t exp = (uint8_t)((val & 0x7F800000u) >>
137                           23u); /* 0b0111_1111_1000_0000_0000_0000_0000_0000 */
138   uint32_t mant =
139       val & 0x7FFFFFu; /* 0b0000_0000_0111_1111_1111_1111_1111_1111 */
140   if (exp == 0xFF) {   /* Infinity or NaNs */
141     if (isnan(value)) {
142       // Note: Values of signaling NaNs are discarded. See `cbor_encode_single`.
143       res = (uint16_t)0x007e00;
144     } else {
145       // If the mantissa is non-zero, we have a NaN, but those are handled
146       // above. See
147       // https://en.wikipedia.org/wiki/Half-precision_floating-point_format
148       CBOR_ASSERT(mant == 0u);
149       res = (uint16_t)((val & 0x80000000u) >> 16u | 0x7C00u);
150     }
151   } else if (exp == 0x00) { /* Zeroes or subnorms */
152     res = (uint16_t)((val & 0x80000000u) >> 16u | mant >> 13u);
153   } else { /* Normal numbers */
154     int8_t logical_exp = (int8_t)(exp - 127);
155     CBOR_ASSERT(logical_exp == exp - 127);
156 
157     // Now we know that 2^exp <= 0 logically
158     if (logical_exp < -24) {
159       /* No unambiguous representation exists, this float is not a half float
160          and is too small to be represented using a half, round off to zero.
161          Consistent with the reference implementation. */
162       res = 0;
163     } else if (logical_exp < -14) {
164       /* Offset the remaining decimal places by shifting the significand, the
165          value is lost. This is an implementation decision that works around the
166          absence of standard half-float in the language. */
167       res = (uint16_t)((val & 0x80000000u) >> 16u) |  // Extract sign bit
168             ((uint16_t)(1u << (24u + logical_exp)) +
169              (uint16_t)(((mant >> (-logical_exp - 2)) + 1) >>
170                         1));  // Round half away from zero for simplicity
171     } else {
172       res = (uint16_t)((val & 0x80000000u) >> 16u |
173                        ((((uint8_t)logical_exp) + 15u) << 10u) |
174                        (uint16_t)(mant >> 13u));
175     }
176   }
177   return _cbor_encode_uint16(res, buffer, buffer_size, 0xE0);
178 }
179 
cbor_encode_single(float value,unsigned char * buffer,size_t buffer_size)180 size_t cbor_encode_single(float value, unsigned char* buffer,
181                           size_t buffer_size) {
182   // Note: Values of signaling NaNs are discarded. There is no standard
183   // way to extract it without assumptions about the internal float
184   // representation.
185   if (isnan(value)) {
186     return _cbor_encode_uint32(0x7FC0 << 16, buffer, buffer_size, 0xE0);
187   }
188   // TODO: Broken on systems that do not use IEEE 754
189   return _cbor_encode_uint32(
190       ((union _cbor_float_helper){.as_float = value}).as_uint, buffer,
191       buffer_size, 0xE0);
192 }
193 
cbor_encode_double(double value,unsigned char * buffer,size_t buffer_size)194 size_t cbor_encode_double(double value, unsigned char* buffer,
195                           size_t buffer_size) {
196   // Note: Values of signaling NaNs are discarded. See `cbor_encode_single`.
197   if (isnan(value)) {
198     return _cbor_encode_uint64((uint64_t)0x7FF8 << 48, buffer, buffer_size,
199                                0xE0);
200   }
201   // TODO: Broken on systems that do not use IEEE 754
202   return _cbor_encode_uint64(
203       ((union _cbor_double_helper){.as_double = value}).as_uint, buffer,
204       buffer_size, 0xE0);
205 }
206 
cbor_encode_break(unsigned char * buffer,size_t buffer_size)207 size_t cbor_encode_break(unsigned char* buffer, size_t buffer_size) {
208   return _cbor_encode_byte(0xFF, buffer, buffer_size);
209 }
210 
cbor_encode_ctrl(uint8_t value,unsigned char * buffer,size_t buffer_size)211 size_t cbor_encode_ctrl(uint8_t value, unsigned char* buffer,
212                         size_t buffer_size) {
213   return _cbor_encode_uint8(value, buffer, buffer_size, 0xE0);
214 }
215