blob: b7f1db1470703a8d3086658ffbb00555d5498d6f [file] [log] [blame]
Jeff Thompson47eecfc2013-07-07 22:56:46 -07001/**
2 * @author: Jeff Thompson
Jeff Thompsone5b37a52013-07-08 15:39:01 -07003 * Derived from BinaryXMLEncoder.js by Meki Cheraoui.
Jeff Thompson47eecfc2013-07-07 22:56:46 -07004 * See COPYING for copyright and distribution information.
Jeff Thompsonc8963652013-06-28 20:17:43 -07005 */
6
Jeff Thompsonedc22252013-07-11 18:05:44 -07007#include <math.h>
Jeff Thompson5a984832013-07-01 19:28:27 -07008#include "../util/ndn_memory.h"
Jeff Thompson590b8692013-06-28 22:07:41 -07009#include "BinaryXML.h"
Jeff Thompsonc8963652013-06-28 20:17:43 -070010#include "BinaryXMLEncoder.h"
Jeff Thompson590b8692013-06-28 22:07:41 -070011
12enum {
13 ENCODING_LIMIT_1_BYTE = ((1 << ndn_BinaryXML_TT_VALUE_BITS) - 1),
14 ENCODING_LIMIT_2_BYTES = ((1 << (ndn_BinaryXML_TT_VALUE_BITS + ndn_BinaryXML_REGULAR_VALUE_BITS)) - 1),
15 ENCODING_LIMIT_3_BYTES = ((1 << (ndn_BinaryXML_TT_VALUE_BITS + 2 * ndn_BinaryXML_REGULAR_VALUE_BITS)) - 1)
16};
17
18/**
Jeff Thompson5a984832013-07-01 19:28:27 -070019 * Call ndn_DynamicUCharArray_ensureLength to ensure that there is enough room in the output, and copy
20 * array to the output. This does not write a header.
21 * @param self pointer to the ndn_BinaryXMLEncoder struct
22 * @param array the array to copy
23 * @param arrayLength the length of the array
Jeff Thompson8b666002013-07-08 01:16:26 -070024 * @return 0 for success, else an error code
Jeff Thompson5a984832013-07-01 19:28:27 -070025 */
Jeff Thompson8b666002013-07-08 01:16:26 -070026static ndn_Error writeArray(struct ndn_BinaryXMLEncoder *self, unsigned char *array, unsigned int arrayLength)
Jeff Thompson5a984832013-07-01 19:28:27 -070027{
Jeff Thompson8b666002013-07-08 01:16:26 -070028 ndn_Error error;
Jeff Thompson5a984832013-07-01 19:28:27 -070029 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + arrayLength))
30 return error;
31
32 ndn_memcpy(self->output.array + self->offset, array, arrayLength);
33 self->offset += arrayLength;
34
35 return 0;
36}
37
38/**
Jeff Thompson590b8692013-06-28 22:07:41 -070039 * Return the number of bytes to encode a header of value x.
40 */
Jeff Thompsone2276892013-07-08 02:44:18 -070041static unsigned int getNHeaderEncodingBytes(unsigned int x)
Jeff Thompson590b8692013-06-28 22:07:41 -070042{
43 // Do a quick check for pre-compiled results.
44 if (x <= ENCODING_LIMIT_1_BYTE)
45 return 1;
46 if (x <= ENCODING_LIMIT_2_BYTES)
47 return 2;
48 if (x <= ENCODING_LIMIT_3_BYTES)
49 return 3;
50
51 unsigned int nBytes = 1;
52
53 // Last byte gives you TT_VALUE_BITS.
54 // Remainder each gives you REGULAR_VALUE_BITS.
55 x >>= ndn_BinaryXML_TT_VALUE_BITS;
56 while (x != 0) {
57 ++nBytes;
58 x >>= ndn_BinaryXML_REGULAR_VALUE_BITS;
59 }
60
61 return nBytes;
62}
Jeff Thompson433e6da2013-07-01 15:09:00 -070063
Jeff Thompson2c1d9212013-07-08 02:10:03 -070064/**
Jeff Thompsona259cc42013-07-08 17:14:09 -070065 * Reverse the length bytes in array.
Jeff Thompson2c1d9212013-07-08 02:10:03 -070066 * @param array
Jeff Thompson2c1d9212013-07-08 02:10:03 -070067 * @param length
68 */
Jeff Thompsona259cc42013-07-08 17:14:09 -070069static void reverse(unsigned char *array, unsigned int length)
Jeff Thompson2c1d9212013-07-08 02:10:03 -070070{
71 if (length == 0)
72 return;
73
Jeff Thompsona259cc42013-07-08 17:14:09 -070074 unsigned char *left = array;
75 unsigned char *right = array + length - 1;
Jeff Thompson2c1d9212013-07-08 02:10:03 -070076 while (left < right) {
77 // Swap.
78 unsigned char temp = *left;
79 *left = *right;
80 *right = temp;
81
82 ++left;
83 --right;
84 }
85}
86
87/**
Jeff Thompson1cb79eb2013-07-08 17:59:01 -070088 * Write x as an unsigned decimal integer to the output with the digits in reverse order, using ndn_DynamicUCharArray_ensureLength.
Jeff Thompson2c1d9212013-07-08 02:10:03 -070089 * This does not write a header.
Jeff Thompson1cb79eb2013-07-08 17:59:01 -070090 * We encode in reverse order, because this is the natural way to encode the digits, and the caller can reverse as needed.
Jeff Thompson2c1d9212013-07-08 02:10:03 -070091 * @param self pointer to the ndn_BinaryXMLEncoder struct
92 * @param x the unsigned int to write
93 * @return 0 for success, else an error code
94 */
Jeff Thompson1cb79eb2013-07-08 17:59:01 -070095static ndn_Error encodeReversedUnsignedDecimalInt(struct ndn_BinaryXMLEncoder *self, unsigned int x)
Jeff Thompson2c1d9212013-07-08 02:10:03 -070096{
Jeff Thompson2c1d9212013-07-08 02:10:03 -070097 while (1) {
98 ndn_Error error;
99 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + 1))
100 return error;
101
102 self->output.array[self->offset++] = (unsigned char)(x % 10 + '0');
103 x /= 10;
104
105 if (x == 0)
106 break;
107 }
108
Jeff Thompsona259cc42013-07-08 17:14:09 -0700109 return 0;
110}
111
112/**
Jeff Thompson1cb79eb2013-07-08 17:59:01 -0700113 * Reverse the buffer in self->output.array, then shift it right by the amount needed to prefix a header with type,
114 * then encode the header at startOffset.
115 * startOffser it the position in self-output.array of the first byte of the buffer and self->offset is the first byte past the end.
116 * We reverse and shift in the same function to avoid unnecessary copying if we first reverse then shift.
Jeff Thompsona259cc42013-07-08 17:14:09 -0700117 * @param self pointer to the ndn_BinaryXMLEncoder struct
118 * @param startOffset the offset in self->output.array of the start of the buffer to shift right
119 * @param type the header type
120 * @return 0 for success, else an error code
121 */
Jeff Thompson1cb79eb2013-07-08 17:59:01 -0700122static ndn_Error reverseBufferAndInsertHeader
Jeff Thompsona259cc42013-07-08 17:14:09 -0700123 (struct ndn_BinaryXMLEncoder *self, unsigned int startOffset, unsigned int type)
124{
125 unsigned int nBufferBytes = self->offset - startOffset;
126 unsigned int nHeaderBytes = getNHeaderEncodingBytes(nBufferBytes);
127 ndn_Error error;
128 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + nHeaderBytes))
129 return error;
130
Jeff Thompson1cb79eb2013-07-08 17:59:01 -0700131 // To reverse and shift at the same time, we first shift nHeaderBytes to the destination while reversing,
132 // then reverse the remaining bytes in place.
133 unsigned char *from = self->output.array + startOffset;
134 unsigned char *fromEnd = from + nHeaderBytes;
135 unsigned char *to = self->output.array + startOffset + nBufferBytes + nHeaderBytes - 1;
136 while (from < fromEnd)
137 *(to--) = *(from++);
138 // Reverse the remaining bytes in place (if any).
139 if (nBufferBytes > nHeaderBytes)
140 reverse(self->output.array + startOffset + nHeaderBytes, nBufferBytes - nHeaderBytes);
Jeff Thompsona259cc42013-07-08 17:14:09 -0700141
142 // Override the offset to force encodeTypeAndValue to encode at startOffset, then fix the offset.
143 self->offset = startOffset;
144 if (error = ndn_BinaryXMLEncoder_encodeTypeAndValue(self, ndn_BinaryXML_UDATA, nBufferBytes))
145 // We don't really expect to get an error, since we have already ensured the length.
146 return error;
147 self->offset = startOffset + nHeaderBytes + nBufferBytes;
148
Jeff Thompson2c1d9212013-07-08 02:10:03 -0700149 return 0;
150}
151
Jeff Thompsonedc22252013-07-11 18:05:44 -0700152/**
153 * Split the absolute value of x into 32 bit unsigned integers hi32 and lo32.
154 * We need this because not all C compilers support 64 bit long long integers, so we carry around
155 * a high precision value as a double, which we assume has more than 32 bits.
156 * But we want to do bit-wise operations on integers.
157 * @param x the double value
158 * @param hi32 output the high 32 bits
159 * @param lo32 output the low 32 bits
160 */
161static inline void splitAbsDouble(double x, unsigned long *hi32, unsigned long *lo32)
162{
163 if (x < 0)
164 x = -x;
165 x = round(x);
166
167 double twoPower32 = 4294967296.0;
168 double lo32Double = fmod(x, twoPower32);
169 *lo32 = (unsigned long)lo32Double;
170 *hi32 = (unsigned long)((x - lo32Double) / twoPower32);
171}
172
Jeff Thompson8b666002013-07-08 01:16:26 -0700173ndn_Error ndn_BinaryXMLEncoder_encodeTypeAndValue(struct ndn_BinaryXMLEncoder *self, unsigned int type, unsigned int value)
Jeff Thompson433e6da2013-07-01 15:09:00 -0700174{
175 if (type > ndn_BinaryXML_UDATA)
Jeff Thompson8b666002013-07-08 01:16:26 -0700176 return NDN_ERROR_header_type_is_out_of_range;
Jeff Thompson433e6da2013-07-01 15:09:00 -0700177
178 // Encode backwards. Calculate how many bytes we need.
Jeff Thompsone2276892013-07-08 02:44:18 -0700179 unsigned int nEncodingBytes = getNHeaderEncodingBytes(value);
Jeff Thompson8b666002013-07-08 01:16:26 -0700180 ndn_Error error;
Jeff Thompson433e6da2013-07-01 15:09:00 -0700181 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + nEncodingBytes))
182 return error;
183
184 // Bottom 4 bits of value go in last byte with tag.
185 self->output.array[self->offset + nEncodingBytes - 1] =
186 (ndn_BinaryXML_TT_MASK & type |
187 ((ndn_BinaryXML_TT_VALUE_MASK & value) << ndn_BinaryXML_TT_BITS)) |
188 ndn_BinaryXML_TT_FINAL; // set top bit for last byte
189 value >>= ndn_BinaryXML_TT_VALUE_BITS;
190
191 // Rest of value goes into preceding bytes, 7 bits per byte. (Zero top bit is "more" flag.)
192 unsigned int i = self->offset + nEncodingBytes - 2;
193 while (value != 0 && i >= self->offset) {
194 self->output.array[i] = (value & ndn_BinaryXML_REGULAR_VALUE_MASK);
195 value >>= ndn_BinaryXML_REGULAR_VALUE_BITS;
196 --i;
197 }
198 if (value != 0)
Jeff Thompsone2276892013-07-08 02:44:18 -0700199 // This should not happen if getNHeaderEncodingBytes is correct.
Jeff Thompson8b666002013-07-08 01:16:26 -0700200 return NDN_ERROR_encodeTypeAndValue_miscalculated_N_encoding_bytes;
Jeff Thompson433e6da2013-07-01 15:09:00 -0700201
202 self->offset+= nEncodingBytes;
203
204 return 0;
205}
Jeff Thompson5a984832013-07-01 19:28:27 -0700206
Jeff Thompson8b666002013-07-08 01:16:26 -0700207ndn_Error ndn_BinaryXMLEncoder_writeElementClose(struct ndn_BinaryXMLEncoder *self)
Jeff Thompson5a984832013-07-01 19:28:27 -0700208{
Jeff Thompson8b666002013-07-08 01:16:26 -0700209 ndn_Error error;
Jeff Thompson5a984832013-07-01 19:28:27 -0700210 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + 1))
211 return error;
212
213 self->output.array[self->offset] = ndn_BinaryXML_CLOSE;
214 self->offset += 1;
215
216 return 0;
217}
218
Jeff Thompson8b666002013-07-08 01:16:26 -0700219ndn_Error ndn_BinaryXMLEncoder_writeBlob(struct ndn_BinaryXMLEncoder *self, unsigned char *value, unsigned int valueLength)
Jeff Thompson5a984832013-07-01 19:28:27 -0700220{
Jeff Thompson8b666002013-07-08 01:16:26 -0700221 ndn_Error error;
Jeff Thompson5a984832013-07-01 19:28:27 -0700222 if (error = ndn_BinaryXMLEncoder_encodeTypeAndValue(self, ndn_BinaryXML_BLOB, valueLength))
223 return error;
224
225 if (error = writeArray(self, value, valueLength))
226 return error;
227
228 return 0;
229}
230
Jeff Thompson8b666002013-07-08 01:16:26 -0700231ndn_Error ndn_BinaryXMLEncoder_writeBlobDTagElement(struct ndn_BinaryXMLEncoder *self, unsigned int tag, unsigned char *value, unsigned int valueLength)
Jeff Thompson5a984832013-07-01 19:28:27 -0700232{
Jeff Thompson8b666002013-07-08 01:16:26 -0700233 ndn_Error error;
Jeff Thompson5a984832013-07-01 19:28:27 -0700234 if (error = ndn_BinaryXMLEncoder_writeElementStartDTag(self, tag))
235 return error;
236
237 if (error = ndn_BinaryXMLEncoder_writeBlob(self, value, valueLength))
238 return error;
239
240 if (error = ndn_BinaryXMLEncoder_writeElementClose(self))
241 return error;
242
243 return 0;
244}
Jeff Thompsone2276892013-07-08 02:44:18 -0700245
246ndn_Error ndn_BinaryXMLEncoder_writeUnsignedDecimalInt(struct ndn_BinaryXMLEncoder *self, unsigned int value)
247{
248 // First write the decimal int (to find out how many bytes it is), then shift it forward to make room for the header.
249 unsigned int startOffset = self->offset;
250
251 ndn_Error error;
Jeff Thompson1cb79eb2013-07-08 17:59:01 -0700252 if (error = encodeReversedUnsignedDecimalInt(self, value))
Jeff Thompsone2276892013-07-08 02:44:18 -0700253 return error;
254
Jeff Thompson1cb79eb2013-07-08 17:59:01 -0700255 if (error = reverseBufferAndInsertHeader(self, startOffset, ndn_BinaryXML_UDATA))
Jeff Thompsone2276892013-07-08 02:44:18 -0700256 return error;
257
Jeff Thompsone2276892013-07-08 02:44:18 -0700258 return 0;
259}
Jeff Thompson5b696e02013-07-08 15:04:22 -0700260
261ndn_Error ndn_BinaryXMLEncoder_writeUnsignedDecimalIntDTagElement(struct ndn_BinaryXMLEncoder *self, unsigned int tag, unsigned int value)
262{
263 ndn_Error error;
264 if (error = ndn_BinaryXMLEncoder_writeElementStartDTag(self, tag))
265 return error;
266
267 if (error = ndn_BinaryXMLEncoder_writeUnsignedDecimalInt(self, value))
268 return error;
269
270 if (error = ndn_BinaryXMLEncoder_writeElementClose(self))
271 return error;
272
273 return 0;
274}
Jeff Thompsona259cc42013-07-08 17:14:09 -0700275
Jeff Thompsonedc22252013-07-11 18:05:44 -0700276ndn_Error ndn_BinaryXMLEncoder_writeAbsDoubleBigEndianBlob(struct ndn_BinaryXMLEncoder *self, double value)
Jeff Thompsona259cc42013-07-08 17:14:09 -0700277{
Jeff Thompsonedc22252013-07-11 18:05:44 -0700278 unsigned long hi32, lo32;
279 splitAbsDouble(value, &hi32, &lo32);
280
281 // First encode the big endian backwards, then reverseBufferAndInsertHeader will reverse it.
Jeff Thompsona259cc42013-07-08 17:14:09 -0700282 unsigned int startOffset = self->offset;
Jeff Thompsonedc22252013-07-11 18:05:44 -0700283
Jeff Thompsona259cc42013-07-08 17:14:09 -0700284 ndn_Error error;
Jeff Thompsonedc22252013-07-11 18:05:44 -0700285 while (lo32 != 0) {
Jeff Thompsona259cc42013-07-08 17:14:09 -0700286 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + 1))
287 return error;
288
Jeff Thompsonedc22252013-07-11 18:05:44 -0700289 self->output.array[self->offset++] = (unsigned char)(lo32 & 0xff);
290 lo32 >>= 8;
291 }
292
293 if (hi32 != 0) {
294 // Pad the lo values out to 4 bytes.
295 while (self->offset - startOffset < 4) {
296 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + 1))
297 return error;
298
299 self->output.array[self->offset++] = 0;
300 }
301
302 // Encode hi32
303 while (hi32 != 0) {
304 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + 1))
305 return error;
306
307 self->output.array[self->offset++] = (unsigned char)(hi32 & 0xff);
308 hi32 >>= 8;
309 }
Jeff Thompsona259cc42013-07-08 17:14:09 -0700310 }
311
Jeff Thompson1cb79eb2013-07-08 17:59:01 -0700312 if (error = reverseBufferAndInsertHeader(self, startOffset, ndn_BinaryXML_BLOB))
Jeff Thompsona259cc42013-07-08 17:14:09 -0700313 return error;
314
315 return 0;
316}
Jeff Thompsonedc22252013-07-11 18:05:44 -0700317
318ndn_Error ndn_BinaryXMLEncoder_writeTimeMillisecondsDTagElement(struct ndn_BinaryXMLEncoder *self, unsigned int tag, double milliseconds)
319{
320 ndn_Error error;
321 if (error = ndn_BinaryXMLEncoder_writeElementStartDTag(self, tag))
322 return error;
323
324 if (error = ndn_BinaryXMLEncoder_writeAbsDoubleBigEndianBlob(self, (milliseconds / 1000.0) * 4096.0))
325 return error;
326
327 if (error = ndn_BinaryXMLEncoder_writeElementClose(self))
328 return error;
329
330 return 0;
331}