blob: 9bf0ccc9068b12b73816dcc30e6f97d0dcafb6a2 [file] [log] [blame]
Wentao Shangbd63e462012-12-03 16:19:33 -08001/**
Jeff Thompsondad617b2012-10-14 17:11:41 -07002 * This class uses BinaryXMLDecoder to follow the structure of a ccnb binary element to
3 * determine its end.
4 *
Jeff Thompson146d7de2012-11-17 16:15:28 -08005 * @author: Jeff Thompson
Jeff Thompsondad617b2012-10-14 17:11:41 -07006 * See COPYING for copyright and distribution information.
7 */
8
9var BinaryXMLStructureDecoder = function BinaryXMLDecoder() {
10 this.gotElementEnd = false;
11 this.offset = 0;
12 this.level = 0;
13 this.state = BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE;
Jeff Thompson8dfa2912012-11-28 21:21:56 -080014 this.headerLength = 0;
15 this.useHeaderBuffer = false;
Jeff Thompson942022d2012-12-29 21:57:47 -080016 this.headerBuffer = new DynamicUint8Array(5);
Jeff Thompson11dc9b62012-11-28 19:21:20 -080017 this.nBytesToRead = 0;
Jeff Thompsondad617b2012-10-14 17:11:41 -070018};
19
20BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE = 0;
21BinaryXMLStructureDecoder.READ_BYTES = 1;
22
23/*
24 * Continue scanning input starting from this.offset. If found the end of the element
25 * which started at offset 0 then return true, else false.
26 * If this returns false, you should read more into input and call again.
27 * You have to pass in input each time because the array could be reallocated.
28 * This throws an exception for badly formed ccnb.
29 */
30BinaryXMLStructureDecoder.prototype.findElementEnd = function(
Jeff Thompson8dfa2912012-11-28 21:21:56 -080031 // Uint8Array
Jeff Thompsondad617b2012-10-14 17:11:41 -070032 input)
33{
34 if (this.gotElementEnd)
35 // Someone is calling when we already got the end.
36 return true;
37
38 var decoder = new BinaryXMLDecoder(input);
39
40 while (true) {
41 if (this.offset >= input.length)
42 // All the cases assume we have some input.
43 return false;
Wentao Shang2b740e62012-12-07 00:02:53 -080044
Jeff Thompsondad617b2012-10-14 17:11:41 -070045 switch (this.state) {
46 case BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE:
47 // First check for XML_CLOSE.
Jeff Thompson8dfa2912012-11-28 21:21:56 -080048 if (this.headerLength == 0 && input[this.offset] == XML_CLOSE) {
Jeff Thompsondad617b2012-10-14 17:11:41 -070049 ++this.offset;
50 // Close the level.
51 --this.level;
52 if (this.level == 0)
53 // Finished.
54 return true;
55 if (this.level < 0)
56 throw new Error("BinaryXMLStructureDecoder: Unexepected close tag at offset " +
57 (this.offset - 1));
58
59 // Get ready for the next header.
Jeff Thompson8dfa2912012-11-28 21:21:56 -080060 this.startHeader();
Jeff Thompsondad617b2012-10-14 17:11:41 -070061 break;
62 }
Jeff Thompson8dfa2912012-11-28 21:21:56 -080063
64 var startingHeaderLength = this.headerLength;
Jeff Thompsondad617b2012-10-14 17:11:41 -070065 while (true) {
Jeff Thompson8dfa2912012-11-28 21:21:56 -080066 if (this.offset >= input.length) {
67 // We can't get all of the header bytes from this input. Save in headerBuffer.
68 this.useHeaderBuffer = true;
69 var nNewBytes = this.headerLength - startingHeaderLength;
Jeff Thompson942022d2012-12-29 21:57:47 -080070 this.headerBuffer.set
Jeff Thompson8dfa2912012-11-28 21:21:56 -080071 (input.subarray(this.offset - nNewBytes, nNewBytes), startingHeaderLength);
72
Jeff Thompsondad617b2012-10-14 17:11:41 -070073 return false;
Jeff Thompson8dfa2912012-11-28 21:21:56 -080074 }
75 var headerByte = input[this.offset++];
76 ++this.headerLength;
77 if (headerByte & XML_TT_NO_MORE)
Jeff Thompsondad617b2012-10-14 17:11:41 -070078 // Break and read the header.
79 break;
80 }
Jeff Thompson8dfa2912012-11-28 21:21:56 -080081
82 var typeAndVal;
83 if (this.useHeaderBuffer) {
84 // Copy the remaining bytes into headerBuffer.
85 nNewBytes = this.headerLength - startingHeaderLength;
Jeff Thompson942022d2012-12-29 21:57:47 -080086 this.headerBuffer.set
Jeff Thompson8dfa2912012-11-28 21:21:56 -080087 (input.subarray(this.offset - nNewBytes, nNewBytes), startingHeaderLength);
88
Jeff Thompson942022d2012-12-29 21:57:47 -080089 typeAndVal = new BinaryXMLDecoder(this.headerBuffer.array).decodeTypeAndVal();
Jeff Thompson8dfa2912012-11-28 21:21:56 -080090 }
91 else {
92 // We didn't have to use the headerBuffer.
93 decoder.seek(this.offset - this.headerLength);
94 typeAndVal = decoder.decodeTypeAndVal();
95 }
96
Jeff Thompsondad617b2012-10-14 17:11:41 -070097 if (typeAndVal == null)
98 throw new Error("BinaryXMLStructureDecoder: Can't read header starting at offset " +
Jeff Thompson8dfa2912012-11-28 21:21:56 -080099 (this.offset - this.headerLength));
Jeff Thompsondad617b2012-10-14 17:11:41 -0700100
101 // Set the next state based on the type.
102 var type = typeAndVal.t;
103 if (type == XML_DATTR)
104 // We already consumed the item. READ_HEADER_OR_CLOSE again.
105 // ccnb has rules about what must follow an attribute, but we are just scanning.
Jeff Thompson8dfa2912012-11-28 21:21:56 -0800106 this.startHeader();
Jeff Thompsondad617b2012-10-14 17:11:41 -0700107 else if (type == XML_DTAG || type == XML_EXT) {
108 // Start a new level and READ_HEADER_OR_CLOSE again.
109 ++this.level;
Jeff Thompson8dfa2912012-11-28 21:21:56 -0800110 this.startHeader();
Jeff Thompsondad617b2012-10-14 17:11:41 -0700111 }
112 else if (type == XML_TAG || type == XML_ATTR) {
113 if (type == XML_TAG)
114 // Start a new level and read the tag.
115 ++this.level;
116 // Minimum tag or attribute length is 1.
Jeff Thompson11dc9b62012-11-28 19:21:20 -0800117 this.nBytesToRead = typeAndVal.v + 1;
Jeff Thompsondad617b2012-10-14 17:11:41 -0700118 this.state = BinaryXMLStructureDecoder.READ_BYTES;
119 // ccnb has rules about what must follow an attribute, but we are just scanning.
120 }
121 else if (type == XML_BLOB || type == XML_UDATA) {
Jeff Thompson11dc9b62012-11-28 19:21:20 -0800122 this.nBytesToRead = typeAndVal.v;
Jeff Thompsondad617b2012-10-14 17:11:41 -0700123 this.state = BinaryXMLStructureDecoder.READ_BYTES;
124 }
125 else
126 throw new Error("BinaryXMLStructureDecoder: Unrecognized header type " + type);
127 break;
128
129 case BinaryXMLStructureDecoder.READ_BYTES:
Jeff Thompson11dc9b62012-11-28 19:21:20 -0800130 var nRemainingBytes = input.length - this.offset;
131 if (nRemainingBytes < this.nBytesToRead) {
Jeff Thompsondad617b2012-10-14 17:11:41 -0700132 // Need more.
Jeff Thompson11dc9b62012-11-28 19:21:20 -0800133 this.offset += nRemainingBytes;
134 this.nBytesToRead -= nRemainingBytes;
Jeff Thompsondad617b2012-10-14 17:11:41 -0700135 return false;
136 }
137 // Got the bytes. Read a new header or close.
Jeff Thompson11dc9b62012-11-28 19:21:20 -0800138 this.offset += this.nBytesToRead;
Jeff Thompson8dfa2912012-11-28 21:21:56 -0800139 this.startHeader();
Jeff Thompsondad617b2012-10-14 17:11:41 -0700140 break;
141
142 default:
143 // We don't expect this to happen.
144 throw new Error("BinaryXMLStructureDecoder: Unrecognized state " + this.state);
145 }
146 }
147};
Jeff Thompson8dfa2912012-11-28 21:21:56 -0800148
149/*
150 * Set the state to READ_HEADER_OR_CLOSE and set up to start reading the header
151 */
152BinaryXMLStructureDecoder.prototype.startHeader = function() {
153 this.headerLength = 0;
154 this.useHeaderBuffer = false;
155 this.state = BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE;
156}
157
158/*
159 * Set the offset into the input, used for the next read.
160 */
161BinaryXMLStructureDecoder.prototype.seek = function(
162 //int
163 offset) {
164 this.offset = offset;
165}