context: /**
   * Copyright (C) 2013-2015 Regents of the University of California.
   * Authors:
   *		Jeff Thompson <jefft0@remap.ucla.edu>
   *		Rafael Teixeira <monoman@gmail.com>
   *
   * This program is free software: you can redistribute it and/or modify
   * it under the terms of the GNU Lesser General Public License as published by
   * the Free Software Foundation, either version 3 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public License
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   * A copy of the GNU Lesser General Public License is in the file COPYING.
   */
  
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using System.Threading.Tasks;
  
  namespace Net.NamedData.Encoding
  {
  
  	
  	using Net.NamedData.Util;
  
  	/**
  	 * A BinaryXmlDecoder has methods to Decode an input according to Binary XML.
  	 */
  	public class BinaryXmlDecoder
  	{
  		/**
  		 * Create a new BinaryXmlDecoder to Decode the input.
  		 * @param input The input ByteBuffer whose position and limit are set to the desired bytes to Decode.
  		 * This calls input.duplicate(), but does not copy the underlying buffer whose contents must remain valid during
  		 * the life of this object.
  		 */
  		public
  		BinaryXmlDecoder(ByteBuffer input)
  		{
  			input_ = input.duplicate();
  		}
  
  		public class TypeAndValue
  		{
  			public
  			TypeAndValue(int type, int value)
  			{
  				type_ = type;
  				value_ = value;
  			}
  
  			int
  			getType() { return type_; }
  
  			int
  			getValue() { return value_; }
  
  			private readonly int type_;
  			private readonly int value_;
  		}
  
  		/**
  		 * Decode the header's type and value from the input starting at its position. Update the input's position.
  		 * @return a TypeAndValue with the header's type and value
  		 * @ For invalid encoding including if the first head octet is zero.
  		 */
  		public TypeAndValue
  		decodeTypeAndValue()
  		{
  			int value = 0;
  			bool gotFirstOctet = false;
  
  			while (true) {
  				int octet = (int)input_.get();
  
  				if (!gotFirstOctet) {
  					if (octet == 0)
  						throw new EncodingException("The first header octet may not be zero");
  
  					gotFirstOctet = true;
  				}
  
  				if ((octet & BinaryXml.TT_FINAL) != 0) {
  					// Finished.
  					value = (value << BinaryXml.TT_VALUE_BITS) | ((octet >> BinaryXml.TT_BITS) & BinaryXml.TT_VALUE_MASK);
  					return new TypeAndValue(octet & BinaryXml.TT_MASK, value);
  				}
  
  				value = (value << BinaryXml.REGULAR_VALUE_BITS) | (octet & BinaryXml.REGULAR_VALUE_MASK);
  			}
  		}
  
  		/**
  		 * Decode the header from the input starting at its position, expecting the type to be DTAG and the value to be expectedTag.
  		 * Update the input's position.
  		 * @param expectedTag The expected value for DTAG.
  		 * @ For invalid encoding including if did not get the expected DTAG.
  		 */
  		public void
  		readElementStartDTag(int expectedTag)
  		{
  			if (input_.position() == previouslyPeekedDTagStartOffset_) {
  				// peekDTag already decoded this DTag.
  				if (previouslyPeekedDTag_ != expectedTag)
  					throw new EncodingException("Did not get the expected DTAG " + expectedTag + ", got " + previouslyPeekedDTag_);
  
  				// Fast forward past the header.
  				input_.position(previouslyPeekedDTagEndOffset_);
  			} else {
  				TypeAndValue typeAndValue = decodeTypeAndValue();
  				if (typeAndValue.getType() != BinaryXml.DTAG)
  					throw new EncodingException("Header type is not a DTAG");
  
  				if (typeAndValue.getValue() != expectedTag)
  					throw new EncodingException("Did not get the expected DTAG " + expectedTag + ", got " + typeAndValue.getValue());
  			}
  		}
  
  		/**
  		 * Read one byte from the input starting at its position, expecting it to be the element Close.
  		 * Update the input's position.
  		 * @ For invalid encoding including if did not get the expected element Close.
  		 */
  		public void
  		readElementClose()
  		{
  			if (input_.get() != BinaryXml.CLOSE)
  				throw new EncodingException("Did not get the expected element Close");
  		}
  
  		/**
  		 * Decode the header from the input starting at its position, and if it is a DTAG where the value is the expectedTag,
  		 * then set return true.  Do not update the input's position.
  		 * @param expectedTag The expected value for DTAG.
  		 * @return True if the tag is the expected tag, otherwise false.
  		 * @ For invalid encoding including if did not get the expected DTAG.
  		 */
  		public bool
  		peekDTag(int expectedTag)
  		{
  			if (input_.position() == previouslyPeekedDTagStartOffset_)
  				// We already decoded this DTag.
  				return previouslyPeekedDTag_ == expectedTag;
  			else {
  				// First check if it is an element Close (which cannot be the expected tag).
  				if (input_.get(input_.position()) == BinaryXml.CLOSE)
  					return false;
  
  				int savePosition = input_.position();
  				TypeAndValue typeAndValue = decodeTypeAndValue();
  				// readElementStartDTag will use this to fast forward.
  				previouslyPeekedDTagEndOffset_ = input_.position();
  				// Restore the position.
  				input_.position(savePosition);
  
  				if (typeAndValue.getType() == BinaryXml.DTAG) {
  					previouslyPeekedDTagStartOffset_ = savePosition;
  					previouslyPeekedDTag_ = typeAndValue.getValue();
  
  					return typeAndValue.getValue() == expectedTag;
  				} else
  					return false;
  			}
  		}
  
  		/**
  		 * Decode the header from the input starting its position, expecting the type to be DTAG and the value to be expectedTag.
  		 * Then read one item of any type (presumably BLOB, UDATA, TAG or ATTR) and return a
  		 * ByteBuffer. However, if allowNull is true, then the item may be absent.
  		 * Finally, read the element Close.  Update the input's position.
  		 * @param expectedTag The expected value for DTAG.
  		 * @param allowNull True if the binary item may be missing.
  		 * @return a ByteBuffer which is a slice on the data inside the input buffer. However,
  		 * if allowNull is true and the binary data item is absent, then return null.
  		 * @ For invalid encoding including if did not get the expected DTAG.
  		 */
  		public ByteBuffer
  		readBinaryDTagElement(int expectedTag, bool allowNull)
  		{
  			readElementStartDTag(expectedTag);
  			if (allowNull) {
  				if (input_.get(input_.position()) == BinaryXml.CLOSE) {
  					// The binary item is missing, and this is allowed, so read the element Close and return a null value.
  					input_.get();
  					return null;
  				}
  			}
  
  			TypeAndValue typeAndValue = decodeTypeAndValue();
  			// Ignore the type.
  			// Temporarily set the limit so we can call slice.
  			int saveLimit = input_.limit();
  			input_.limit(input_.position() + typeAndValue.getValue());
  
gt: 			ByteBuffer result = input_.slice();