blob: c2ca1546f7b37d18bdb21bb6b6842f70c81e1388 [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/**
Jeff Thompson1cb79eb2013-07-08 17:59:01 -070087 * 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 -070088 * This does not write a header.
Jeff Thompson1cb79eb2013-07-08 17:59:01 -070089 * 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 -070090 * @param self pointer to the ndn_BinaryXMLEncoder struct
91 * @param x the unsigned int to write
92 * @return 0 for success, else an error code
93 */
Jeff Thompson1cb79eb2013-07-08 17:59:01 -070094static ndn_Error encodeReversedUnsignedDecimalInt(struct ndn_BinaryXMLEncoder *self, unsigned int x)
Jeff Thompson2c1d9212013-07-08 02:10:03 -070095{
Jeff Thompson2c1d9212013-07-08 02:10:03 -070096 while (1) {
97 ndn_Error error;
98 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + 1))
99 return error;
100
101 self->output.array[self->offset++] = (unsigned char)(x % 10 + '0');
102 x /= 10;
103
104 if (x == 0)
105 break;
106 }
107
Jeff Thompsona259cc42013-07-08 17:14:09 -0700108 return 0;
109}
110
111/**
Jeff Thompson1cb79eb2013-07-08 17:59:01 -0700112 * Reverse the buffer in self->output.array, then shift it right by the amount needed to prefix a header with type,
113 * then encode the header at startOffset.
114 * 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.
115 * 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 -0700116 * @param self pointer to the ndn_BinaryXMLEncoder struct
117 * @param startOffset the offset in self->output.array of the start of the buffer to shift right
118 * @param type the header type
119 * @return 0 for success, else an error code
120 */
Jeff Thompson1cb79eb2013-07-08 17:59:01 -0700121static ndn_Error reverseBufferAndInsertHeader
Jeff Thompsona259cc42013-07-08 17:14:09 -0700122 (struct ndn_BinaryXMLEncoder *self, unsigned int startOffset, unsigned int type)
123{
124 unsigned int nBufferBytes = self->offset - startOffset;
125 unsigned int nHeaderBytes = getNHeaderEncodingBytes(nBufferBytes);
126 ndn_Error error;
127 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + nHeaderBytes))
128 return error;
129
Jeff Thompson1cb79eb2013-07-08 17:59:01 -0700130 // To reverse and shift at the same time, we first shift nHeaderBytes to the destination while reversing,
131 // then reverse the remaining bytes in place.
132 unsigned char *from = self->output.array + startOffset;
133 unsigned char *fromEnd = from + nHeaderBytes;
134 unsigned char *to = self->output.array + startOffset + nBufferBytes + nHeaderBytes - 1;
135 while (from < fromEnd)
136 *(to--) = *(from++);
137 // Reverse the remaining bytes in place (if any).
138 if (nBufferBytes > nHeaderBytes)
139 reverse(self->output.array + startOffset + nHeaderBytes, nBufferBytes - nHeaderBytes);
Jeff Thompsona259cc42013-07-08 17:14:09 -0700140
141 // Override the offset to force encodeTypeAndValue to encode at startOffset, then fix the offset.
142 self->offset = startOffset;
143 if (error = ndn_BinaryXMLEncoder_encodeTypeAndValue(self, ndn_BinaryXML_UDATA, nBufferBytes))
144 // We don't really expect to get an error, since we have already ensured the length.
145 return error;
146 self->offset = startOffset + nHeaderBytes + nBufferBytes;
147
Jeff Thompson2c1d9212013-07-08 02:10:03 -0700148 return 0;
149}
150
Jeff Thompson8b666002013-07-08 01:16:26 -0700151ndn_Error ndn_BinaryXMLEncoder_encodeTypeAndValue(struct ndn_BinaryXMLEncoder *self, unsigned int type, unsigned int value)
Jeff Thompson433e6da2013-07-01 15:09:00 -0700152{
153 if (type > ndn_BinaryXML_UDATA)
Jeff Thompson8b666002013-07-08 01:16:26 -0700154 return NDN_ERROR_header_type_is_out_of_range;
Jeff Thompson433e6da2013-07-01 15:09:00 -0700155
156 // Encode backwards. Calculate how many bytes we need.
Jeff Thompsone2276892013-07-08 02:44:18 -0700157 unsigned int nEncodingBytes = getNHeaderEncodingBytes(value);
Jeff Thompson8b666002013-07-08 01:16:26 -0700158 ndn_Error error;
Jeff Thompson433e6da2013-07-01 15:09:00 -0700159 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + nEncodingBytes))
160 return error;
161
162 // Bottom 4 bits of value go in last byte with tag.
163 self->output.array[self->offset + nEncodingBytes - 1] =
164 (ndn_BinaryXML_TT_MASK & type |
165 ((ndn_BinaryXML_TT_VALUE_MASK & value) << ndn_BinaryXML_TT_BITS)) |
166 ndn_BinaryXML_TT_FINAL; // set top bit for last byte
167 value >>= ndn_BinaryXML_TT_VALUE_BITS;
168
169 // Rest of value goes into preceding bytes, 7 bits per byte. (Zero top bit is "more" flag.)
170 unsigned int i = self->offset + nEncodingBytes - 2;
171 while (value != 0 && i >= self->offset) {
172 self->output.array[i] = (value & ndn_BinaryXML_REGULAR_VALUE_MASK);
173 value >>= ndn_BinaryXML_REGULAR_VALUE_BITS;
174 --i;
175 }
176 if (value != 0)
Jeff Thompsone2276892013-07-08 02:44:18 -0700177 // This should not happen if getNHeaderEncodingBytes is correct.
Jeff Thompson8b666002013-07-08 01:16:26 -0700178 return NDN_ERROR_encodeTypeAndValue_miscalculated_N_encoding_bytes;
Jeff Thompson433e6da2013-07-01 15:09:00 -0700179
180 self->offset+= nEncodingBytes;
181
182 return 0;
183}
Jeff Thompson5a984832013-07-01 19:28:27 -0700184
Jeff Thompson8b666002013-07-08 01:16:26 -0700185ndn_Error ndn_BinaryXMLEncoder_writeElementClose(struct ndn_BinaryXMLEncoder *self)
Jeff Thompson5a984832013-07-01 19:28:27 -0700186{
Jeff Thompson8b666002013-07-08 01:16:26 -0700187 ndn_Error error;
Jeff Thompson5a984832013-07-01 19:28:27 -0700188 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + 1))
189 return error;
190
191 self->output.array[self->offset] = ndn_BinaryXML_CLOSE;
192 self->offset += 1;
193
194 return 0;
195}
196
Jeff Thompson8b666002013-07-08 01:16:26 -0700197ndn_Error ndn_BinaryXMLEncoder_writeBlob(struct ndn_BinaryXMLEncoder *self, unsigned char *value, unsigned int valueLength)
Jeff Thompson5a984832013-07-01 19:28:27 -0700198{
Jeff Thompson8b666002013-07-08 01:16:26 -0700199 ndn_Error error;
Jeff Thompson5a984832013-07-01 19:28:27 -0700200 if (error = ndn_BinaryXMLEncoder_encodeTypeAndValue(self, ndn_BinaryXML_BLOB, valueLength))
201 return error;
202
203 if (error = writeArray(self, value, valueLength))
204 return error;
205
206 return 0;
207}
208
Jeff Thompson8b666002013-07-08 01:16:26 -0700209ndn_Error ndn_BinaryXMLEncoder_writeBlobDTagElement(struct ndn_BinaryXMLEncoder *self, unsigned int tag, unsigned char *value, unsigned int valueLength)
Jeff Thompson5a984832013-07-01 19:28:27 -0700210{
Jeff Thompson8b666002013-07-08 01:16:26 -0700211 ndn_Error error;
Jeff Thompson5a984832013-07-01 19:28:27 -0700212 if (error = ndn_BinaryXMLEncoder_writeElementStartDTag(self, tag))
213 return error;
214
215 if (error = ndn_BinaryXMLEncoder_writeBlob(self, value, valueLength))
216 return error;
217
218 if (error = ndn_BinaryXMLEncoder_writeElementClose(self))
219 return error;
220
221 return 0;
222}
Jeff Thompsone2276892013-07-08 02:44:18 -0700223
224ndn_Error ndn_BinaryXMLEncoder_writeUnsignedDecimalInt(struct ndn_BinaryXMLEncoder *self, unsigned int value)
225{
226 // First write the decimal int (to find out how many bytes it is), then shift it forward to make room for the header.
227 unsigned int startOffset = self->offset;
228
229 ndn_Error error;
Jeff Thompson1cb79eb2013-07-08 17:59:01 -0700230 if (error = encodeReversedUnsignedDecimalInt(self, value))
Jeff Thompsone2276892013-07-08 02:44:18 -0700231 return error;
232
Jeff Thompson1cb79eb2013-07-08 17:59:01 -0700233 if (error = reverseBufferAndInsertHeader(self, startOffset, ndn_BinaryXML_UDATA))
Jeff Thompsone2276892013-07-08 02:44:18 -0700234 return error;
235
Jeff Thompsone2276892013-07-08 02:44:18 -0700236 return 0;
237}
Jeff Thompson5b696e02013-07-08 15:04:22 -0700238
239ndn_Error ndn_BinaryXMLEncoder_writeUnsignedDecimalIntDTagElement(struct ndn_BinaryXMLEncoder *self, unsigned int tag, unsigned int value)
240{
241 ndn_Error error;
242 if (error = ndn_BinaryXMLEncoder_writeElementStartDTag(self, tag))
243 return error;
244
245 if (error = ndn_BinaryXMLEncoder_writeUnsignedDecimalInt(self, value))
246 return error;
247
248 if (error = ndn_BinaryXMLEncoder_writeElementClose(self))
249 return error;
250
251 return 0;
252}
Jeff Thompsona259cc42013-07-08 17:14:09 -0700253
254ndn_Error ndn_BinaryXMLEncoder_writeUnsignedIntBigEndianBlob(struct ndn_BinaryXMLEncoder *self, unsigned int value)
255{
256 // First encode the big endian backwards, then reverse it.
257 unsigned int startOffset = self->offset;
258 ndn_Error error;
259 while (value != 0) {
260 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + 1))
261 return error;
262
263 self->output.array[self->offset++] = (unsigned char)(value & 0xff);
264 value >>= 8;
265 }
266
Jeff Thompson1cb79eb2013-07-08 17:59:01 -0700267 if (error = reverseBufferAndInsertHeader(self, startOffset, ndn_BinaryXML_BLOB))
Jeff Thompsona259cc42013-07-08 17:14:09 -0700268 return error;
269
270 return 0;
271}