blob: a618abf0567ae3bab23f55224dd731ad263b4c52 [file] [log] [blame]
Jeff Thompson47eecfc2013-07-07 22:56:46 -07001/**
2 * @author: Jeff Thompson
3 * See COPYING for copyright and distribution information.
Jeff Thompsonc8963652013-06-28 20:17:43 -07004 */
5
Jeff Thompson5a984832013-07-01 19:28:27 -07006#include "../util/ndn_memory.h"
Jeff Thompson590b8692013-06-28 22:07:41 -07007#include "BinaryXML.h"
Jeff Thompsonc8963652013-06-28 20:17:43 -07008#include "BinaryXMLEncoder.h"
Jeff Thompson590b8692013-06-28 22:07:41 -07009
10enum {
11 ENCODING_LIMIT_1_BYTE = ((1 << ndn_BinaryXML_TT_VALUE_BITS) - 1),
12 ENCODING_LIMIT_2_BYTES = ((1 << (ndn_BinaryXML_TT_VALUE_BITS + ndn_BinaryXML_REGULAR_VALUE_BITS)) - 1),
13 ENCODING_LIMIT_3_BYTES = ((1 << (ndn_BinaryXML_TT_VALUE_BITS + 2 * ndn_BinaryXML_REGULAR_VALUE_BITS)) - 1)
14};
15
16/**
Jeff Thompson5a984832013-07-01 19:28:27 -070017 * Call ndn_DynamicUCharArray_ensureLength to ensure that there is enough room in the output, and copy
18 * array to the output. This does not write a header.
19 * @param self pointer to the ndn_BinaryXMLEncoder struct
20 * @param array the array to copy
21 * @param arrayLength the length of the array
Jeff Thompson8b666002013-07-08 01:16:26 -070022 * @return 0 for success, else an error code
Jeff Thompson5a984832013-07-01 19:28:27 -070023 */
Jeff Thompson8b666002013-07-08 01:16:26 -070024static ndn_Error writeArray(struct ndn_BinaryXMLEncoder *self, unsigned char *array, unsigned int arrayLength)
Jeff Thompson5a984832013-07-01 19:28:27 -070025{
Jeff Thompson8b666002013-07-08 01:16:26 -070026 ndn_Error error;
Jeff Thompson5a984832013-07-01 19:28:27 -070027 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + arrayLength))
28 return error;
29
30 ndn_memcpy(self->output.array + self->offset, array, arrayLength);
31 self->offset += arrayLength;
32
33 return 0;
34}
35
36/**
Jeff Thompson590b8692013-06-28 22:07:41 -070037 * Return the number of bytes to encode a header of value x.
38 */
Jeff Thompsone2276892013-07-08 02:44:18 -070039static unsigned int getNHeaderEncodingBytes(unsigned int x)
Jeff Thompson590b8692013-06-28 22:07:41 -070040{
41 // Do a quick check for pre-compiled results.
42 if (x <= ENCODING_LIMIT_1_BYTE)
43 return 1;
44 if (x <= ENCODING_LIMIT_2_BYTES)
45 return 2;
46 if (x <= ENCODING_LIMIT_3_BYTES)
47 return 3;
48
49 unsigned int nBytes = 1;
50
51 // Last byte gives you TT_VALUE_BITS.
52 // Remainder each gives you REGULAR_VALUE_BITS.
53 x >>= ndn_BinaryXML_TT_VALUE_BITS;
54 while (x != 0) {
55 ++nBytes;
56 x >>= ndn_BinaryXML_REGULAR_VALUE_BITS;
57 }
58
59 return nBytes;
60}
Jeff Thompson433e6da2013-07-01 15:09:00 -070061
Jeff Thompson2c1d9212013-07-08 02:10:03 -070062/**
63 * Reverse the length bytes in array starting at offset.
64 * @param array
65 * @param offset
66 * @param length
67 */
68static void reverse(unsigned char *array, unsigned int offset, unsigned int length)
69{
70 if (length == 0)
71 return;
72
73 unsigned char *left = array + offset;
74 unsigned char *right = array + offset + length - 1;
75 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.
111 reverse(self->output.array, startOffset, self->offset - startOffset);
112 return 0;
113}
114
Jeff Thompson8b666002013-07-08 01:16:26 -0700115ndn_Error ndn_BinaryXMLEncoder_encodeTypeAndValue(struct ndn_BinaryXMLEncoder *self, unsigned int type, unsigned int value)
Jeff Thompson433e6da2013-07-01 15:09:00 -0700116{
117 if (type > ndn_BinaryXML_UDATA)
Jeff Thompson8b666002013-07-08 01:16:26 -0700118 return NDN_ERROR_header_type_is_out_of_range;
Jeff Thompson433e6da2013-07-01 15:09:00 -0700119
120 // Encode backwards. Calculate how many bytes we need.
Jeff Thompsone2276892013-07-08 02:44:18 -0700121 unsigned int nEncodingBytes = getNHeaderEncodingBytes(value);
Jeff Thompson8b666002013-07-08 01:16:26 -0700122 ndn_Error error;
Jeff Thompson433e6da2013-07-01 15:09:00 -0700123 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + nEncodingBytes))
124 return error;
125
126 // Bottom 4 bits of value go in last byte with tag.
127 self->output.array[self->offset + nEncodingBytes - 1] =
128 (ndn_BinaryXML_TT_MASK & type |
129 ((ndn_BinaryXML_TT_VALUE_MASK & value) << ndn_BinaryXML_TT_BITS)) |
130 ndn_BinaryXML_TT_FINAL; // set top bit for last byte
131 value >>= ndn_BinaryXML_TT_VALUE_BITS;
132
133 // Rest of value goes into preceding bytes, 7 bits per byte. (Zero top bit is "more" flag.)
134 unsigned int i = self->offset + nEncodingBytes - 2;
135 while (value != 0 && i >= self->offset) {
136 self->output.array[i] = (value & ndn_BinaryXML_REGULAR_VALUE_MASK);
137 value >>= ndn_BinaryXML_REGULAR_VALUE_BITS;
138 --i;
139 }
140 if (value != 0)
Jeff Thompsone2276892013-07-08 02:44:18 -0700141 // This should not happen if getNHeaderEncodingBytes is correct.
Jeff Thompson8b666002013-07-08 01:16:26 -0700142 return NDN_ERROR_encodeTypeAndValue_miscalculated_N_encoding_bytes;
Jeff Thompson433e6da2013-07-01 15:09:00 -0700143
144 self->offset+= nEncodingBytes;
145
146 return 0;
147}
Jeff Thompson5a984832013-07-01 19:28:27 -0700148
Jeff Thompson8b666002013-07-08 01:16:26 -0700149ndn_Error ndn_BinaryXMLEncoder_writeElementClose(struct ndn_BinaryXMLEncoder *self)
Jeff Thompson5a984832013-07-01 19:28:27 -0700150{
Jeff Thompson8b666002013-07-08 01:16:26 -0700151 ndn_Error error;
Jeff Thompson5a984832013-07-01 19:28:27 -0700152 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + 1))
153 return error;
154
155 self->output.array[self->offset] = ndn_BinaryXML_CLOSE;
156 self->offset += 1;
157
158 return 0;
159}
160
Jeff Thompson8b666002013-07-08 01:16:26 -0700161ndn_Error ndn_BinaryXMLEncoder_writeBlob(struct ndn_BinaryXMLEncoder *self, unsigned char *value, unsigned int valueLength)
Jeff Thompson5a984832013-07-01 19:28:27 -0700162{
Jeff Thompson8b666002013-07-08 01:16:26 -0700163 ndn_Error error;
Jeff Thompson5a984832013-07-01 19:28:27 -0700164 if (error = ndn_BinaryXMLEncoder_encodeTypeAndValue(self, ndn_BinaryXML_BLOB, valueLength))
165 return error;
166
167 if (error = writeArray(self, value, valueLength))
168 return error;
169
170 return 0;
171}
172
Jeff Thompson8b666002013-07-08 01:16:26 -0700173ndn_Error ndn_BinaryXMLEncoder_writeBlobDTagElement(struct ndn_BinaryXMLEncoder *self, unsigned int tag, unsigned char *value, unsigned int valueLength)
Jeff Thompson5a984832013-07-01 19:28:27 -0700174{
Jeff Thompson8b666002013-07-08 01:16:26 -0700175 ndn_Error error;
Jeff Thompson5a984832013-07-01 19:28:27 -0700176 if (error = ndn_BinaryXMLEncoder_writeElementStartDTag(self, tag))
177 return error;
178
179 if (error = ndn_BinaryXMLEncoder_writeBlob(self, value, valueLength))
180 return error;
181
182 if (error = ndn_BinaryXMLEncoder_writeElementClose(self))
183 return error;
184
185 return 0;
186}
Jeff Thompsone2276892013-07-08 02:44:18 -0700187
188ndn_Error ndn_BinaryXMLEncoder_writeUnsignedDecimalInt(struct ndn_BinaryXMLEncoder *self, unsigned int value)
189{
190 // First write the decimal int (to find out how many bytes it is), then shift it forward to make room for the header.
191 unsigned int startOffset = self->offset;
192
193 ndn_Error error;
194 if (error = encodeUnsignedDecimalInt(self, value))
195 return error;
196
197 unsigned int nIntegerBytes = self->offset - startOffset;
198 unsigned int nHeaderBytes = getNHeaderEncodingBytes(nIntegerBytes);
199 if (error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + nHeaderBytes))
200 return error;
201
202 // Don't use memcpy to shift because its behavior is not guaranteed when the buffers overlap.
Jeff Thompson5b696e02013-07-08 15:04:22 -0700203 // We are shifting forward, so start from the end of the buffer.
Jeff Thompsone2276892013-07-08 02:44:18 -0700204 unsigned char *source = self->output.array + startOffset + nIntegerBytes - 1;
205 unsigned char *dest = source + nHeaderBytes;
206 unsigned char *sourceFinal = self->output.array + startOffset;
207 while (source >= sourceFinal)
208 *(dest--) = *(source--);
209
210 // Override the offset to force encodeTypeAndValue to encode at startOffset, then fix the offset.
211 self->offset = startOffset;
212 if (error = ndn_BinaryXMLEncoder_encodeTypeAndValue(self, ndn_BinaryXML_UDATA, nIntegerBytes))
213 // We don't really expect to get an error, since we have already ensured the length.
214 return error;
215 self->offset = startOffset + nHeaderBytes + nIntegerBytes;
216
217 return 0;
218}
Jeff Thompson5b696e02013-07-08 15:04:22 -0700219
220ndn_Error ndn_BinaryXMLEncoder_writeUnsignedDecimalIntDTagElement(struct ndn_BinaryXMLEncoder *self, unsigned int tag, unsigned int value)
221{
222 ndn_Error error;
223 if (error = ndn_BinaryXMLEncoder_writeElementStartDTag(self, tag))
224 return error;
225
226 if (error = ndn_BinaryXMLEncoder_writeUnsignedDecimalInt(self, value))
227 return error;
228
229 if (error = ndn_BinaryXMLEncoder_writeElementClose(self))
230 return error;
231
232 return 0;
233}