blob: 9b99fd58a64091dc65b7781eac7c09c8872546e3 [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;
Jeff Thompsonf5c56122013-06-26 19:52:06 -070011 this.offset = 0;
Jeff Thompsondad617b2012-10-14 17:11:41 -070012 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;
Jeff Thompsonf5c56122013-06-26 19:52:06 -070052 if (this.level == 0) {
Jeff Thompsondad617b2012-10-14 17:11:41 -070053 // Finished.
Jeff Thompsonf5c56122013-06-26 19:52:06 -070054 this.gotElementEnd = true;
Jeff Thompsondad617b2012-10-14 17:11:41 -070055 return true;
Jeff Thompsonf5c56122013-06-26 19:52:06 -070056 }
Jeff Thompsondad617b2012-10-14 17:11:41 -070057 if (this.level < 0)
Jeff Thompsonf5c56122013-06-26 19:52:06 -070058 throw new Error("BinaryXMLStructureDecoder: Unexpected close tag at offset " +
Jeff Thompsondad617b2012-10-14 17:11:41 -070059 (this.offset - 1));
60
61 // Get ready for the next header.
Jeff Thompson8dfa2912012-11-28 21:21:56 -080062 this.startHeader();
Jeff Thompsondad617b2012-10-14 17:11:41 -070063 break;
64 }
Jeff Thompson8dfa2912012-11-28 21:21:56 -080065
66 var startingHeaderLength = this.headerLength;
Jeff Thompsondad617b2012-10-14 17:11:41 -070067 while (true) {
Jeff Thompson8dfa2912012-11-28 21:21:56 -080068 if (this.offset >= input.length) {
69 // We can't get all of the header bytes from this input. Save in headerBuffer.
70 this.useHeaderBuffer = true;
71 var nNewBytes = this.headerLength - startingHeaderLength;
Jeff Thompson942022d2012-12-29 21:57:47 -080072 this.headerBuffer.set
Jeff Thompson8dfa2912012-11-28 21:21:56 -080073 (input.subarray(this.offset - nNewBytes, nNewBytes), startingHeaderLength);
74
Jeff Thompsondad617b2012-10-14 17:11:41 -070075 return false;
Jeff Thompson8dfa2912012-11-28 21:21:56 -080076 }
77 var headerByte = input[this.offset++];
78 ++this.headerLength;
79 if (headerByte & XML_TT_NO_MORE)
Jeff Thompsondad617b2012-10-14 17:11:41 -070080 // Break and read the header.
81 break;
82 }
Jeff Thompson8dfa2912012-11-28 21:21:56 -080083
84 var typeAndVal;
85 if (this.useHeaderBuffer) {
86 // Copy the remaining bytes into headerBuffer.
87 nNewBytes = this.headerLength - startingHeaderLength;
Jeff Thompson942022d2012-12-29 21:57:47 -080088 this.headerBuffer.set
Jeff Thompson8dfa2912012-11-28 21:21:56 -080089 (input.subarray(this.offset - nNewBytes, nNewBytes), startingHeaderLength);
90
Jeff Thompson942022d2012-12-29 21:57:47 -080091 typeAndVal = new BinaryXMLDecoder(this.headerBuffer.array).decodeTypeAndVal();
Jeff Thompson8dfa2912012-11-28 21:21:56 -080092 }
93 else {
94 // We didn't have to use the headerBuffer.
95 decoder.seek(this.offset - this.headerLength);
96 typeAndVal = decoder.decodeTypeAndVal();
97 }
98
Jeff Thompsondad617b2012-10-14 17:11:41 -070099 if (typeAndVal == null)
100 throw new Error("BinaryXMLStructureDecoder: Can't read header starting at offset " +
Jeff Thompson8dfa2912012-11-28 21:21:56 -0800101 (this.offset - this.headerLength));
Jeff Thompsondad617b2012-10-14 17:11:41 -0700102
103 // Set the next state based on the type.
104 var type = typeAndVal.t;
105 if (type == XML_DATTR)
106 // We already consumed the item. READ_HEADER_OR_CLOSE again.
107 // ccnb has rules about what must follow an attribute, but we are just scanning.
Jeff Thompson8dfa2912012-11-28 21:21:56 -0800108 this.startHeader();
Jeff Thompsondad617b2012-10-14 17:11:41 -0700109 else if (type == XML_DTAG || type == XML_EXT) {
110 // Start a new level and READ_HEADER_OR_CLOSE again.
111 ++this.level;
Jeff Thompson8dfa2912012-11-28 21:21:56 -0800112 this.startHeader();
Jeff Thompsondad617b2012-10-14 17:11:41 -0700113 }
114 else if (type == XML_TAG || type == XML_ATTR) {
115 if (type == XML_TAG)
116 // Start a new level and read the tag.
117 ++this.level;
118 // Minimum tag or attribute length is 1.
Jeff Thompson11dc9b62012-11-28 19:21:20 -0800119 this.nBytesToRead = typeAndVal.v + 1;
Jeff Thompsondad617b2012-10-14 17:11:41 -0700120 this.state = BinaryXMLStructureDecoder.READ_BYTES;
121 // ccnb has rules about what must follow an attribute, but we are just scanning.
122 }
123 else if (type == XML_BLOB || type == XML_UDATA) {
Jeff Thompson11dc9b62012-11-28 19:21:20 -0800124 this.nBytesToRead = typeAndVal.v;
Jeff Thompsondad617b2012-10-14 17:11:41 -0700125 this.state = BinaryXMLStructureDecoder.READ_BYTES;
126 }
127 else
128 throw new Error("BinaryXMLStructureDecoder: Unrecognized header type " + type);
129 break;
130
131 case BinaryXMLStructureDecoder.READ_BYTES:
Jeff Thompson11dc9b62012-11-28 19:21:20 -0800132 var nRemainingBytes = input.length - this.offset;
133 if (nRemainingBytes < this.nBytesToRead) {
Jeff Thompsondad617b2012-10-14 17:11:41 -0700134 // Need more.
Jeff Thompson11dc9b62012-11-28 19:21:20 -0800135 this.offset += nRemainingBytes;
136 this.nBytesToRead -= nRemainingBytes;
Jeff Thompsondad617b2012-10-14 17:11:41 -0700137 return false;
138 }
139 // Got the bytes. Read a new header or close.
Jeff Thompson11dc9b62012-11-28 19:21:20 -0800140 this.offset += this.nBytesToRead;
Jeff Thompson8dfa2912012-11-28 21:21:56 -0800141 this.startHeader();
Jeff Thompsondad617b2012-10-14 17:11:41 -0700142 break;
143
144 default:
145 // We don't expect this to happen.
146 throw new Error("BinaryXMLStructureDecoder: Unrecognized state " + this.state);
147 }
148 }
149};
Jeff Thompson8dfa2912012-11-28 21:21:56 -0800150
151/*
152 * Set the state to READ_HEADER_OR_CLOSE and set up to start reading the header
153 */
154BinaryXMLStructureDecoder.prototype.startHeader = function() {
155 this.headerLength = 0;
156 this.useHeaderBuffer = false;
157 this.state = BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE;
158}
159
160/*
161 * Set the offset into the input, used for the next read.
162 */
163BinaryXMLStructureDecoder.prototype.seek = function(
164 //int
165 offset) {
166 this.offset = offset;
167}