| /* |
| * This class uses BinaryXMLDecoder to follow the structure of a ccnb binary element to |
| * determine its end. |
| * |
| * @author: Jeff Thompson |
| * See COPYING for copyright and distribution information. |
| */ |
| |
| var BinaryXMLStructureDecoder = function BinaryXMLDecoder() { |
| this.gotElementEnd = false; |
| this.offset = 0; |
| this.level = 0; |
| this.state = BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE; |
| this.headerStartOffset = 0; |
| this.nBytesToRead = 0; |
| }; |
| |
| BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE = 0; |
| BinaryXMLStructureDecoder.READ_BYTES = 1; |
| |
| /* |
| * Continue scanning input starting from this.offset. If found the end of the element |
| * which started at offset 0 then return true, else false. |
| * If this returns false, you should read more into input and call again. |
| * You have to pass in input each time because the array could be reallocated. |
| * This throws an exception for badly formed ccnb. |
| */ |
| BinaryXMLStructureDecoder.prototype.findElementEnd = function( |
| // byte array |
| input) |
| { |
| if (this.gotElementEnd) |
| // Someone is calling when we already got the end. |
| return true; |
| |
| var decoder = new BinaryXMLDecoder(input); |
| |
| while (true) { |
| if (this.offset >= input.length) |
| // All the cases assume we have some input. |
| return false; |
| |
| switch (this.state) { |
| case BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE: |
| // First check for XML_CLOSE. |
| if (this.offset == this.headerStartOffset && input[this.offset] == XML_CLOSE) { |
| ++this.offset; |
| // Close the level. |
| --this.level; |
| if (this.level == 0) |
| // Finished. |
| return true; |
| if (this.level < 0) |
| throw new Error("BinaryXMLStructureDecoder: Unexepected close tag at offset " + |
| (this.offset - 1)); |
| |
| // Get ready for the next header. |
| this.headerStartOffset = this.offset; |
| break; |
| } |
| |
| while (true) { |
| if (this.offset >= input.length) |
| return false; |
| if (input[this.offset++] & XML_TT_NO_MORE) |
| // Break and read the header. |
| break; |
| } |
| |
| decoder.seek(this.headerStartOffset); |
| var typeAndVal = decoder.decodeTypeAndVal(); |
| if (typeAndVal == null) |
| throw new Error("BinaryXMLStructureDecoder: Can't read header starting at offset " + |
| this.headerStartOffset); |
| |
| // Set the next state based on the type. |
| var type = typeAndVal.t; |
| if (type == XML_DATTR) |
| // We already consumed the item. READ_HEADER_OR_CLOSE again. |
| // ccnb has rules about what must follow an attribute, but we are just scanning. |
| this.headerStartOffset = this.offset; |
| else if (type == XML_DTAG || type == XML_EXT) { |
| // Start a new level and READ_HEADER_OR_CLOSE again. |
| ++this.level; |
| this.headerStartOffset = this.offset; |
| } |
| else if (type == XML_TAG || type == XML_ATTR) { |
| if (type == XML_TAG) |
| // Start a new level and read the tag. |
| ++this.level; |
| // Minimum tag or attribute length is 1. |
| this.nBytesToRead = typeAndVal.v + 1; |
| this.state = BinaryXMLStructureDecoder.READ_BYTES; |
| // ccnb has rules about what must follow an attribute, but we are just scanning. |
| } |
| else if (type == XML_BLOB || type == XML_UDATA) { |
| this.nBytesToRead = typeAndVal.v; |
| this.state = BinaryXMLStructureDecoder.READ_BYTES; |
| } |
| else |
| throw new Error("BinaryXMLStructureDecoder: Unrecognized header type " + type); |
| break; |
| |
| case BinaryXMLStructureDecoder.READ_BYTES: |
| var nRemainingBytes = input.length - this.offset; |
| if (nRemainingBytes < this.nBytesToRead) { |
| // Need more. |
| this.offset += nRemainingBytes; |
| this.nBytesToRead -= nRemainingBytes; |
| return false; |
| } |
| // Got the bytes. Read a new header or close. |
| this.offset += this.nBytesToRead; |
| this.headerStartOffset = this.offset; |
| this.state = BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE; |
| break; |
| |
| default: |
| // We don't expect this to happen. |
| throw new Error("BinaryXMLStructureDecoder: Unrecognized state " + this.state); |
| } |
| } |
| }; |