blob: 517abcab8f2dbfa8689e65bcc132785b3fd928c4 [file] [log] [blame]
Wentao Shangbd63e462012-12-03 16:19:33 -08001/**
Jeff Thompsone6923bf2012-10-14 09:47:42 -07002 * This class is used to encode ccnb binary elements (blob, type/value pairs).
Meki Cherkaouif441d3a2012-04-22 15:17:52 -07003 *
Jeff Thompson146d7de2012-11-17 16:15:28 -08004 * @author: Meki Cheraoui
Jeff Thompson745026e2012-10-13 12:49:20 -07005 * See COPYING for copyright and distribution information.
Meki Cherkaouif441d3a2012-04-22 15:17:52 -07006 */
7
8var XML_EXT = 0x00;
9
10var XML_TAG = 0x01;
11
12var XML_DTAG = 0x02;
13
14var XML_ATTR = 0x03;
15
16var XML_DATTR = 0x04;
17
18var XML_BLOB = 0x05;
19
20var XML_UDATA = 0x06;
21
22var XML_CLOSE = 0x0;
23
24var XML_SUBTYPE_PROCESSING_INSTRUCTIONS = 16;
Meki Cherkaouiabb973b2012-05-09 14:25:57 -070025
Meki Cherkaouif441d3a2012-04-22 15:17:52 -070026
27var XML_TT_BITS = 3;
28var XML_TT_MASK = ((1 << XML_TT_BITS) - 1);
29var XML_TT_VAL_BITS = XML_TT_BITS + 1;
30var XML_TT_VAL_MASK = ((1 << (XML_TT_VAL_BITS)) - 1);
31var XML_REG_VAL_BITS = 7;
32var XML_REG_VAL_MASK = ((1 << XML_REG_VAL_BITS) - 1);
33var XML_TT_NO_MORE = (1 << XML_REG_VAL_BITS); // 0x80
34var BYTE_MASK = 0xFF;
35var LONG_BYTES = 8;
36var LONG_BITS = 64;
37
38var bits_11 = 0x0000007FF;
39var bits_18 = 0x00003FFFF;
40var bits_32 = 0x0FFFFFFFF;
41
Jeff Thompson2b14c7e2013-07-29 15:09:56 -070042/**
43 * @constructor
44 */
Meki Cherkaouif441d3a2012-04-22 15:17:52 -070045var BinaryXMLEncoder = function BinaryXMLEncoder(){
Jeff Thompson96978b42012-12-29 21:59:54 -080046 this.ostream = new DynamicUint8Array(100);
Meki Cherkaouif441d3a2012-04-22 15:17:52 -070047 this.offset =0;
Meki Cherkaouif441d3a2012-04-22 15:17:52 -070048 this.CODEC_NAME = "Binary";
Meki Cherkaouif441d3a2012-04-22 15:17:52 -070049};
50
Jeff Thompson2b14c7e2013-07-29 15:09:56 -070051/**
Jeff Thompsond24dad32012-11-18 18:19:28 -080052 * Encode utf8Content as utf8.
53 */
Jeff Thompsonc92706b2012-11-11 19:12:58 -080054BinaryXMLEncoder.prototype.writeUString = function(/*String*/ utf8Content) {
Jeff Thompsond15aa7b2012-11-17 15:17:24 -080055 this.encodeUString(utf8Content, XML_UDATA);
Meki Cherkaouif441d3a2012-04-22 15:17:52 -070056};
57
Jeff Thompsonc92706b2012-11-11 19:12:58 -080058
59BinaryXMLEncoder.prototype.writeBlob = function(
60 /*Uint8Array*/ binaryContent
Jeff Thompsonc92706b2012-11-11 19:12:58 -080061 ) {
Meki Cherkaouiabb973b2012-05-09 14:25:57 -070062
63 if(LOG >3) console.log(binaryContent);
64
Jeff Thompsond15aa7b2012-11-17 15:17:24 -080065 this.encodeBlob(binaryContent, binaryContent.length);
Meki Cherkaouif441d3a2012-04-22 15:17:52 -070066};
67
Meki Cherkaouif441d3a2012-04-22 15:17:52 -070068
Jeff Thompsonc92706b2012-11-11 19:12:58 -080069BinaryXMLEncoder.prototype.writeStartElement = function(
70 /*String*/ tag,
71 /*TreeMap<String,String>*/ attributes
72 ) {
73
Jeff Thompsond15aa7b2012-11-17 15:17:24 -080074 /*Long*/ var dictionaryVal = tag; //stringToTag(tag);
Meki Cherkaouif441d3a2012-04-22 15:17:52 -070075
76 if (null == dictionaryVal) {
Jeff Thompsond15aa7b2012-11-17 15:17:24 -080077 this.encodeUString(tag, XML_TAG);
Meki Cherkaouif441d3a2012-04-22 15:17:52 -070078 } else {
Jeff Thompsond15aa7b2012-11-17 15:17:24 -080079 this.encodeTypeAndVal(XML_DTAG, dictionaryVal);
Meki Cherkaouif441d3a2012-04-22 15:17:52 -070080 }
81
82 if (null != attributes) {
83 this.writeAttributes(attributes);
84 }
85};
86
87
Jeff Thompsonc92706b2012-11-11 19:12:58 -080088BinaryXMLEncoder.prototype.writeEndElement = function() {
Jeff Thompson96978b42012-12-29 21:59:54 -080089 this.ostream.ensureLength(this.offset + 1);
90 this.ostream.array[this.offset] = XML_CLOSE;
Jeff Thompsonc92706b2012-11-11 19:12:58 -080091 this.offset += 1;
Meki Cherkaouif441d3a2012-04-22 15:17:52 -070092}
93
Jeff Thompsonc92706b2012-11-11 19:12:58 -080094
Meki Cherkaouif441d3a2012-04-22 15:17:52 -070095BinaryXMLEncoder.prototype.writeAttributes = function(/*TreeMap<String,String>*/ attributes) {
Meki Cherkaouif441d3a2012-04-22 15:17:52 -070096 if (null == attributes) {
97 return;
98 }
99
100 // the keySet of a TreeMap is sorted.
101
102 for(var i=0; i<attributes.length;i++){
103 var strAttr = attributes[i].k;
104 var strValue = attributes[i].v;
105
106 var dictionaryAttr = stringToTag(strAttr);
107 if (null == dictionaryAttr) {
108 // not in dictionary, encode as attr
109 // compressed format wants length of tag represented as length-1
110 // to save that extra bit, as tag cannot be 0 length.
111 // encodeUString knows to do that.
Jeff Thompsond15aa7b2012-11-17 15:17:24 -0800112 this.encodeUString(strAttr, XML_ATTR);
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700113 } else {
Jeff Thompsond15aa7b2012-11-17 15:17:24 -0800114 this.encodeTypeAndVal(XML_DATTR, dictionaryAttr);
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700115 }
116 // Write value
Jeff Thompsond15aa7b2012-11-17 15:17:24 -0800117 this.encodeUString(strValue);
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700118
119 }
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700120}
121
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800122
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700123//returns a string
124stringToTag = function(/*long*/ tagVal) {
125 if ((tagVal >= 0) && (tagVal < CCNProtocolDTagsStrings.length)) {
126 return CCNProtocolDTagsStrings[tagVal];
127 } else if (tagVal == CCNProtocolDTags.CCNProtocolDataUnit) {
128 return CCNProtocolDTags.CCNPROTOCOL_DATA_UNIT;
129 }
130 return null;
131};
132
133//returns a Long
134tagToString = function(/*String*/ tagName) {
135 // the slow way, but right now we don't care.... want a static lookup for the forward direction
136 for (var i=0; i < CCNProtocolDTagsStrings.length; ++i) {
137 if ((null != CCNProtocolDTagsStrings[i]) && (CCNProtocolDTagsStrings[i] == tagName)) {
138 return i;
139 }
140 }
141 if (CCNProtocolDTags.CCNPROTOCOL_DATA_UNIT == tagName) {
142 return CCNProtocolDTags.CCNProtocolDataUnit;
143 }
144 return null;
145};
146
Jeff Thompson2b14c7e2013-07-29 15:09:56 -0700147/**
Jeff Thompsond24dad32012-11-18 18:19:28 -0800148 * If Content is a string, then encode as utf8 and write UDATA.
149 */
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700150BinaryXMLEncoder.prototype.writeElement = function(
151 //long
152 tag,
153 //byte[]
Meki Cherkaouiabb973b2012-05-09 14:25:57 -0700154 Content,
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700155 //TreeMap<String, String>
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800156 attributes
157 ) {
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700158 this.writeStartElement(tag, attributes);
159 // Will omit if 0-length
160
Meki Cherkaouiabb973b2012-05-09 14:25:57 -0700161 if(typeof Content === 'number') {
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800162 if(LOG>4) console.log('GOING TO WRITE THE NUMBER .charCodeAt(0) ' + Content.toString().charCodeAt(0) );
163 if(LOG>4) console.log('GOING TO WRITE THE NUMBER ' + Content.toString() );
164 if(LOG>4) console.log('type of number is ' + typeof Content.toString() );
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700165
166 this.writeUString(Content.toString());
Meki Cherkaouiabb973b2012-05-09 14:25:57 -0700167 //whatever
Meki Cherkaouiabb973b2012-05-09 14:25:57 -0700168 }
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700169 else if(typeof Content === 'string'){
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800170 if(LOG>4) console.log('GOING TO WRITE THE STRING ' + Content );
171 if(LOG>4) console.log('type of STRING is ' + typeof Content );
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700172
173 this.writeUString(Content);
174 }
Meki Cherkaouiabb973b2012-05-09 14:25:57 -0700175 else{
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800176 if(LOG>4) console.log('GOING TO WRITE A BLOB ' + Content );
177
Meki Cherkaouiabb973b2012-05-09 14:25:57 -0700178 this.writeBlob(Content);
Meki Cherkaouiabb973b2012-05-09 14:25:57 -0700179 }
180
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700181 this.writeEndElement();
182}
183
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800184
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700185
186var TypeAndVal = function TypeAndVal(_type,_val) {
187 this.type = _type;
188 this.val = _val;
189
190};
191
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800192
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700193BinaryXMLEncoder.prototype.encodeTypeAndVal = function(
194 //int
195 type,
196 //long
Jeff Thompsond15aa7b2012-11-17 15:17:24 -0800197 val
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800198 ) {
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700199
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800200 if(LOG>4) console.log('Encoding type '+ type+ ' and value '+ val);
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700201
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800202 if(LOG>4) console.log('OFFSET IS ' + this.offset);
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700203
204 if ((type > XML_UDATA) || (type < 0) || (val < 0)) {
Jeff Thompson34a2ec02012-09-29 21:47:05 -0700205 throw new Error("Tag and value must be positive, and tag valid.");
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700206 }
207
208 // Encode backwards. Calculate how many bytes we need:
209 var numEncodingBytes = this.numEncodingBytes(val);
Jeff Thompson96978b42012-12-29 21:59:54 -0800210 this.ostream.ensureLength(this.offset + numEncodingBytes);
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700211
212 // Bottom 4 bits of val go in last byte with tag.
Jeff Thompson96978b42012-12-29 21:59:54 -0800213 this.ostream.array[this.offset + numEncodingBytes - 1] =
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700214 //(byte)
215 (BYTE_MASK &
216 (((XML_TT_MASK & type) |
217 ((XML_TT_VAL_MASK & val) << XML_TT_BITS))) |
218 XML_TT_NO_MORE); // set top bit for last byte
Jeff Thompson96978b42012-12-29 21:59:54 -0800219 val = val >>> XML_TT_VAL_BITS;
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700220
221 // Rest of val goes into preceding bytes, 7 bits per byte, top bit
222 // is "more" flag.
223 var i = this.offset + numEncodingBytes - 2;
224 while ((0 != val) && (i >= this.offset)) {
Jeff Thompson96978b42012-12-29 21:59:54 -0800225 this.ostream.array[i] = //(byte)
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800226 (BYTE_MASK & (val & XML_REG_VAL_MASK)); // leave top bit unset
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700227 val = val >>> XML_REG_VAL_BITS;
228 --i;
229 }
230 if (val != 0) {
Jeff Thompson34a2ec02012-09-29 21:47:05 -0700231 throw new Error( "This should not happen: miscalculated encoding");
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700232 //Log.warning(Log.FAC_ENCODING, "This should not happen: miscalculated encoding length, have " + val + " left.");
233 }
234 this.offset+= numEncodingBytes;
235
236 return numEncodingBytes;
237};
238
Jeff Thompson2b14c7e2013-07-29 15:09:56 -0700239/**
Jeff Thompsond24dad32012-11-18 18:19:28 -0800240 * Encode ustring as utf8.
241 */
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700242BinaryXMLEncoder.prototype.encodeUString = function(
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700243 //String
244 ustring,
245 //byte
246 type) {
247
Jeff Thompsond24dad32012-11-18 18:19:28 -0800248 if (null == ustring)
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700249 return;
Jeff Thompsond24dad32012-11-18 18:19:28 -0800250 if (type == XML_TAG || type == XML_ATTR && ustring.length == 0)
251 return;
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700252
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700253 if(LOG>3) console.log("The string to write is ");
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700254 if(LOG>3) console.log(ustring);
255
Jeff Thompsond24dad32012-11-18 18:19:28 -0800256 var strBytes = DataUtils.stringToUtf8Array(ustring);
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700257
258 this.encodeTypeAndVal(type,
259 (((type == XML_TAG) || (type == XML_ATTR)) ?
260 (strBytes.length-1) :
Jeff Thompsond15aa7b2012-11-17 15:17:24 -0800261 strBytes.length));
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700262
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700263 if(LOG>3) console.log("THE string to write is ");
264
265 if(LOG>3) console.log(strBytes);
266
Jeff Thompson96978b42012-12-29 21:59:54 -0800267 this.writeString(strBytes);
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700268 this.offset+= strBytes.length;
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700269};
270
271
272
273BinaryXMLEncoder.prototype.encodeBlob = function(
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800274 //Uint8Array
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700275 blob,
276 //int
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700277 length) {
278
279
Jeff Thompson84ca9502012-11-04 13:11:36 -0800280 if (null == blob)
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700281 return;
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700282
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700283 if(LOG>4) console.log('LENGTH OF XML_BLOB IS '+length);
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700284
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800285 /*blobCopy = new Array(blob.Length);
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700286
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800287 for (i = 0; i < blob.length; i++) //in InStr.ToCharArray())
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700288 {
289 blobCopy[i] = blob[i];
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800290 }*/
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700291
Jeff Thompsond15aa7b2012-11-17 15:17:24 -0800292 this.encodeTypeAndVal(XML_BLOB, length);
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700293
Jeff Thompson96978b42012-12-29 21:59:54 -0800294 this.writeBlobArray(blob);
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800295 this.offset += length;
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700296};
297
298var ENCODING_LIMIT_1_BYTE = ((1 << (XML_TT_VAL_BITS)) - 1);
299var ENCODING_LIMIT_2_BYTES = ((1 << (XML_TT_VAL_BITS + XML_REG_VAL_BITS)) - 1);
300var ENCODING_LIMIT_3_BYTES = ((1 << (XML_TT_VAL_BITS + 2 * XML_REG_VAL_BITS)) - 1);
301
302BinaryXMLEncoder.prototype.numEncodingBytes = function(
303 //long
304 x) {
305 if (x <= ENCODING_LIMIT_1_BYTE) return (1);
306 if (x <= ENCODING_LIMIT_2_BYTES) return (2);
307 if (x <= ENCODING_LIMIT_3_BYTES) return (3);
308
309 var numbytes = 1;
310
311 // Last byte gives you XML_TT_VAL_BITS
312 // Remainder each give you XML_REG_VAL_BITS
313 x = x >>> XML_TT_VAL_BITS;
314 while (x != 0) {
315 numbytes++;
316 x = x >>> XML_REG_VAL_BITS;
317 }
318 return (numbytes);
319};
320
321BinaryXMLEncoder.prototype.writeDateTime = function(
322 //String
323 tag,
324 //CCNTime
325 dateTime) {
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700326
327 if(LOG>4)console.log('ENCODING DATE with LONG VALUE');
328 if(LOG>4)console.log(dateTime.msec);
329
330 //var binarydate = DataUtils.unsignedLongToByteArray( Math.round((dateTime.msec/1000) * 4096) );
331
332
333 //parse to hex
334 var binarydate = Math.round((dateTime.msec/1000) * 4096).toString(16) ;
Jeff Thompson8023c152013-07-11 13:21:44 -0700335 if (binarydate.length % 2 == 1)
336 binarydate = '0' + binarydate;
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700337
Jeff Thompson8023c152013-07-11 13:21:44 -0700338 // Hack toNumbers by appending a 0 which is ignored.
339 var binarydate = DataUtils.toNumbers( binarydate + '0') ;
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700340
341
342 if(LOG>4)console.log('ENCODING DATE with BINARY VALUE');
343 if(LOG>4)console.log(binarydate);
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700344 if(LOG>4)console.log('ENCODING DATE with BINARY VALUE(HEX)');
345 if(LOG>4)console.log(DataUtils.toHex(binarydate));
346
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700347 this.writeElement(tag, binarydate);
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700348};
349
Jeff Thompson96978b42012-12-29 21:59:54 -0800350// This does not update this.offset.
351BinaryXMLEncoder.prototype.writeString = function(input) {
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700352
Meki Cherkaouiabb973b2012-05-09 14:25:57 -0700353 if(typeof input === 'string'){
354 //console.log('went here');
355 if(LOG>4) console.log('GOING TO WRITE A STRING');
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700356 if(LOG>4) console.log(input);
357
Jeff Thompson96978b42012-12-29 21:59:54 -0800358 this.ostream.ensureLength(this.offset + input.length);
359 for (var i = 0; i < input.length; i++) {
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700360 if(LOG>4) console.log('input.charCodeAt(i)=' + input.charCodeAt(i));
Jeff Thompson96978b42012-12-29 21:59:54 -0800361 this.ostream.array[this.offset + i] = (input.charCodeAt(i));
Meki Cherkaouiabb973b2012-05-09 14:25:57 -0700362 }
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700363 }
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700364 else{
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800365 if(LOG>4) console.log('GOING TO WRITE A STRING IN BINARY FORM');
366 if(LOG>4) console.log(input);
367
368 this.writeBlobArray(input);
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700369 }
370 /*
Meki Cherkaouiabb973b2012-05-09 14:25:57 -0700371 else if(typeof input === 'object'){
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700372
Meki Cherkaouiabb973b2012-05-09 14:25:57 -0700373 }
Meki Cherkaoui8f173612012-06-06 01:05:40 -0700374 */
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700375};
376
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800377
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700378BinaryXMLEncoder.prototype.writeBlobArray = function(
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800379 //Uint8Array
Jeff Thompson96978b42012-12-29 21:59:54 -0800380 blob) {
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700381
Meki Cherkaouiabb973b2012-05-09 14:25:57 -0700382 if(LOG>4) console.log('GOING TO WRITE A BLOB');
383
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800384 this.ostream.set(blob, this.offset);
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700385};
Meki Cherkaouiabb973b2012-05-09 14:25:57 -0700386
387
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700388BinaryXMLEncoder.prototype.getReducedOstream = function() {
Jeff Thompsonc92706b2012-11-11 19:12:58 -0800389 return this.ostream.subarray(0, this.offset);
Meki Cherkaouif441d3a2012-04-22 15:17:52 -0700390};
391