blob: 6754e20da120f4a4924dd0e3b07c0035008eac8d [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 Thompson53412192013-08-06 13:35:50 -07009#include "binary-xml.h"
10#include "binary-xml-encoder.h"
Jeff Thompson590b8692013-06-28 22:07:41 -070011
12enum {
Jeff Thompsonf0fea002013-07-30 17:22:42 -070013 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)
Jeff Thompson590b8692013-06-28 22:07:41 -070016};
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.
Jeff Thompsonf0fea002013-07-30 17:22:42 -070021 * @param self pointer to the ndn_BinaryXmlEncoder struct
Jeff Thompson5a984832013-07-01 19:28:27 -070022 * @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 Thompsonf0fea002013-07-30 17:22:42 -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 Thompson94ddc272013-08-08 14:17:38 -070029 if ((error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + arrayLength)))
Jeff Thompson5a984832013-07-01 19:28:27 -070030 return error;
31
32 ndn_memcpy(self->output.array + self->offset, array, arrayLength);
Jeff Thompson2d27e2f2013-08-09 12:55:00 -070033 self->offset += arrayLength;
Jeff Thompson5a984832013-07-01 19:28:27 -070034
Jeff Thompsonadaf9232013-08-08 14:30:29 -070035 return NDN_ERROR_success;
Jeff Thompson5a984832013-07-01 19:28:27 -070036}
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.
Jeff Thompson2d27e2f2013-08-09 12:55:00 -070044 if (x <= ENCODING_LIMIT_1_BYTE)
Jeff Thompson590b8692013-06-28 22:07:41 -070045 return 1;
Jeff Thompson2d27e2f2013-08-09 12:55:00 -070046 if (x <= ENCODING_LIMIT_2_BYTES)
Jeff Thompson590b8692013-06-28 22:07:41 -070047 return 2;
Jeff Thompson2d27e2f2013-08-09 12:55:00 -070048 if (x <= ENCODING_LIMIT_3_BYTES)
Jeff Thompson590b8692013-06-28 22:07:41 -070049 return 3;
Jeff Thompson590b8692013-06-28 22:07:41 -070050
Jeff Thompson2d27e2f2013-08-09 12:55:00 -070051 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;
Jeff Thompson590b8692013-06-28 22:07:41 -070062}
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 Thompsonf0fea002013-07-30 17:22:42 -070091 * @param self pointer to the ndn_BinaryXmlEncoder struct
Jeff Thompson2c1d9212013-07-08 02:10:03 -070092 * @param x the unsigned int to write
93 * @return 0 for success, else an error code
94 */
Jeff Thompsonf0fea002013-07-30 17:22:42 -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;
Jeff Thompson94ddc272013-08-08 14:17:38 -070099 if ((error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + 1)))
Jeff Thompson2c1d9212013-07-08 02:10:03 -0700100 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 Thompsonadaf9232013-08-08 14:30:29 -0700109 return NDN_ERROR_success;
Jeff Thompsona259cc42013-07-08 17:14:09 -0700110}
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 Thompsonf0fea002013-07-30 17:22:42 -0700117 * @param self pointer to the ndn_BinaryXmlEncoder struct
Jeff Thompsona259cc42013-07-08 17:14:09 -0700118 * @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 Thompsonf0fea002013-07-30 17:22:42 -0700123 (struct ndn_BinaryXmlEncoder *self, unsigned int startOffset, unsigned int type)
Jeff Thompsona259cc42013-07-08 17:14:09 -0700124{
125 unsigned int nBufferBytes = self->offset - startOffset;
126 unsigned int nHeaderBytes = getNHeaderEncodingBytes(nBufferBytes);
127 ndn_Error error;
Jeff Thompson94ddc272013-08-08 14:17:38 -0700128 if ((error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + nHeaderBytes)))
Jeff Thompsona259cc42013-07-08 17:14:09 -0700129 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;
Jeff Thompsonb123be62013-08-08 21:13:19 -0700144 if ((error = ndn_BinaryXmlEncoder_encodeTypeAndValue(self, type, nBufferBytes)))
Jeff Thompsona259cc42013-07-08 17:14:09 -0700145 // 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 Thompsonadaf9232013-08-08 14:30:29 -0700149 return NDN_ERROR_success;
Jeff Thompson2c1d9212013-07-08 02:10:03 -0700150}
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 Thompsonf0fea002013-07-30 17:22:42 -0700173ndn_Error ndn_BinaryXmlEncoder_encodeTypeAndValue(struct ndn_BinaryXmlEncoder *self, unsigned int type, unsigned int value)
Jeff Thompson433e6da2013-07-01 15:09:00 -0700174{
Jeff Thompson2d27e2f2013-08-09 12:55:00 -0700175 if (type > ndn_BinaryXml_UDATA)
176 return NDN_ERROR_header_type_is_out_of_range;
Jeff Thompson433e6da2013-07-01 15:09:00 -0700177
Jeff Thompson2d27e2f2013-08-09 12:55:00 -0700178 // Encode backwards. Calculate how many bytes we need.
179 unsigned int nEncodingBytes = getNHeaderEncodingBytes(value);
Jeff Thompson8b666002013-07-08 01:16:26 -0700180 ndn_Error error;
Jeff Thompson94ddc272013-08-08 14:17:38 -0700181 if ((error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + nEncodingBytes)))
Jeff Thompson433e6da2013-07-01 15:09:00 -0700182 return error;
183
Jeff Thompson2d27e2f2013-08-09 12:55:00 -0700184 // 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 Thompson2d27e2f2013-08-09 12:55:00 -0700200 return NDN_ERROR_encodeTypeAndValue_miscalculated_N_encoding_bytes;
201
202 self->offset+= nEncodingBytes;
Jeff Thompson433e6da2013-07-01 15:09:00 -0700203
Jeff Thompsonadaf9232013-08-08 14:30:29 -0700204 return NDN_ERROR_success;
Jeff Thompson433e6da2013-07-01 15:09:00 -0700205}
Jeff Thompson5a984832013-07-01 19:28:27 -0700206
Jeff Thompsonf0fea002013-07-30 17:22:42 -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 Thompson94ddc272013-08-08 14:17:38 -0700210 if ((error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + 1)))
Jeff Thompson5a984832013-07-01 19:28:27 -0700211 return error;
212
Jeff Thompson2d27e2f2013-08-09 12:55:00 -0700213 self->output.array[self->offset] = ndn_BinaryXml_CLOSE;
214 self->offset += 1;
Jeff Thompson5a984832013-07-01 19:28:27 -0700215
Jeff Thompsonadaf9232013-08-08 14:30:29 -0700216 return NDN_ERROR_success;
Jeff Thompson5a984832013-07-01 19:28:27 -0700217}
218
Jeff Thompsonf0fea002013-07-30 17:22:42 -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 Thompson94ddc272013-08-08 14:17:38 -0700222 if ((error = ndn_BinaryXmlEncoder_encodeTypeAndValue(self, ndn_BinaryXml_BLOB, valueLength)))
Jeff Thompson5a984832013-07-01 19:28:27 -0700223 return error;
224
Jeff Thompson94ddc272013-08-08 14:17:38 -0700225 if ((error = writeArray(self, value, valueLength)))
Jeff Thompson5a984832013-07-01 19:28:27 -0700226 return error;
227
Jeff Thompsonadaf9232013-08-08 14:30:29 -0700228 return NDN_ERROR_success;
Jeff Thompson5a984832013-07-01 19:28:27 -0700229}
230
Jeff Thompsonf0fea002013-07-30 17:22:42 -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 Thompson94ddc272013-08-08 14:17:38 -0700234 if ((error = ndn_BinaryXmlEncoder_writeElementStartDTag(self, tag)))
Jeff Thompson5a984832013-07-01 19:28:27 -0700235 return error;
236
Jeff Thompson94ddc272013-08-08 14:17:38 -0700237 if ((error = ndn_BinaryXmlEncoder_writeBlob(self, value, valueLength)))
Jeff Thompson5a984832013-07-01 19:28:27 -0700238 return error;
239
Jeff Thompson94ddc272013-08-08 14:17:38 -0700240 if ((error = ndn_BinaryXmlEncoder_writeElementClose(self)))
Jeff Thompson5a984832013-07-01 19:28:27 -0700241 return error;
242
Jeff Thompsonadaf9232013-08-08 14:30:29 -0700243 return NDN_ERROR_success;
Jeff Thompson5a984832013-07-01 19:28:27 -0700244}
Jeff Thompsone2276892013-07-08 02:44:18 -0700245
Jeff Thompsonf0fea002013-07-30 17:22:42 -0700246ndn_Error ndn_BinaryXmlEncoder_writeUnsignedDecimalInt(struct ndn_BinaryXmlEncoder *self, unsigned int value)
Jeff Thompsone2276892013-07-08 02:44:18 -0700247{
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 Thompson94ddc272013-08-08 14:17:38 -0700252 if ((error = encodeReversedUnsignedDecimalInt(self, value)))
Jeff Thompsone2276892013-07-08 02:44:18 -0700253 return error;
254
Jeff Thompson94ddc272013-08-08 14:17:38 -0700255 if ((error = reverseBufferAndInsertHeader(self, startOffset, ndn_BinaryXml_UDATA)))
Jeff Thompsone2276892013-07-08 02:44:18 -0700256 return error;
257
Jeff Thompsonadaf9232013-08-08 14:30:29 -0700258 return NDN_ERROR_success;
Jeff Thompsone2276892013-07-08 02:44:18 -0700259}
Jeff Thompson5b696e02013-07-08 15:04:22 -0700260
Jeff Thompsonf0fea002013-07-30 17:22:42 -0700261ndn_Error ndn_BinaryXmlEncoder_writeUnsignedDecimalIntDTagElement(struct ndn_BinaryXmlEncoder *self, unsigned int tag, unsigned int value)
Jeff Thompson5b696e02013-07-08 15:04:22 -0700262{
263 ndn_Error error;
Jeff Thompson94ddc272013-08-08 14:17:38 -0700264 if ((error = ndn_BinaryXmlEncoder_writeElementStartDTag(self, tag)))
Jeff Thompson5b696e02013-07-08 15:04:22 -0700265 return error;
266
Jeff Thompson94ddc272013-08-08 14:17:38 -0700267 if ((error = ndn_BinaryXmlEncoder_writeUnsignedDecimalInt(self, value)))
Jeff Thompson5b696e02013-07-08 15:04:22 -0700268 return error;
269
Jeff Thompson94ddc272013-08-08 14:17:38 -0700270 if ((error = ndn_BinaryXmlEncoder_writeElementClose(self)))
Jeff Thompson5b696e02013-07-08 15:04:22 -0700271 return error;
272
Jeff Thompsonadaf9232013-08-08 14:30:29 -0700273 return NDN_ERROR_success;
Jeff Thompson5b696e02013-07-08 15:04:22 -0700274}
Jeff Thompsona259cc42013-07-08 17:14:09 -0700275
Jeff Thompsonf0fea002013-07-30 17:22:42 -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 Thompson94ddc272013-08-08 14:17:38 -0700286 if ((error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + 1)))
Jeff Thompsona259cc42013-07-08 17:14:09 -0700287 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) {
Jeff Thompson94ddc272013-08-08 14:17:38 -0700296 if ((error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + 1)))
Jeff Thompsonedc22252013-07-11 18:05:44 -0700297 return error;
298
299 self->output.array[self->offset++] = 0;
300 }
301
302 // Encode hi32
303 while (hi32 != 0) {
Jeff Thompson94ddc272013-08-08 14:17:38 -0700304 if ((error = ndn_DynamicUCharArray_ensureLength(&self->output, self->offset + 1)))
Jeff Thompsonedc22252013-07-11 18:05:44 -0700305 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 Thompson94ddc272013-08-08 14:17:38 -0700312 if ((error = reverseBufferAndInsertHeader(self, startOffset, ndn_BinaryXml_BLOB)))
Jeff Thompsona259cc42013-07-08 17:14:09 -0700313 return error;
314
Jeff Thompsonadaf9232013-08-08 14:30:29 -0700315 return NDN_ERROR_success;
Jeff Thompsona259cc42013-07-08 17:14:09 -0700316}
Jeff Thompsonedc22252013-07-11 18:05:44 -0700317
Jeff Thompsonf0fea002013-07-30 17:22:42 -0700318ndn_Error ndn_BinaryXmlEncoder_writeTimeMillisecondsDTagElement(struct ndn_BinaryXmlEncoder *self, unsigned int tag, double milliseconds)
Jeff Thompsonedc22252013-07-11 18:05:44 -0700319{
320 ndn_Error error;
Jeff Thompson94ddc272013-08-08 14:17:38 -0700321 if ((error = ndn_BinaryXmlEncoder_writeElementStartDTag(self, tag)))
Jeff Thompsonedc22252013-07-11 18:05:44 -0700322 return error;
323
Jeff Thompson94ddc272013-08-08 14:17:38 -0700324 if ((error = ndn_BinaryXmlEncoder_writeAbsDoubleBigEndianBlob(self, (milliseconds / 1000.0) * 4096.0)))
Jeff Thompsonedc22252013-07-11 18:05:44 -0700325 return error;
326
Jeff Thompson94ddc272013-08-08 14:17:38 -0700327 if ((error = ndn_BinaryXmlEncoder_writeElementClose(self)))
Jeff Thompsonedc22252013-07-11 18:05:44 -0700328 return error;
329
Jeff Thompsonadaf9232013-08-08 14:30:29 -0700330 return NDN_ERROR_success;
Jeff Thompsonedc22252013-07-11 18:05:44 -0700331}