blob: 6111c8c54553115dc856301ea5586b04021f7b64 [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 Thompson5a984832013-07-01 19:28:27 -07007#include "../util/ndn_memory.h"
Jeff Thompson590b8692013-06-28 22:07:41 -07008#include "BinaryXML.h"
Jeff Thompsonc8963652013-06-28 20:17:43 -07009#include "BinaryXMLEncoder.h"
Jeff Thompson590b8692013-06-28 22:07:41 -070010
11enum {
12 ENCODING_LIMIT_1_BYTE = ((1 << ndn_BinaryXML_TT_VALUE_BITS) - 1),
13 ENCODING_LIMIT_2_BYTES = ((1 << (ndn_BinaryXML_TT_VALUE_BITS + ndn_BinaryXML_REGULAR_VALUE_BITS)) - 1),
14 ENCODING_LIMIT_3_BYTES = ((1 << (ndn_BinaryXML_TT_VALUE_BITS + 2 * ndn_BinaryXML_REGULAR_VALUE_BITS)) - 1)
15};
16
17/**
Jeff Thompson5a984832013-07-01 19:28:27 -070018 * Call ndn_DynamicUCharArray_ensureLength to ensure that there is enough room in the output, and copy
19 * array to the output. This does not write a header.
20 * @param self pointer to the ndn_BinaryXMLEncoder struct
21 * @param array the array to copy
22 * @param arrayLength the length of the array
Jeff Thompson8b666002013-07-08 01:16:26 -070023 * @return 0 for success, else an error code
Jeff Thompson5a984832013-07-01 19:28:27 -070024 */
Jeff Thompson8b666002013-07-08 01:16:26 -070025static ndn_Error writeArray(struct ndn_BinaryXMLEncoder *self, unsigned char *array, unsigned int arrayLength)
Jeff Thompson5a984832013-07-01 19:28:27 -070026{
Jeff Thompson8b666002013-07-08 01:16:26 -070027 ndn_Error error;
Jeff Thompson5a984832013-07-01 19:28:27 -070028 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + arrayLength))
29 return error;
30
31 ndn_memcpy(self->output.array + self->offset, array, arrayLength);
32 self->offset += arrayLength;
33
34 return 0;
35}
36
37/**
Jeff Thompson590b8692013-06-28 22:07:41 -070038 * Return the number of bytes to encode a header of value x.
39 */
Jeff Thompsone2276892013-07-08 02:44:18 -070040static unsigned int getNHeaderEncodingBytes(unsigned int x)
Jeff Thompson590b8692013-06-28 22:07:41 -070041{
42 // Do a quick check for pre-compiled results.
43 if (x <= ENCODING_LIMIT_1_BYTE)
44 return 1;
45 if (x <= ENCODING_LIMIT_2_BYTES)
46 return 2;
47 if (x <= ENCODING_LIMIT_3_BYTES)
48 return 3;
49
50 unsigned int nBytes = 1;
51
52 // Last byte gives you TT_VALUE_BITS.
53 // Remainder each gives you REGULAR_VALUE_BITS.
54 x >>= ndn_BinaryXML_TT_VALUE_BITS;
55 while (x != 0) {
56 ++nBytes;
57 x >>= ndn_BinaryXML_REGULAR_VALUE_BITS;
58 }
59
60 return nBytes;
61}
Jeff Thompson433e6da2013-07-01 15:09:00 -070062
Jeff Thompson2c1d9212013-07-08 02:10:03 -070063/**
Jeff Thompsona259cc42013-07-08 17:14:09 -070064 * Reverse the length bytes in array.
Jeff Thompson2c1d9212013-07-08 02:10:03 -070065 * @param array
Jeff Thompson2c1d9212013-07-08 02:10:03 -070066 * @param length
67 */
Jeff Thompsona259cc42013-07-08 17:14:09 -070068static void reverse(unsigned char *array, unsigned int length)
Jeff Thompson2c1d9212013-07-08 02:10:03 -070069{
70 if (length == 0)
71 return;
72
Jeff Thompsona259cc42013-07-08 17:14:09 -070073 unsigned char *left = array;
74 unsigned char *right = array + length - 1;
Jeff Thompson2c1d9212013-07-08 02:10:03 -070075 while (left < right) {
76 // Swap.
77 unsigned char temp = *left;
78 *left = *right;
79 *right = temp;
80
81 ++left;
82 --right;
83 }
84}
85
86/**
87 * Write x as an unsigned decimal integer to the output, using ndn_DynamicUCharArray_ensureLength.
88 * This does not write a header.
89 * @param self pointer to the ndn_BinaryXMLEncoder struct
90 * @param x the unsigned int to write
91 * @return 0 for success, else an error code
92 */
Jeff Thompsone2276892013-07-08 02:44:18 -070093static ndn_Error encodeUnsignedDecimalInt(struct ndn_BinaryXMLEncoder *self, unsigned int x)
Jeff Thompson2c1d9212013-07-08 02:10:03 -070094{
95 // We write the value backwards, then reverse it.
96 unsigned int startOffset = self->offset;
97
98 while (1) {
99 ndn_Error error;
100 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + 1))
101 return error;
102
103 self->output.array[self->offset++] = (unsigned char)(x % 10 + '0');
104 x /= 10;
105
106 if (x == 0)
107 break;
108 }
109
110 // Now reverse.
Jeff Thompsona259cc42013-07-08 17:14:09 -0700111 reverse(self->output.array + startOffset, self->offset - startOffset);
112 return 0;
113}
114
115/**
116 * Like memcpy, copy length bytes from source to dest, assuming that the buffers can overlap and that
117 * we are shifting the buffer right.
118 * Don't use memcpy to shift because its behavior is not guaranteed when the buffers overlap.
119 * @param dest
120 * @param source
121 * @param length
122 */
123static void copyBufferRight(unsigned char *dest, unsigned char *source, unsigned int length)
124{
125 if (length == 0)
126 return;
127
128 // We are shifting right, so start from the end of the buffer.
129 unsigned char *from = source + length - 1;
130 unsigned char *to = dest + length - 1;
131 while (from >= source)
132 *(to--) = *(from--);
133}
134
135/**
136 * Shift a buffer in self->output.array right by the amount needed to prefix a header with type, then encode the header
137 * at startOffset.
138 * The buffer to shift right begins at startOffset and self->offset is at the end.
139 * @param self pointer to the ndn_BinaryXMLEncoder struct
140 * @param startOffset the offset in self->output.array of the start of the buffer to shift right
141 * @param type the header type
142 * @return 0 for success, else an error code
143 */
144static ndn_Error insertHeader
145 (struct ndn_BinaryXMLEncoder *self, unsigned int startOffset, unsigned int type)
146{
147 unsigned int nBufferBytes = self->offset - startOffset;
148 unsigned int nHeaderBytes = getNHeaderEncodingBytes(nBufferBytes);
149 ndn_Error error;
150 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + nHeaderBytes))
151 return error;
152
153 copyBufferRight(self->output.array + startOffset + nHeaderBytes, self->output.array + startOffset, nBufferBytes);
154
155 // Override the offset to force encodeTypeAndValue to encode at startOffset, then fix the offset.
156 self->offset = startOffset;
157 if (error = ndn_BinaryXMLEncoder_encodeTypeAndValue(self, ndn_BinaryXML_UDATA, nBufferBytes))
158 // We don't really expect to get an error, since we have already ensured the length.
159 return error;
160 self->offset = startOffset + nHeaderBytes + nBufferBytes;
161
Jeff Thompson2c1d9212013-07-08 02:10:03 -0700162 return 0;
163}
164
Jeff Thompson8b666002013-07-08 01:16:26 -0700165ndn_Error ndn_BinaryXMLEncoder_encodeTypeAndValue(struct ndn_BinaryXMLEncoder *self, unsigned int type, unsigned int value)
Jeff Thompson433e6da2013-07-01 15:09:00 -0700166{
167 if (type > ndn_BinaryXML_UDATA)
Jeff Thompson8b666002013-07-08 01:16:26 -0700168 return NDN_ERROR_header_type_is_out_of_range;
Jeff Thompson433e6da2013-07-01 15:09:00 -0700169
170 // Encode backwards. Calculate how many bytes we need.
Jeff Thompsone2276892013-07-08 02:44:18 -0700171 unsigned int nEncodingBytes = getNHeaderEncodingBytes(value);
Jeff Thompson8b666002013-07-08 01:16:26 -0700172 ndn_Error error;
Jeff Thompson433e6da2013-07-01 15:09:00 -0700173 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + nEncodingBytes))
174 return error;
175
176 // Bottom 4 bits of value go in last byte with tag.
177 self->output.array[self->offset + nEncodingBytes - 1] =
178 (ndn_BinaryXML_TT_MASK & type |
179 ((ndn_BinaryXML_TT_VALUE_MASK & value) << ndn_BinaryXML_TT_BITS)) |
180 ndn_BinaryXML_TT_FINAL; // set top bit for last byte
181 value >>= ndn_BinaryXML_TT_VALUE_BITS;
182
183 // Rest of value goes into preceding bytes, 7 bits per byte. (Zero top bit is "more" flag.)
184 unsigned int i = self->offset + nEncodingBytes - 2;
185 while (value != 0 && i >= self->offset) {
186 self->output.array[i] = (value & ndn_BinaryXML_REGULAR_VALUE_MASK);
187 value >>= ndn_BinaryXML_REGULAR_VALUE_BITS;
188 --i;
189 }
190 if (value != 0)
Jeff Thompsone2276892013-07-08 02:44:18 -0700191 // This should not happen if getNHeaderEncodingBytes is correct.
Jeff Thompson8b666002013-07-08 01:16:26 -0700192 return NDN_ERROR_encodeTypeAndValue_miscalculated_N_encoding_bytes;
Jeff Thompson433e6da2013-07-01 15:09:00 -0700193
194 self->offset+= nEncodingBytes;
195
196 return 0;
197}
Jeff Thompson5a984832013-07-01 19:28:27 -0700198
Jeff Thompson8b666002013-07-08 01:16:26 -0700199ndn_Error ndn_BinaryXMLEncoder_writeElementClose(struct ndn_BinaryXMLEncoder *self)
Jeff Thompson5a984832013-07-01 19:28:27 -0700200{
Jeff Thompson8b666002013-07-08 01:16:26 -0700201 ndn_Error error;
Jeff Thompson5a984832013-07-01 19:28:27 -0700202 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + 1))
203 return error;
204
205 self->output.array[self->offset] = ndn_BinaryXML_CLOSE;
206 self->offset += 1;
207
208 return 0;
209}
210
Jeff Thompson8b666002013-07-08 01:16:26 -0700211ndn_Error ndn_BinaryXMLEncoder_writeBlob(struct ndn_BinaryXMLEncoder *self, unsigned char *value, unsigned int valueLength)
Jeff Thompson5a984832013-07-01 19:28:27 -0700212{
Jeff Thompson8b666002013-07-08 01:16:26 -0700213 ndn_Error error;
Jeff Thompson5a984832013-07-01 19:28:27 -0700214 if (error = ndn_BinaryXMLEncoder_encodeTypeAndValue(self, ndn_BinaryXML_BLOB, valueLength))
215 return error;
216
217 if (error = writeArray(self, value, valueLength))
218 return error;
219
220 return 0;
221}
222
Jeff Thompson8b666002013-07-08 01:16:26 -0700223ndn_Error ndn_BinaryXMLEncoder_writeBlobDTagElement(struct ndn_BinaryXMLEncoder *self, unsigned int tag, unsigned char *value, unsigned int valueLength)
Jeff Thompson5a984832013-07-01 19:28:27 -0700224{
Jeff Thompson8b666002013-07-08 01:16:26 -0700225 ndn_Error error;
Jeff Thompson5a984832013-07-01 19:28:27 -0700226 if (error = ndn_BinaryXMLEncoder_writeElementStartDTag(self, tag))
227 return error;
228
229 if (error = ndn_BinaryXMLEncoder_writeBlob(self, value, valueLength))
230 return error;
231
232 if (error = ndn_BinaryXMLEncoder_writeElementClose(self))
233 return error;
234
235 return 0;
236}
Jeff Thompsone2276892013-07-08 02:44:18 -0700237
238ndn_Error ndn_BinaryXMLEncoder_writeUnsignedDecimalInt(struct ndn_BinaryXMLEncoder *self, unsigned int value)
239{
240 // First write the decimal int (to find out how many bytes it is), then shift it forward to make room for the header.
241 unsigned int startOffset = self->offset;
242
243 ndn_Error error;
244 if (error = encodeUnsignedDecimalInt(self, value))
245 return error;
246
Jeff Thompsona259cc42013-07-08 17:14:09 -0700247 if (error = insertHeader(self, startOffset, ndn_BinaryXML_UDATA))
Jeff Thompsone2276892013-07-08 02:44:18 -0700248 return error;
249
Jeff Thompsone2276892013-07-08 02:44:18 -0700250 return 0;
251}
Jeff Thompson5b696e02013-07-08 15:04:22 -0700252
253ndn_Error ndn_BinaryXMLEncoder_writeUnsignedDecimalIntDTagElement(struct ndn_BinaryXMLEncoder *self, unsigned int tag, unsigned int value)
254{
255 ndn_Error error;
256 if (error = ndn_BinaryXMLEncoder_writeElementStartDTag(self, tag))
257 return error;
258
259 if (error = ndn_BinaryXMLEncoder_writeUnsignedDecimalInt(self, value))
260 return error;
261
262 if (error = ndn_BinaryXMLEncoder_writeElementClose(self))
263 return error;
264
265 return 0;
266}
Jeff Thompsona259cc42013-07-08 17:14:09 -0700267
268ndn_Error ndn_BinaryXMLEncoder_writeUnsignedIntBigEndianBlob(struct ndn_BinaryXMLEncoder *self, unsigned int value)
269{
270 // First encode the big endian backwards, then reverse it.
271 unsigned int startOffset = self->offset;
272 ndn_Error error;
273 while (value != 0) {
274 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + 1))
275 return error;
276
277 self->output.array[self->offset++] = (unsigned char)(value & 0xff);
278 value >>= 8;
279 }
280
281 unsigned int bigEndianLength = self->offset - startOffset;
282 reverse(self->output.array + startOffset, bigEndianLength);
283
284 if (error = insertHeader(self, startOffset, ndn_BinaryXML_BLOB))
285 return error;
286
287 return 0;
288}