blob: f70cca472ac9ad332a69bfe440a5e64d8a744814 [file] [log] [blame]
Meki Cherkaouif441d3a2012-04-22 15:17:52 -07001/*
2 * This class is used to encode and decode binary elements ( blog, type/value pairs)
3 *
4 * @author: ucla-cs
5 */
6
7var XML_EXT = 0x00;
8
9var XML_TAG = 0x01;
10
11var XML_DTAG = 0x02;
12
13var XML_ATTR = 0x03;
14
15var XML_DATTR = 0x04;
16
17var XML_BLOB = 0x05;
18
19var XML_UDATA = 0x06;
20
21var XML_CLOSE = 0x0;
22
23var XML_SUBTYPE_PROCESSING_INSTRUCTIONS = 16;
24
25
26var XML_TT_BITS = 3;
27var XML_TT_MASK = ((1 << XML_TT_BITS) - 1);
28var XML_TT_VAL_BITS = XML_TT_BITS + 1;
29var XML_TT_VAL_MASK = ((1 << (XML_TT_VAL_BITS)) - 1);
30var XML_REG_VAL_BITS = 7;
31var XML_REG_VAL_MASK = ((1 << XML_REG_VAL_BITS) - 1);
32var XML_TT_NO_MORE = (1 << XML_REG_VAL_BITS); // 0x80
33var BYTE_MASK = 0xFF;
34var LONG_BYTES = 8;
35var LONG_BITS = 64;
36
37var bits_11 = 0x0000007FF;
38var bits_18 = 0x00003FFFF;
39var bits_32 = 0x0FFFFFFFF;
40
41
42
43//returns a string
44tagToString = function(/*long*/ tagVal) {
45 if ((tagVal >= 0) && (tagVal < CCNProtocolDTagsStrings.length)) {
46 return CCNProtocolDTagsStrings[tagVal];
47 } else if (tagVal == CCNProtocolDTags.CCNProtocolDataUnit) {
48 return CCNProtocolDTags.CCNPROTOCOL_DATA_UNIT;
49 }
50 return null;
51};
52
53//returns a Long
54stringToTag = function(/*String*/ tagName) {
55 // the slow way, but right now we don't care.... want a static lookup for the forward direction
56 for (var i=0; i < CCNProtocolDTagsStrings.length; ++i) {
57 if ((null != CCNProtocolDTagsStrings[i]) && (CCNProtocolDTagsStrings[i] == tagName)) {
58 return i;
59 }
60 }
61 if (CCNProtocolDTags.CCNPROTOCOL_DATA_UNIT == tagName) {
62 return CCNProtocolDTags.CCNProtocolDataUnit;
63 }
64 return null;
65};
66
67//console.log(stringToTag(64));
68var BinaryXMLDecoder = function BinaryXMLDecoder(istream){
69 var MARK_LEN=512;
70 var DEBUG_MAX_LEN = 32768;
71
72 this.istream = istream;
73 //console.log('istream is '+ this.istream);
74 this.offset = 0;
75};
76
77
78BinaryXMLDecoder.prototype.readStartElement =function(
79 //String
80 startTag,
81 //TreeMap<String, String>
82 attributes) {
83
84 try {
85 //this.TypeAndVal
86 tv = this.decodeTypeAndVal(this.istream);
87
88 if (null == tv) {
89 throw new Exception("Expected start element: " + startTag + " got something not a tag.");
90 }
91
92 //String
93 decodedTag = null;
94
95 if (tv.type() == XML_TAG) {
96
97 decodedTag = this.decodeUString(this.Istream, tv.val()+1);
98
99 } else if (tv.type() == XML_DTAG) {
100 decodedTag = tagToString(tv.val());
101 }
102
103 if ((null == decodedTag) || decodedTag != startTag) {
104 throw new Exception("Expected start element: " + startTag + " got: " + decodedTag + "(" + tv.val() + ")");
105 }
106
107 if (null != attributes) {
108 readAttributes(attributes);
109 }
110
111 } catch (e) {
112 throw new Exception("readStartElement", e);
113 }
114};
115
116BinaryXMLDecoder.prototype.readAttributes = function(
117 //TreeMap<String,String>
118 attributes){
119
120 if (null == attributes) {
121 return;
122 }
123
124 try {
125
126 //this.TypeAndVal
127 nextTV = this.peekTypeAndVal(this.istream);
128
129 while ((null != nextTV) && ((XML_ATTR == nextTV.type()) ||
130 (XML_DATTR == nextTV.type()))) {
131
132 //this.TypeAndVal
133 thisTV = this.decodeTypeAndVal(this.Istream);
134
135 var attributeName = null;
136 if (XML_ATTR == thisTV.type()) {
137
138 attributeName = this.decodeUString(this.istream, thisTV.val()+1);
139
140 } else if (XML_DATTR == thisTV.type()) {
141 // DKS TODO are attributes same or different dictionary?
142 attributeName = tagToString(thisTV.val());
143 if (null == attributeName) {
144 throw new ContentDecodingException("Unknown DATTR value" + thisTV.val());
145 }
146 }
147
148 var attributeValue = this.decodeUString(this.istream);
149
150 attributes.put(attributeName, attributeValue);
151
152 nextTV = this.peekTypeAndVal(this.istream);
153 }
154
155 } catch ( e) {
156
157 throw new ContentDecodingException("readStartElement", e);
158 }
159};
160
161
162BinaryXMLDecoder.prototype.initializeDecoding = function() {
163 //if (!this.istream.markSupported()) {
164 //throw new IllegalArgumentException(this.getClass().getName() + ": input stream must support marking!");
165 //}
166}
167
168BinaryXMLDecoder.prototype.readStartDocument = function(){
169 // Currently no start document in binary encoding.
170 }
171
172BinaryXMLDecoder.prototype.readEndDocument = function() {
173 // Currently no end document in binary encoding.
174 };
175
176BinaryXMLDecoder.prototype.readStartElement = function(
177 //String
178 startTag,
179 //TreeMap<String, String>
180 attributes) {
181
182
183 //NOT SURE
184 //if(typeof startTag == 'number')
185 //startTag = tagToString(startTag);
186
187 //try {
188 //TypeAndVal
189 tv = this.decodeTypeAndVal(this.istream);
190
191 if (null == tv) {
192 throw new Exception("Expected start element: " + startTag + " got something not a tag.");
193 }
194
195 //String
196 decodedTag = null;
197 //console.log(tv);
198 //console.log(typeof tv);
199
200 //console.log(XML_TAG);
201 if (tv.type() == XML_TAG) {
202 //console.log('got here');
203 //Log.info(Log.FAC_ENCODING, "Unexpected: got tag in readStartElement; looking for tag " + startTag + " got length: " + (int)tv.val()+1);
204 // Tag value represents length-1 as tags can never be empty.
205 var valval ;
206 if(typeof tv.val() == 'string'){
207 valval = (parseInt(tv.val())) + 1;
208 }
209 else
210 valval = (tv.val())+ 1;
211
212 //console.log('valval is ' +valval);
213
214 decodedTag = this.decodeUString(this.istream, valval);
215
216 } else if (tv.type() == XML_DTAG) {
217 //console.log('gothere');
218 //console.log(tv.val());
219 //decodedTag = tagToString(tv.val());
220 //console.log()
221 decodedTag = tv.val();
222 }
223
224 //console.log(decodedTag);
225 //console.log('startTag is '+startTag);
226
227
228 if ((null == decodedTag) || decodedTag != startTag ) {
229 console.log('expecting '+ startag + ' but got '+ decodedTag);
230 throw new Exception("Expected start element: " + startTag + " got: " + decodedTag + "(" + tv.val() + ")");
231 }
232
233 // DKS: does not read attributes out of stream if caller doesn't
234 // ask for them. Should possibly peek and skip over them regardless.
235 // TODO: fix this
236 if (null != attributes) {
237 readAttributes(attributes);
238 }
239
240 //} catch ( e) {
241 //console.log(e);
242 //throw new Exception("readStartElement", e);
243 //}
244 }
245
246
247BinaryXMLDecoder.prototype.readAttributes = function(
248 //TreeMap<String,String>
249 attributes) {
250
251 if (null == attributes) {
252 return;
253 }
254
255 try {
256 // Now need to get attributes.
257 //TypeAndVal
258 nextTV = this.peekTypeAndVal(this.istream);
259
260 while ((null != nextTV) && ((XML_ATTR == nextTV.type()) ||
261 (XML_DATTR == nextTV.type()))) {
262
263 // Decode this attribute. First, really read the type and value.
264 //this.TypeAndVal
265 thisTV = this.decodeTypeAndVal(this.istream);
266
267 //String
268 attributeName = null;
269 if (XML_ATTR == thisTV.type()) {
270 // Tag value represents length-1 as attribute names cannot be empty.
271 var valval ;
272 if(typeof tv.val() == 'string'){
273 valval = (parseInt(tv.val())) + 1;
274 }
275 else
276 valval = (tv.val())+ 1;
277
278 attributeName = this.decodeUString(this.istream,valval);
279
280 } else if (XML_DATTR == thisTV.type()) {
281 // DKS TODO are attributes same or different dictionary?
282 attributeName = tagToString(thisTV.val());
283 if (null == attributeName) {
284 throw new Exception("Unknown DATTR value" + thisTV.val());
285 }
286 }
287 // Attribute values are always UDATA
288 //String
289 attributeValue = this.decodeUString(this.istream);
290
291 //
292 attributes.push([attributeName, attributeValue]);
293
294 nextTV = this.peekTypeAndVal(this.istream);
295 }
296
297 } catch ( e) {
298 Log.logStackTrace(Log.FAC_ENCODING, Level.WARNING, e);
299 throw new Exception("readStartElement", e);
300 }
301};
302
303//returns a string
304BinaryXMLDecoder.prototype.peekStartElementAsString = function() {
305 //this.istream.mark(MARK_LEN);
306
307 //String
308 decodedTag = null;
309 var previousOffset = this.offset;
310 try {
311 // Have to distinguish genuine errors from wrong tags. Could either use
312 // a special exception subtype, or redo the work here.
313 //this.TypeAndVal
314 tv = this.decodeTypeAndVal(this.istream);
315
316 if (null != tv) {
317
318 if (tv.type() == XML_TAG) {
319 /*if (tv.val()+1 > DEBUG_MAX_LEN) {
320 throw new ContentDecodingException("Decoding error: length " + tv.val()+1 + " longer than expected maximum length!");
321 }*/
322
323 // Tag value represents length-1 as tags can never be empty.
324 var valval ;
325 if(typeof tv.val() == 'string'){
326 valval = (parseInt(tv.val())) + 1;
327 }
328 else
329 valval = (tv.val())+ 1;
330
331 decodedTag = this.decodeUString(this.istream, valval);
332
333 //Log.info(Log.FAC_ENCODING, "Unexpected: got text tag in peekStartElement; length: " + valval + " decoded tag = " + decodedTag);
334
335 } else if (tv.type() == XML_DTAG) {
336 decodedTag = tagToString(tv.val());
337 }
338
339 } // else, not a type and val, probably an end element. rewind and return false.
340
341 } catch ( e) {
342
343 } finally {
344 try {
345 this.offset = previousOffset;
346 } catch ( e) {
347 Log.logStackTrace(Log.FAC_ENCODING, Level.WARNING, e);
348 throw new ContentDecodingException("Cannot reset stream! " + e.getMessage(), e);
349 }
350 }
351 return decodedTag;
352};
353
354BinaryXMLDecoder.prototype.peekStartElement = function(
355 //String
356 startTag) {
357 //String
358 if(typeof startTag == 'string'){
359 decodedTag = this.peekStartElementAsString();
360
361 if ((null != decodedTag) && decodedTag == startTag) {
362 return true;
363 }
364 return false;
365 }
366 else if(typeof startTag == 'number'){
367 decodedTag = this.peekStartElementAsLong();
368 if ((null != decodedTag) && decodedTag == startTag) {
369 return true;
370 }
371 return false;
372 }
373 else{
374 throw new Exception("SHOULD BE STRING OR NUMBER");
375 }
376}
377//returns Long
378BinaryXMLDecoder.prototype.peekStartElementAsLong = function() {
379 //this.istream.mark(MARK_LEN);
380
381 //Long
382 decodedTag = null;
383
384 var previousOffset = this.offset;
385
386 try {
387 // Have to distinguish genuine errors from wrong tags. Could either use
388 // a special exception subtype, or redo the work here.
389 //this.TypeAndVal
390 tv = this.decodeTypeAndVal(this.istream);
391
392 if (null != tv) {
393
394 if (tv.type() == XML_TAG) {
395 if (tv.val()+1 > DEBUG_MAX_LEN) {
396 throw new ContentDecodingException("Decoding error: length " + tv.val()+1 + " longer than expected maximum length!");
397 }
398
399 var valval ;
400 if(typeof tv.val() == 'string'){
401 valval = (parseInt(tv.val())) + 1;
402 }
403 else
404 valval = (tv.val())+ 1;
405
406 // Tag value represents length-1 as tags can never be empty.
407 //String
408 strTag = this.decodeUString(this.istream, valval);
409
410 decodedTag = stringToTag(strTag);
411
412 //Log.info(Log.FAC_ENCODING, "Unexpected: got text tag in peekStartElement; length: " + valval + " decoded tag = " + decodedTag);
413
414 } else if (tv.type() == XML_DTAG) {
415 decodedTag = tv.val();
416 }
417
418 } // else, not a type and val, probably an end element. rewind and return false.
419
420 } catch ( e) {
421
422 } finally {
423 try {
424 //this.istream.reset();
425 this.offset = previousOffset;
426 } catch ( e) {
427 Log.logStackTrace(Log.FAC_ENCODING, Level.WARNING, e);
428 throw new Exception("Cannot reset stream! " + e.getMessage(), e);
429 }
430 }
431 return decodedTag;
432 };
433
434
435// returns a byte[]
436BinaryXMLDecoder.prototype.readBinaryElement = function(
437 //long
438 startTag,
439 //TreeMap<String, String>
440 attributes){
441 //byte []
442 blob = null;
443
444 this.readStartElement(startTag, attributes);
445 blob = this.readBlob();
446
447
448 return blob;
449
450};
451
452
453BinaryXMLDecoder.prototype.readEndElement = function(){
454 try {
455 var next = this.istream[this.offset];
456 this.offset++;
457 //read();
458 if (next != XML_CLOSE) {
459 throw new ContentDecodingException("Expected end element, got: " + next);
460 }
461 } catch ( e) {
462 throw new ContentDecodingException(e);
463 }
464 };
465
466
467//String
468BinaryXMLDecoder.prototype.readUString = function(){
469 //String
470 ustring = this.decodeUString(this.istream);
471 this.readEndElement();
472 return ustring;
473
474 };
475
476
477//returns a byte[]
478BinaryXMLDecoder.prototype.readBlob = function() {
479 //byte []
480
481 blob = this.decodeBlob(this.istream);
482 this.readEndElement();
483 return blob;
484
485 };
486
487
488//CCNTime
489BinaryXMLDecoder.prototype.readDateTime = function(
490 //long
491 startTag) {
492 //byte []
493
494 byteTimestamp = this.readBinaryElement(startTag);
495 //CCNTime
496 timestamp = new CCNTime();
497 timestamp.setDateBinary(byteTimestamp);
498
499 if (null == timestamp) {
500 throw new ContentDecodingException("Cannot parse timestamp: " + DataUtils.printHexBytes(byteTimestamp));
501 }
502 return timestamp;
503};
504
505BinaryXMLDecoder.prototype.decodeTypeAndVal = function(
506 /*InputStream*/
507 istream) {
508
509 /*int*/next;
510 /*int*/type = -1;
511 /*long*/val = 0;
512 /*boolean*/more = true;
513
514
515 //var savedOffset = this.offset;
516 var count = 0;
517
518 do {
519
520 var next = this.istream[this.offset ];
521
522
523 if (next < 0) {
524 return null;
525 }
526
527 if ((0 == next) && (0 == val)) {
528 return null;
529 }
530
531 more = (0 == (next & XML_TT_NO_MORE));
532
533 if (more) {
534 val = val << XML_REG_VAL_BITS;
535 val |= (next & XML_REG_VAL_MASK);
536 } else {
537
538 type = next & XML_TT_MASK;
539 val = val << XML_TT_VAL_BITS;
540 val |= ((next >>> XML_TT_BITS) & XML_TT_VAL_MASK);
541 }
542
543 this.offset++;
544
545 } while (more);
546
547 return new TypeAndVal(type, val);
548};
549
550
551
552//TypeAndVal
553BinaryXMLDecoder.peekTypeAndVal = function(
554 //InputStream
555 istream) {
556 //TypeAndVal
557 tv = null;
558
559 //istream.mark(LONG_BYTES*2);
560
561 var previousOffset = this.offset;
562
563 try {
564 tv = this.decodeTypeAndVal(this.istream);
565 } finally {
566 //istream.reset();
567 this.offset = previousOffset;
568 }
569
570 return tv;
571};
572
573
574//byte[]
575BinaryXMLDecoder.prototype.decodeBlob = function(
576 //InputStream
577 istream,
578 //int
579 blobLength) {
580
581
582 if(null == blobLength){
583 //TypeAndVal
584 tv = this.decodeTypeAndVal(this.istream);
585
586 var valval ;
587 if(typeof tv.val() == 'string'){
588 valval = (parseInt(tv.val()));
589 }
590 else
591 valval = (tv.val());
592
593 //console.log('valval here is ' + valval);
594 return this.decodeBlob(this.istream, valval);
595 }
596
597 //
598 //byte []
599
600 bytes = this.istream.slice(this.offset, this.offset+ blobLength);
601 this.offset += blobLength;
602
603 //int
604 return bytes;
605
606 count = 0;
607
608};
609
610
611
612//String
613BinaryXMLDecoder.prototype.decodeUString = function(
614 //InputStream
615 istream,
616 //int
617 byteLength) {
618
619 if(null == byteLength){
620 tv = this.decodeTypeAndVal(this.istream);
621 var valval ;
622 if(typeof tv.val() == 'string'){
623 valval = (parseInt(tv.val()));
624 }
625 else
626 valval = (tv.val());
627
628 byteLength= this.decodeUString(this.istream, valval);
629 }
630
631 stringBytes = this.decodeBlob(this.istream, byteLength);
632
633 tempBuffer = this.istream.slice(this.offset, this.offset+byteLength);
634 this.offset+= byteLength;
635 console.log('read the String' + tempBuffer.toString('ascii'));
636 return tempBuffer.toString('ascii');//DataUtils.getUTF8StringFromBytes(stringBytes);
637};
638
639
640//OBject containg a pair of type and value
641var TypeAndVal = function TypeAndVal(_type,_val) {
642 this.t = _type;
643 this.v = _val;
644
645};
646
647TypeAndVal.prototype.type = function(){
648 return this.t;
649};
650
651TypeAndVal.prototype.val = function(){
652 return this.v;
653};
654//TODO