Added BinaryXMLStructureDecoder.  Use it in NDNSocketTransportService to detect the end of the input (instead of the hack to try to decode a ContentObject).
diff --git a/js/NDNSocketTransportService.js b/js/NDNSocketTransportService.js
index 30c901f..aa12588 100644
--- a/js/NDNSocketTransportService.js
+++ b/js/NDNSocketTransportService.js
@@ -32,6 +32,7 @@
 	var inStream = transport.openInputStream(0, 0, 0);
 	var dataListener = {
 		data: [],
+        structureDecoder: new BinaryXMLStructureDecoder(),
 		calledOnReceivedData: false,
         debugNOnDataAvailable: 0,
 		
@@ -46,29 +47,21 @@
 			}
 		},
 		onDataAvailable: function (request, context, _inputStream, offset, count) {
+            if (this.calledOnReceivedData)
+                // Already finished.  Ignore extra data.
+                return;
+            
 			try {
                 this.debugNOnDataAvailable += 1;
 				// Ignore _inputStream and use inStream.
 				// Use readInputStreamToString to handle binary data.
 				var rawData = NetUtil.readInputStreamToString(inStream, count);
-                // Append to this.data.
                 this.data = this.data.concat(DataUtils.toNumbersFromString(rawData));
 				
-				// TODO: Need to parse the input to check if a whole ccnb object has been read, as in 
-				// CcnbObjectReader class: https://github.com/NDN-Routing/NDNLP/blob/master/ndnld.h#L256 .
-				// For now as a hack, try to fully decode this.data as a ContentObject.
-				try {
-                    var decoder = new BinaryXMLDecoder(this.data);	
-                    var co = new ContentObject();
-                    co.from_ccnb(decoder);
-				} catch (ex) {
-					// Assume the exception is because the decoder only got partial data, so read moe.
-                    dump("Awaiting more data at onDataAvailable call # " + 
-                        this.debugNOnDataAvailable + "\n");
-					return;
-				}
-				// We were able to parse the ContentObject, so finish.
-				this.onStopRequest();
+				// Scan the input to check if a whole ccnb object has been read.
+                if (this.structureDecoder.findElementEnd(this.data))
+                    // Finish.
+                    this.onStopRequest();
 			} catch (ex) {
 				dump("onDataAvailable exception: " + ex + "\n");
 			}
diff --git a/js/ccnxProtocol.xpi b/js/ccnxProtocol.xpi
index a39d75b..9fd4dbb 100644
--- a/js/ccnxProtocol.xpi
+++ b/js/ccnxProtocol.xpi
Binary files differ
diff --git a/js/ccnxProtocol/modules/make-ndn-js.jsm.sh b/js/ccnxProtocol/modules/make-ndn-js.jsm.sh
index 65eb1e7..2c4209b 100755
--- a/js/ccnxProtocol/modules/make-ndn-js.jsm.sh
+++ b/js/ccnxProtocol/modules/make-ndn-js.jsm.sh
@@ -15,6 +15,7 @@
   ../../ForwardingEntry.js emptyLine.txt \
   ../../encoding/BinaryXMLEncoder.js emptyLine.txt \
   ../../encoding/BinaryXMLDecoder.js emptyLine.txt \
+  ../../encoding/BinaryXMLStructureDecoder.js emptyLine.txt \
   ../../encoding/DataUtils.js emptyLine.txt \
   ../../encoding/EncodingUtils.js emptyLine.txt \
   ../../security/KeyManager.js emptyLine.txt \
diff --git a/js/ccnxProtocol/modules/ndn-js.jsm b/js/ccnxProtocol/modules/ndn-js.jsm
index a336031..e5f2837 100644
--- a/js/ccnxProtocol/modules/ndn-js.jsm
+++ b/js/ccnxProtocol/modules/ndn-js.jsm
@@ -220,6 +220,7 @@
 	var inStream = transport.openInputStream(0, 0, 0);
 	var dataListener = {
 		data: [],
+        structureDecoder: new BinaryXMLStructureDecoder(),
 		calledOnReceivedData: false,
         debugNOnDataAvailable: 0,
 		
@@ -234,28 +235,21 @@
 			}
 		},
 		onDataAvailable: function (request, context, _inputStream, offset, count) {
+            if (this.calledOnReceivedData)
+                // Already finished.  Ignore extra data.
+                return;
+            
 			try {
                 this.debugNOnDataAvailable += 1;
 				// Ignore _inputStream and use inStream.
 				// Use readInputStreamToString to handle binary data.
 				var rawData = NetUtil.readInputStreamToString(inStream, count);
-                // Append to this.data.
                 this.data = this.data.concat(DataUtils.toNumbersFromString(rawData));
 				
-				// TODO: Need to parse the input to check if a whole ccnb object has been read, as in 
-				// CcnbObjectReader class: https://github.com/NDN-Routing/NDNLP/blob/master/ndnld.h#L256 .
-				// For now as a hack, try to fully decode this.data as a ContentObject.
-				try {
-                    var decoder = new BinaryXMLDecoder(this.data);	
-                    var co = new ContentObject();
-                    co.from_ccnb(decoder);
-				} catch (ex) {
-					// Assume the exception is because the decoder only got partial data, so read moe.
-                    dump("Awaiting more data at onDataAvailable call # " + this.debugNOnDataAvailable);
-					return;
-				}
-				// We were able to parse the ContentObject, so finish.
-				this.onStopRequest();
+				// Scan the input to check if a whole ccnb object has been read.
+                if (this.structureDecoder.findElementEnd(this.data))
+                    // Finish.
+                    this.onStopRequest();
 			} catch (ex) {
 				dump("onDataAvailable exception: " + ex + "\n");
 			}
@@ -2101,7 +2095,7 @@
 ForwardingEntry.prototype.getElementLabel = function() { return CCNProtocolDTags.ForwardingEntry; }
 
 /*
- * This class is used to encode and decode binary elements ( blog, type/value pairs)
+ * This class is used to encode ccnb binary elements (blob, type/value pairs).
  * 
  * @author: ucla-cs
  * See COPYING for copyright and distribution information.
@@ -2545,7 +2539,7 @@
 
 
 /*
- * This class is used to encode and decode binary elements ( blog, type/value pairs)
+ * This class is used to decode ccnb binary elements (blob, type/value pairs).
  * 
  * @author: ucla-cs
  * See COPYING for copyright and distribution information.
@@ -2617,49 +2611,9 @@
 	var DEBUG_MAX_LEN =  32768;
 	
 	this.istream = istream;
-	//console.log('istream is '+ this.istream);
 	this.offset = 0;
 };
 
-
-BinaryXMLDecoder.prototype.readStartElement =function(
-	//String
-	startTag,				    
-	//TreeMap<String, String>
-	attributes) {
-
-	try {
-		//this.TypeAndVal 
-		tv = this.decodeTypeAndVal(this.istream);
-
-		if (null == tv) {
-			throw new Error("Expected start element: " + startTag + " got something not a tag.");
-		}
-		
-		//String 
-		decodedTag = null;
-		
-		if (tv.type() == XML_TAG) {
-			
-			decodedTag = this.decodeUString(this.Istream, tv.val()+1);
-			
-		} else if (tv.type() == XML_DTAG) {
-			decodedTag = tagToString(tv.val());	
-		}
-		
-		if ((null ==  decodedTag) || decodedTag != startTag) {
-			throw new Error("Expected start element: " + startTag + " got: " + decodedTag + "(" + tv.val() + ")");
-		}
-		
-		if (null != attributes) {
-			readAttributes(attributes); 
-		}
-		
-	} catch (e) {
-		throw new Error("readStartElement", e);
-	}
-};
-
 BinaryXMLDecoder.prototype.readAttributes = function(
 	//TreeMap<String,String> 
 	attributes){
@@ -2671,18 +2625,18 @@
 	try {
 
 		//this.TypeAndVal 
-		nextTV = this.peekTypeAndVal(this.istream);
+		nextTV = this.peekTypeAndVal();
 
 		while ((null != nextTV) && ((XML_ATTR == nextTV.type()) ||
 				(XML_DATTR == nextTV.type()))) {
 
 			//this.TypeAndVal 
-			thisTV = this.decodeTypeAndVal(this.Istream);
+			thisTV = this.decodeTypeAndVal();
 
 			var attributeName = null;
 			if (XML_ATTR == thisTV.type()) {
 				
-				attributeName = this.decodeUString(this.istream, thisTV.val()+1);
+				attributeName = this.decodeUString(thisTV.val()+1);
 
 			} else if (XML_DATTR == thisTV.type()) {
 				// DKS TODO are attributes same or different dictionary?
@@ -2692,11 +2646,11 @@
 				}
 			}
 			
-			var attributeValue = this.decodeUString(this.istream);
+			var attributeValue = this.decodeUString();
 
 			attributes.put(attributeName, attributeValue);
 
-			nextTV = this.peekTypeAndVal(this.istream);
+			nextTV = this.peekTypeAndVal();
 		}
 
 	} catch ( e) {
@@ -2733,7 +2687,7 @@
 		
 		//try {
 			//TypeAndVal 
-			tv = this.decodeTypeAndVal(this.istream);
+			tv = this.decodeTypeAndVal();
 			
 			if (null == tv) {
 				throw new Error("Expected start element: " + startTag + " got something not a tag.");
@@ -2758,7 +2712,7 @@
 				
 				//console.log('valval is ' +valval);
 				
-				decodedTag = this.decodeUString(this.istream,  valval);
+				decodedTag = this.decodeUString(valval);
 				
 			} else if (tv.type() == XML_DTAG) {
 				//console.log('gothere');
@@ -2802,14 +2756,14 @@
 	try {
 		// Now need to get attributes.
 		//TypeAndVal 
-		nextTV = this.peekTypeAndVal(this.istream);
+		nextTV = this.peekTypeAndVal();
 
 		while ((null != nextTV) && ((XML_ATTR == nextTV.type()) ||
 				(XML_DATTR == nextTV.type()))) {
 
 			// Decode this attribute. First, really read the type and value.
 			//this.TypeAndVal 
-			thisTV = this.decodeTypeAndVal(this.istream);
+			thisTV = this.decodeTypeAndVal();
 
 			//String 
 			attributeName = null;
@@ -2822,7 +2776,7 @@
 				else
 					valval = (tv.val())+ 1;
 				
-				attributeName = this.decodeUString(this.istream,valval);
+				attributeName = this.decodeUString(valval);
 
 			} else if (XML_DATTR == thisTV.type()) {
 				// DKS TODO are attributes same or different dictionary?
@@ -2833,12 +2787,12 @@
 			}
 			// Attribute values are always UDATA
 			//String
-			attributeValue = this.decodeUString(this.istream);
+			attributeValue = this.decodeUString();
 
 			//
 			attributes.push([attributeName, attributeValue]);
 
-			nextTV = this.peekTypeAndVal(this.istream);
+			nextTV = this.peekTypeAndVal();
 		}
 
 	} catch ( e) {
@@ -2858,7 +2812,7 @@
 		// Have to distinguish genuine errors from wrong tags. Could either use
 		// a special exception subtype, or redo the work here.
 		//this.TypeAndVal 
-		tv = this.decodeTypeAndVal(this.istream);
+		tv = this.decodeTypeAndVal();
 
 		if (null != tv) {
 
@@ -2875,7 +2829,7 @@
 				else
 					valval = (tv.val())+ 1;
 				
-				decodedTag = this.decodeUString(this.istream, valval);
+				decodedTag = this.decodeUString(valval);
 				
 				//Log.info(Log.FAC_ENCODING, "Unexpected: got text tag in peekStartElement; length: " + valval + " decoded tag = " + decodedTag);
 
@@ -2934,7 +2888,7 @@
 			// Have to distinguish genuine errors from wrong tags. Could either use
 			// a special exception subtype, or redo the work here.
 			//this.TypeAndVal
-			tv = this.decodeTypeAndVal(this.istream);
+			tv = this.decodeTypeAndVal();
 
 			if (null != tv) {
 
@@ -2952,7 +2906,7 @@
 					
 					// Tag value represents length-1 as tags can never be empty.
 					//String 
-					strTag = this.decodeUString(this.istream, valval);
+					strTag = this.decodeUString(valval);
 					
 					decodedTag = stringToTag(strTag);
 					
@@ -3022,7 +2976,7 @@
 //String	
 BinaryXMLDecoder.prototype.readUString = function(){
 			//String 
-			ustring = this.decodeUString(this.istream);	
+			ustring = this.decodeUString();	
 			this.readEndElement();
 			return ustring;
 
@@ -3033,7 +2987,7 @@
 BinaryXMLDecoder.prototype.readBlob = function() {
 			//byte []
 			
-			blob = this.decodeBlob(this.istream);	
+			blob = this.decodeBlob();	
 			this.readEndElement();
 			return blob;
 
@@ -3073,9 +3027,7 @@
 	return timestamp;
 };
 
-BinaryXMLDecoder.prototype.decodeTypeAndVal = function(
-		/*InputStream*/
-		istream) {
+BinaryXMLDecoder.prototype.decodeTypeAndVal = function() {
 	
 	/*int*/next;
 	/*int*/type = -1;
@@ -3123,20 +3075,18 @@
 
 
 //TypeAndVal
-BinaryXMLDecoder.peekTypeAndVal = function(
-		//InputStream 
-		istream) {
+BinaryXMLDecoder.peekTypeAndVal = function() {
 	//TypeAndVal 
 	tv = null;
 	
-	//istream.mark(LONG_BYTES*2);		
+	//this.istream.mark(LONG_BYTES*2);		
 	
 	var previousOffset = this.offset;
 	
 	try {
-		tv = this.decodeTypeAndVal(this.istream);
+		tv = this.decodeTypeAndVal();
 	} finally {
-		//istream.reset();
+		//this.istream.reset();
 		this.offset = previousOffset;
 	}
 	
@@ -3146,15 +3096,13 @@
 
 //byte[]
 BinaryXMLDecoder.prototype.decodeBlob = function(
-		//InputStream 
-		istream, 
 		//int 
 		blobLength) {
 	
 	
 	if(null == blobLength){
 		//TypeAndVal
-		tv = this.decodeTypeAndVal(this.istream);
+		tv = this.decodeTypeAndVal();
 
 		var valval ;
 		
@@ -3165,7 +3113,7 @@
 			valval = (tv.val());
 		
 		//console.log('valval here is ' + valval);
-		return  this.decodeBlob(this.istream, valval);
+		return  this.decodeBlob(valval);
 	}
 	
 	//
@@ -3185,8 +3133,6 @@
 
 //String
 BinaryXMLDecoder.prototype.decodeUString = function(
-		//InputStream 
-		istream, 
 		//int 
 		byteLength) {
 	
@@ -3196,7 +3142,7 @@
 	count++;
 	if(null == byteLength||  undefined == byteLength){
 		console.log("!!!!");
-		tv = this.decodeTypeAndVal(this.istream);
+		tv = this.decodeTypeAndVal();
 		var valval ;
 		if(typeof tv.val() == 'string'){
 			valval = (parseInt(tv.val()));
@@ -3205,7 +3151,7 @@
 			valval = (tv.val());
 		
 		if(LOG>4) console.log('valval  is ' + valval);
-		byteLength= this.decodeUString(this.istream, valval);
+		byteLength= this.decodeUString(valval);
 		
 		//if(LOG>4) console.log('byte Length found in type val is '+ byteLength.charCodeAt(0));
 		byteLength = parseInt(byteLength);
@@ -3217,7 +3163,7 @@
 	if(LOG>4)console.log('byteLength is '+byteLength);
 	if(LOG>4)console.log('type of byteLength is '+typeof byteLength);
 	
-	stringBytes = this.decodeBlob(this.istream, byteLength);
+	stringBytes = this.decodeBlob(byteLength);
 	
 	//console.log('String bytes are '+ stringBytes);
 	//console.log('stringBytes);
@@ -3249,7 +3195,7 @@
 		var tempStreamPosition = this.offset;
 			
 		//TypeAndVal 
-		tv = this.decodeTypeAndVal(istream);
+		tv = this.decodeTypeAndVal();
 		
 		if(LOG>3)console.log('TV is '+tv);
 		if(LOG>3)console.log(tv);
@@ -3265,11 +3211,11 @@
 			return "";
 		}
 			
-		return this.decodeUString(istream, tv.val());
+		return this.decodeUString(tv.val());
 	}
 	else{
 		//byte [] 
-		stringBytes = this.decodeBlob(istream, byteLength);
+		stringBytes = this.decodeBlob(byteLength);
 		
 		//return DataUtils.getUTF8StringFromBytes(stringBytes);
 		return  DataUtils.toString(stringBytes);
@@ -3334,6 +3280,137 @@
 		return strElementText;
 };
 
+/* 
+ * Set the offset into the input, used for the next read.
+ */
+BinaryXMLDecoder.prototype.seek = function(
+        //int
+        offset) {
+    this.offset = offset;        
+}
+
+/*
+ * This class uses BinaryXMLDecoder to follow the structure of a ccnb binary element to 
+ * determine its end.
+ * 
+ * @author: ucla-cs
+ * 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.readBytesEndOffset = 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.readBytesEndOffset = this.offset + 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.readBytesEndOffset = this.offset + typeAndVal.v;
+                    this.state = BinaryXMLStructureDecoder.READ_BYTES;
+                }
+                else
+                    throw new Error("BinaryXMLStructureDecoder: Unrecognized header type " + type);
+                break;
+            
+            case BinaryXMLStructureDecoder.READ_BYTES:
+                if (input.length < this.readBytesEndOffset) {
+                    // Need more.
+                    this.offset = input.length;
+                    return false;
+                }
+                // Got the bytes.  Read a new header or close.
+                this.offset = this.readBytesEndOffset;
+                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);
+        }
+    }
+};
+
 /*
  * This class contains utilities to help parse the data
  * author: ucla-cs
diff --git a/js/encoding/BinaryXMLStructureDecoder.js b/js/encoding/BinaryXMLStructureDecoder.js
new file mode 100644
index 0000000..4ad3de9
--- /dev/null
+++ b/js/encoding/BinaryXMLStructureDecoder.js
@@ -0,0 +1,121 @@
+/*
+ * This class uses BinaryXMLDecoder to follow the structure of a ccnb binary element to 
+ * determine its end.
+ * 
+ * @author: ucla-cs
+ * 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.readBytesEndOffset = 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.readBytesEndOffset = this.offset + 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.readBytesEndOffset = this.offset + typeAndVal.v;
+                    this.state = BinaryXMLStructureDecoder.READ_BYTES;
+                }
+                else
+                    throw new Error("BinaryXMLStructureDecoder: Unrecognized header type " + type);
+                break;
+            
+            case BinaryXMLStructureDecoder.READ_BYTES:
+                if (input.length < this.readBytesEndOffset) {
+                    // Need more.
+                    this.offset = input.length;
+                    return false;
+                }
+                // Got the bytes.  Read a new header or close.
+                this.offset = this.readBytesEndOffset;
+                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);
+        }
+    }
+};