/src/libcbor/src/cbor/encoding.c
Line | Count | Source (jump to first uncovered line) |
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 | | #include "internal/encoders.h" |
10 | | |
11 | | size_t cbor_encode_uint8(uint8_t value, unsigned char *buffer, |
12 | 2.09M | size_t buffer_size) { |
13 | 2.09M | return _cbor_encode_uint8(value, buffer, buffer_size, 0x00); |
14 | 2.09M | } |
15 | | |
16 | | size_t cbor_encode_uint16(uint16_t value, unsigned char *buffer, |
17 | 23.7k | size_t buffer_size) { |
18 | 23.7k | return _cbor_encode_uint16(value, buffer, buffer_size, 0x00); |
19 | 23.7k | } |
20 | | |
21 | | size_t cbor_encode_uint32(uint32_t value, unsigned char *buffer, |
22 | 0 | size_t buffer_size) { |
23 | 0 | return _cbor_encode_uint32(value, buffer, buffer_size, 0x00); |
24 | 0 | } |
25 | | |
26 | | size_t cbor_encode_uint64(uint64_t value, unsigned char *buffer, |
27 | 300 | size_t buffer_size) { |
28 | 300 | return _cbor_encode_uint64(value, buffer, buffer_size, 0x00); |
29 | 300 | } |
30 | | |
31 | | size_t cbor_encode_uint(uint64_t value, unsigned char *buffer, |
32 | 0 | size_t buffer_size) { |
33 | 0 | return _cbor_encode_uint(value, buffer, buffer_size, 0x00); |
34 | 0 | } |
35 | | |
36 | | size_t cbor_encode_negint8(uint8_t value, unsigned char *buffer, |
37 | 223 | size_t buffer_size) { |
38 | 223 | return _cbor_encode_uint8(value, buffer, buffer_size, 0x20); |
39 | 223 | } |
40 | | |
41 | | size_t cbor_encode_negint16(uint16_t value, unsigned char *buffer, |
42 | 0 | size_t buffer_size) { |
43 | 0 | return _cbor_encode_uint16(value, buffer, buffer_size, 0x20); |
44 | 0 | } |
45 | | |
46 | | size_t cbor_encode_negint32(uint32_t value, unsigned char *buffer, |
47 | 0 | size_t buffer_size) { |
48 | 0 | return _cbor_encode_uint32(value, buffer, buffer_size, 0x20); |
49 | 0 | } |
50 | | |
51 | | size_t cbor_encode_negint64(uint64_t value, unsigned char *buffer, |
52 | 0 | size_t buffer_size) { |
53 | 0 | return _cbor_encode_uint64(value, buffer, buffer_size, 0x20); |
54 | 0 | } |
55 | | |
56 | | size_t cbor_encode_negint(uint64_t value, unsigned char *buffer, |
57 | 0 | size_t buffer_size) { |
58 | 0 | return _cbor_encode_uint(value, buffer, buffer_size, 0x20); |
59 | 0 | } |
60 | | |
61 | | size_t cbor_encode_bytestring_start(size_t length, unsigned char *buffer, |
62 | 85.8k | size_t buffer_size) { |
63 | 85.8k | return _cbor_encode_uint((size_t)length, buffer, buffer_size, 0x40); |
64 | 85.8k | } |
65 | | |
66 | | size_t _cbor_encode_byte(uint8_t value, unsigned char *buffer, |
67 | 2.15M | size_t buffer_size) { |
68 | 2.15M | if (buffer_size >= 1) { |
69 | 2.15M | buffer[0] = value; |
70 | 2.15M | return 1; |
71 | 2.15M | } else |
72 | 0 | return 0; |
73 | 2.15M | } |
74 | | |
75 | | size_t cbor_encode_indef_bytestring_start(unsigned char *buffer, |
76 | 0 | size_t buffer_size) { |
77 | 0 | return _cbor_encode_byte(0x5F, buffer, buffer_size); |
78 | 0 | } |
79 | | |
80 | | size_t cbor_encode_string_start(size_t length, unsigned char *buffer, |
81 | 63.4k | size_t buffer_size) { |
82 | 63.4k | return _cbor_encode_uint((size_t)length, buffer, buffer_size, 0x60); |
83 | 63.4k | } |
84 | | |
85 | | size_t cbor_encode_indef_string_start(unsigned char *buffer, |
86 | 0 | size_t buffer_size) { |
87 | 0 | return _cbor_encode_byte(0x7F, buffer, buffer_size); |
88 | 0 | } |
89 | | |
90 | | size_t cbor_encode_array_start(size_t length, unsigned char *buffer, |
91 | 85.3k | size_t buffer_size) { |
92 | 85.3k | return _cbor_encode_uint((size_t)length, buffer, buffer_size, 0x80); |
93 | 85.3k | } |
94 | | |
95 | | size_t cbor_encode_indef_array_start(unsigned char *buffer, |
96 | 12.4k | size_t buffer_size) { |
97 | 12.4k | return _cbor_encode_byte(0x9F, buffer, buffer_size); |
98 | 12.4k | } |
99 | | |
100 | | size_t cbor_encode_map_start(size_t length, unsigned char *buffer, |
101 | 0 | size_t buffer_size) { |
102 | 0 | return _cbor_encode_uint((size_t)length, buffer, buffer_size, 0xA0); |
103 | 0 | } |
104 | | |
105 | 1.06M | size_t cbor_encode_indef_map_start(unsigned char *buffer, size_t buffer_size) { |
106 | 1.06M | return _cbor_encode_byte(0xBF, buffer, buffer_size); |
107 | 1.06M | } |
108 | | |
109 | | size_t cbor_encode_tag(uint64_t value, unsigned char *buffer, |
110 | 0 | size_t buffer_size) { |
111 | 0 | return _cbor_encode_uint(value, buffer, buffer_size, 0xC0); |
112 | 0 | } |
113 | | |
114 | 0 | size_t cbor_encode_bool(bool value, unsigned char *buffer, size_t buffer_size) { |
115 | 0 | return value ? _cbor_encode_byte(0xF5, buffer, buffer_size) |
116 | 0 | : _cbor_encode_byte(0xF4, buffer, buffer_size); |
117 | 0 | } |
118 | | |
119 | 0 | size_t cbor_encode_null(unsigned char *buffer, size_t buffer_size) { |
120 | 0 | return _cbor_encode_byte(0xF6, buffer, buffer_size); |
121 | 0 | } |
122 | | |
123 | 0 | size_t cbor_encode_undef(unsigned char *buffer, size_t buffer_size) { |
124 | 0 | return _cbor_encode_byte(0xF7, buffer, buffer_size); |
125 | 0 | } |
126 | | |
127 | | size_t cbor_encode_half(float value, unsigned char *buffer, |
128 | 0 | size_t buffer_size) { |
129 | | /* Assuming value is normalized */ |
130 | 0 | uint32_t val = ((union _cbor_float_helper){.as_float = value}).as_uint; |
131 | 0 | uint16_t res; |
132 | 0 | uint8_t exp = (uint8_t)((val & 0x7F800000u) >> |
133 | 0 | 23u); /* 0b0111_1111_1000_0000_0000_0000_0000_0000 */ |
134 | 0 | uint32_t mant = |
135 | 0 | val & 0x7FFFFFu; /* 0b0000_0000_0111_1111_1111_1111_1111_1111 */ |
136 | 0 | if (exp == 0xFF) { /* Infinity or NaNs */ |
137 | 0 | if (value != value) { |
138 | | // We discard information bits in half-float NaNs. This is |
139 | | // not required for the core CBOR protocol (it is only a suggestion in |
140 | | // Section 3.9). |
141 | | // See https://github.com/PJK/libcbor/issues/215 |
142 | 0 | res = (uint16_t)0x007e00; |
143 | 0 | } else { |
144 | | // If the mantissa is non-zero, we have a NaN, but those are handled |
145 | | // above. See |
146 | | // https://en.wikipedia.org/wiki/Half-precision_floating-point_format |
147 | 0 | CBOR_ASSERT(mant == 0u); |
148 | 0 | res = (uint16_t)((val & 0x80000000u) >> 16u | 0x7C00u); |
149 | 0 | } |
150 | 0 | } else if (exp == 0x00) { /* Zeroes or subnorms */ |
151 | 0 | res = (uint16_t)((val & 0x80000000u) >> 16u | mant >> 13u); |
152 | 0 | } else { /* Normal numbers */ |
153 | 0 | int8_t logical_exp = (int8_t)(exp - 127); |
154 | 0 | CBOR_ASSERT(logical_exp == exp - 127); |
155 | | |
156 | | // Now we know that 2^exp <= 0 logically |
157 | 0 | if (logical_exp < -24) { |
158 | | /* No unambiguous representation exists, this float is not a half float |
159 | | and is too small to be represented using a half, round off to zero. |
160 | | Consistent with the reference implementation. */ |
161 | 0 | res = 0; |
162 | 0 | } else if (logical_exp < -14) { |
163 | | /* Offset the remaining decimal places by shifting the significand, the |
164 | | value is lost. This is an implementation decision that works around the |
165 | | absence of standard half-float in the language. */ |
166 | 0 | res = (uint16_t)((val & 0x80000000u) >> 16u) | // Extract sign bit |
167 | 0 | ((uint16_t)(1u << (24u + logical_exp)) + |
168 | 0 | (uint16_t)(((mant >> (-logical_exp - 2)) + 1) >> |
169 | 0 | 1)); // Round half away from zero for simplicity |
170 | 0 | } else { |
171 | 0 | res = (uint16_t)((val & 0x80000000u) >> 16u | |
172 | 0 | ((((uint8_t)logical_exp) + 15u) << 10u) | |
173 | 0 | (uint16_t)(mant >> 13u)); |
174 | 0 | } |
175 | 0 | } |
176 | 0 | return _cbor_encode_uint16(res, buffer, buffer_size, 0xE0); |
177 | 0 | } |
178 | | |
179 | | size_t cbor_encode_single(float value, unsigned char *buffer, |
180 | 0 | size_t buffer_size) { |
181 | 0 | return _cbor_encode_uint32( |
182 | 0 | ((union _cbor_float_helper){.as_float = value}).as_uint, buffer, |
183 | 0 | buffer_size, 0xE0); |
184 | 0 | } |
185 | | |
186 | | size_t cbor_encode_double(double value, unsigned char *buffer, |
187 | 0 | size_t buffer_size) { |
188 | 0 | return _cbor_encode_uint64( |
189 | 0 | ((union _cbor_double_helper){.as_double = value}).as_uint, buffer, |
190 | 0 | buffer_size, 0xE0); |
191 | 0 | } |
192 | | |
193 | 1.07M | size_t cbor_encode_break(unsigned char *buffer, size_t buffer_size) { |
194 | 1.07M | return _cbor_encode_byte(0xFF, buffer, buffer_size); |
195 | 1.07M | } |
196 | | |
197 | | size_t cbor_encode_ctrl(uint8_t value, unsigned char *buffer, |
198 | 24.0k | size_t buffer_size) { |
199 | 24.0k | return _cbor_encode_uint8(value, buffer, buffer_size, 0xE0); |
200 | 24.0k | } |