blob: 19f7cebb77be70ab43887005bdd75b4635220fbd [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
Jeff Thompson2b14c7e2013-07-29 15:09:56 -07009/**
10 * @constructor
11 */
Jeff Thompsondad617b2012-10-14 17:11:41 -070012var BinaryXMLStructureDecoder = function BinaryXMLDecoder() {
13 this.gotElementEnd = false;
Jeff Thompsonf5c56122013-06-26 19:52:06 -070014 this.offset = 0;
Jeff Thompsondad617b2012-10-14 17:11:41 -070015 this.level = 0;
16 this.state = BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE;
Jeff Thompson8dfa2912012-11-28 21:21:56 -080017 this.headerLength = 0;
18 this.useHeaderBuffer = false;
Jeff Thompson942022d2012-12-29 21:57:47 -080019 this.headerBuffer = new DynamicUint8Array(5);
Jeff Thompson11dc9b62012-11-28 19:21:20 -080020 this.nBytesToRead = 0;
Jeff Thompsondad617b2012-10-14 17:11:41 -070021};
22
23BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE = 0;
24BinaryXMLStructureDecoder.READ_BYTES = 1;
25
Jeff Thompson2b14c7e2013-07-29 15:09:56 -070026/**
Jeff Thompsondad617b2012-10-14 17:11:41 -070027 * Continue scanning input starting from this.offset. If found the end of the element
28 * which started at offset 0 then return true, else false.
29 * If this returns false, you should read more into input and call again.
30 * You have to pass in input each time because the array could be reallocated.
31 * This throws an exception for badly formed ccnb.
32 */
33BinaryXMLStructureDecoder.prototype.findElementEnd = function(
Jeff Thompson8dfa2912012-11-28 21:21:56 -080034 // Uint8Array
Jeff Thompsondad617b2012-10-14 17:11:41 -070035 input)
36{
37 if (this.gotElementEnd)
38 // Someone is calling when we already got the end.
39 return true;
40
41 var decoder = new BinaryXMLDecoder(input);
42
43 while (true) {
44 if (this.offset >= input.length)
45 // All the cases assume we have some input.
46 return false;
Wentao Shang2b740e62012-12-07 00:02:53 -080047
Jeff Thompsondad617b2012-10-14 17:11:41 -070048 switch (this.state) {
49 case BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE:
50 // First check for XML_CLOSE.
Jeff Thompson8dfa2912012-11-28 21:21:56 -080051 if (this.headerLength == 0 && input[this.offset] == XML_CLOSE) {
Jeff Thompsondad617b2012-10-14 17:11:41 -070052 ++this.offset;
53 // Close the level.
54 --this.level;
Jeff Thompsonf5c56122013-06-26 19:52:06 -070055 if (this.level == 0) {
Jeff Thompsondad617b2012-10-14 17:11:41 -070056 // Finished.
Jeff Thompsonf5c56122013-06-26 19:52:06 -070057 this.gotElementEnd = true;
Jeff Thompsondad617b2012-10-14 17:11:41 -070058 return true;
Jeff Thompsonf5c56122013-06-26 19:52:06 -070059 }
Jeff Thompsondad617b2012-10-14 17:11:41 -070060 if (this.level < 0)
Jeff Thompsonf5c56122013-06-26 19:52:06 -070061 throw new Error("BinaryXMLStructureDecoder: Unexpected close tag at offset " +
Jeff Thompsondad617b2012-10-14 17:11:41 -070062 (this.offset - 1));
63
64 // Get ready for the next header.
Jeff Thompson8dfa2912012-11-28 21:21:56 -080065 this.startHeader();
Jeff Thompsondad617b2012-10-14 17:11:41 -070066 break;
67 }
Jeff Thompson8dfa2912012-11-28 21:21:56 -080068
69 var startingHeaderLength = this.headerLength;
Jeff Thompsondad617b2012-10-14 17:11:41 -070070 while (true) {
Jeff Thompson8dfa2912012-11-28 21:21:56 -080071 if (this.offset >= input.length) {
72 // We can't get all of the header bytes from this input. Save in headerBuffer.
73 this.useHeaderBuffer = true;
74 var nNewBytes = this.headerLength - startingHeaderLength;
Jeff Thompson942022d2012-12-29 21:57:47 -080075 this.headerBuffer.set
Jeff Thompson8dfa2912012-11-28 21:21:56 -080076 (input.subarray(this.offset - nNewBytes, nNewBytes), startingHeaderLength);
77
Jeff Thompsondad617b2012-10-14 17:11:41 -070078 return false;
Jeff Thompson8dfa2912012-11-28 21:21:56 -080079 }
80 var headerByte = input[this.offset++];
81 ++this.headerLength;
82 if (headerByte & XML_TT_NO_MORE)
Jeff Thompsondad617b2012-10-14 17:11:41 -070083 // Break and read the header.
84 break;
85 }
Jeff Thompson8dfa2912012-11-28 21:21:56 -080086
87 var typeAndVal;
88 if (this.useHeaderBuffer) {
89 // Copy the remaining bytes into headerBuffer.
90 nNewBytes = this.headerLength - startingHeaderLength;
Jeff Thompson942022d2012-12-29 21:57:47 -080091 this.headerBuffer.set
Jeff Thompson8dfa2912012-11-28 21:21:56 -080092 (input.subarray(this.offset - nNewBytes, nNewBytes), startingHeaderLength);
93
Jeff Thompson942022d2012-12-29 21:57:47 -080094 typeAndVal = new BinaryXMLDecoder(this.headerBuffer.array).decodeTypeAndVal();
Jeff Thompson8dfa2912012-11-28 21:21:56 -080095 }
96 else {
97 // We didn't have to use the headerBuffer.
98 decoder.seek(this.offset - this.headerLength);
99 typeAndVal = decoder.decodeTypeAndVal();
100 }
101
Jeff Thompsondad617b2012-10-14 17:11:41 -0700102 if (typeAndVal == null)
103 throw new Error("BinaryXMLStructureDecoder: Can't read header starting at offset " +
Jeff Thompson8dfa2912012-11-28 21:21:56 -0800104 (this.offset - this.headerLength));
Jeff Thompsondad617b2012-10-14 17:11:41 -0700105
106 // Set the next state based on the type.
107 var type = typeAndVal.t;
108 if (type == XML_DATTR)
109 // We already consumed the item. READ_HEADER_OR_CLOSE again.
110 // ccnb has rules about what must follow an attribute, but we are just scanning.
Jeff Thompson8dfa2912012-11-28 21:21:56 -0800111 this.startHeader();
Jeff Thompsondad617b2012-10-14 17:11:41 -0700112 else if (type == XML_DTAG || type == XML_EXT) {
113 // Start a new level and READ_HEADER_OR_CLOSE again.
114 ++this.level;
Jeff Thompson8dfa2912012-11-28 21:21:56 -0800115 this.startHeader();
Jeff Thompsondad617b2012-10-14 17:11:41 -0700116 }
117 else if (type == XML_TAG || type == XML_ATTR) {
118 if (type == XML_TAG)
119 // Start a new level and read the tag.
120 ++this.level;
121 // Minimum tag or attribute length is 1.
Jeff Thompson11dc9b62012-11-28 19:21:20 -0800122 this.nBytesToRead = typeAndVal.v + 1;
Jeff Thompsondad617b2012-10-14 17:11:41 -0700123 this.state = BinaryXMLStructureDecoder.READ_BYTES;
124 // ccnb has rules about what must follow an attribute, but we are just scanning.
125 }
126 else if (type == XML_BLOB || type == XML_UDATA) {
Jeff Thompson11dc9b62012-11-28 19:21:20 -0800127 this.nBytesToRead = typeAndVal.v;
Jeff Thompsondad617b2012-10-14 17:11:41 -0700128 this.state = BinaryXMLStructureDecoder.READ_BYTES;
129 }
130 else
131 throw new Error("BinaryXMLStructureDecoder: Unrecognized header type " + type);
132 break;
133
134 case BinaryXMLStructureDecoder.READ_BYTES:
Jeff Thompson11dc9b62012-11-28 19:21:20 -0800135 var nRemainingBytes = input.length - this.offset;
136 if (nRemainingBytes < this.nBytesToRead) {
Jeff Thompsondad617b2012-10-14 17:11:41 -0700137 // Need more.
Jeff Thompson11dc9b62012-11-28 19:21:20 -0800138 this.offset += nRemainingBytes;
139 this.nBytesToRead -= nRemainingBytes;
Jeff Thompsondad617b2012-10-14 17:11:41 -0700140 return false;
141 }
142 // Got the bytes. Read a new header or close.
Jeff Thompson11dc9b62012-11-28 19:21:20 -0800143 this.offset += this.nBytesToRead;
Jeff Thompson8dfa2912012-11-28 21:21:56 -0800144 this.startHeader();
Jeff Thompsondad617b2012-10-14 17:11:41 -0700145 break;
146
147 default:
148 // We don't expect this to happen.
149 throw new Error("BinaryXMLStructureDecoder: Unrecognized state " + this.state);
150 }
151 }
152};
Jeff Thompson8dfa2912012-11-28 21:21:56 -0800153
Jeff Thompson2b14c7e2013-07-29 15:09:56 -0700154/**
Jeff Thompson8dfa2912012-11-28 21:21:56 -0800155 * Set the state to READ_HEADER_OR_CLOSE and set up to start reading the header
156 */
157BinaryXMLStructureDecoder.prototype.startHeader = function() {
Jeff Thompson2b14c7e2013-07-29 15:09:56 -0700158 this.headerLength = 0;
159 this.useHeaderBuffer = false;
160 this.state = BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE;
Jeff Thompson8dfa2912012-11-28 21:21:56 -0800161}
162
Jeff Thompson2b14c7e2013-07-29 15:09:56 -0700163/**
Jeff Thompson8dfa2912012-11-28 21:21:56 -0800164 * Set the offset into the input, used for the next read.
165 */
Jeff Thompson2b14c7e2013-07-29 15:09:56 -0700166BinaryXMLStructureDecoder.prototype.seek = function(offset) {
167 this.offset = offset;
Jeff Thompson8dfa2912012-11-28 21:21:56 -0800168}