/// <summary> /// Continue scanning input starting from offset_ to find the element end. /// If the end of the element which started at offset 0 is found, this returns /// true and getOffset() is the length of the element. Otherwise, this returns /// false which means you should read more into input and call again. /// </summary> /// /// <param name="input">You have to pass in input each time because the buffer could be reallocated.</param> /// <returns>true if found the element end, false if not.</returns> public bool findElementEnd(ByteBuffer input) { if (gotElementEnd_) // Someone is calling when we already got the end. return true; TlvDecoder decoder = new TlvDecoder(input); while (true) { if (offset_ >= input.limit()) // All the cases assume we have some input. Return and wait for more. return false; if (state_ == TlvStructureDecoder.READ_TYPE) { int firstOctet = (int) input.get(offset_) & 0xff; offset_ += 1; if (firstOctet < 253) // The value is simple, so we can skip straight to reading the length. state_ = TlvStructureDecoder.READ_LENGTH; else { // Set up to skip the type bytes. if (firstOctet == 253) nBytesToRead_ = 2; else if (firstOctet == 254) nBytesToRead_ = 4; else // value == 255. nBytesToRead_ = 8; state_ = TlvStructureDecoder.READ_TYPE_BYTES; } } else if (state_ == TlvStructureDecoder.READ_TYPE_BYTES) { int nRemainingBytes = input.limit() - offset_; if (nRemainingBytes < nBytesToRead_) { // Need more. offset_ += nRemainingBytes; nBytesToRead_ -= nRemainingBytes; return false; } // Got the type bytes. Move on to read the length. offset_ += nBytesToRead_; state_ = TlvStructureDecoder.READ_LENGTH; } else if (state_ == TlvStructureDecoder.READ_LENGTH) { int firstOctet_0 = (int) input.get(offset_) & 0xff; offset_ += 1; if (firstOctet_0 < 253) { // The value is simple, so we can skip straight to reading // the value bytes. nBytesToRead_ = firstOctet_0; if (nBytesToRead_ == 0) { // No value bytes to read. We're finished. gotElementEnd_ = true; return true; } state_ = TlvStructureDecoder.READ_VALUE_BYTES; } else { // We need to read the bytes in the extended encoding of // the length. if (firstOctet_0 == 253) nBytesToRead_ = 2; else if (firstOctet_0 == 254) nBytesToRead_ = 4; else // value == 255. nBytesToRead_ = 8; // We need to use firstOctet in the next state. firstOctet_ = firstOctet_0; state_ = TlvStructureDecoder.READ_LENGTH_BYTES; } } else if (state_ == TlvStructureDecoder.READ_LENGTH_BYTES) { int nRemainingBytes_1 = input.limit() - offset_; if (!useHeaderBuffer_ && nRemainingBytes_1 >= nBytesToRead_) { // We don't have to use the headerBuffer. Set nBytesToRead. decoder.seek(offset_); nBytesToRead_ = decoder.readExtendedVarNumber(firstOctet_); // Update offset_ to the decoder's offset after reading. offset_ = decoder.getOffset(); } else { useHeaderBuffer_ = true; int nNeededBytes = nBytesToRead_ - headerBuffer_.position(); if (nNeededBytes > nRemainingBytes_1) { // We can't get all of the header bytes from this input. // Save in headerBuffer. if (headerBuffer_.position() + nRemainingBytes_1 > headerBuffer_ .limit()) // We don't expect this to happen. throw new Exception( "Cannot store more header bytes than the size of headerBuffer"); ByteBuffer remainingInput = input.duplicate(); remainingInput.position(offset_); headerBuffer_.put(remainingInput); offset_ += nRemainingBytes_1; return false; } // Copy the remaining bytes into headerBuffer, read the // length and set nBytesToRead. if (headerBuffer_.position() + nNeededBytes > headerBuffer_ .limit()) // We don't expect this to happen. throw new Exception( "Cannot store more header bytes than the size of headerBuffer"); ByteBuffer remainingLengthBytes = input.duplicate(); remainingLengthBytes.position(offset_); remainingLengthBytes.limit(offset_ + nNeededBytes); headerBuffer_.put(remainingLengthBytes); offset_ += nNeededBytes; // Use a local decoder just for the headerBuffer. headerBuffer_.flip(); TlvDecoder bufferDecoder = new TlvDecoder(headerBuffer_); // Replace nBytesToRead with the length of the value. nBytesToRead_ = bufferDecoder .readExtendedVarNumber(firstOctet_); } if (nBytesToRead_ == 0) { // No value bytes to read. We're finished. gotElementEnd_ = true; return true; } // Get ready to read the value bytes. state_ = TlvStructureDecoder.READ_VALUE_BYTES; } else if (state_ == TlvStructureDecoder.READ_VALUE_BYTES) { int nRemainingBytes_2 = input.limit() - offset_; if (nRemainingBytes_2 < nBytesToRead_) { // Need more. offset_ += nRemainingBytes_2; nBytesToRead_ -= nRemainingBytes_2; return false; } // Got the bytes. We're finished. offset_ += nBytesToRead_; gotElementEnd_ = true; return true; } else // We don't expect this to happen. throw new Exception("findElementEnd: unrecognized state"); } }
/// <summary> /// Continue to read data until the end of an element, then call /// elementListener.onReceivedElement(element ). The buffer passed to /// onReceivedElement is only valid during this call. If you need the data /// later, you must copy. /// </summary> /// /// <param name="data"></param> /// <exception cref="EncodingException">For invalid encoding.</exception> public void onReceivedData(ByteBuffer data) { // We may repeatedly set data to a slice as we read elements. data = data.slice(); // Process multiple objects in the data. while (true) { bool gotElementEnd; int offset; try { if (!usePartialData_) { // This is the beginning of an element. if (data.remaining() <= 0) // Wait for more data. return; } // Scan the input to check if a whole TLV object has been read. tlvStructureDecoder_.seek(0); gotElementEnd = tlvStructureDecoder_.findElementEnd(data); offset = tlvStructureDecoder_.getOffset(); } catch (EncodingException ex) { // Reset to read a new element on the next call. usePartialData_ = false; tlvStructureDecoder_ = new TlvStructureDecoder(); throw ex; } if (gotElementEnd) { // Got the remainder of an element. Report to the caller. ByteBuffer element; if (usePartialData_) { // We have partial data from a previous call, so append this data and point to partialData. partialData_.ensuredPut(data, 0, offset); element = partialData_.flippedBuffer(); // Assume we don't need to use partialData anymore until needed. usePartialData_ = false; } else { // We are not using partialData, so just point to the input data buffer. element = data.duplicate(); element.limit(offset); } // Reset to read a new object. Do this before calling onReceivedElement // in case it throws an exception. data.position(offset); data = data.slice(); tlvStructureDecoder_ = new TlvStructureDecoder(); elementListener_.onReceivedElement(element); if (data.remaining() <= 0) // No more data in the packet. return; // else loop back to decode. } else { // Save remaining data for a later call. if (!usePartialData_) { usePartialData_ = true; partialData_.position(0); } if (partialData_.buffer().position() + data.remaining() > net.named_data.jndn.util.Common.MAX_NDN_PACKET_SIZE) { // Reset to read a new element on the next call. usePartialData_ = false; tlvStructureDecoder_ = new TlvStructureDecoder(); throw new EncodingException( "The incoming packet exceeds the maximum limit Face.getMaxNdnPacketSize()"); } partialData_.ensuredPut(data); return; } } }
/// <summary> /// Create a new TlvDecoder to decode the input. /// </summary> /// /// <param name="input">the underlying buffer whose contents must remain valid during the life of this object.</param> public TlvDecoder(ByteBuffer input) { input_ = input.duplicate(); }