/// <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> /// Decode the name component as NDN-TLV and return the component. This handles /// different component types such as ImplicitSha256DigestComponent. /// </summary> /// /// <param name="decoder">The decoder with the input to decode.</param> /// <param name="copy">unchanged while the Blob values are used.</param> /// <returns>A new Name.Component.</returns> /// <exception cref="EncodingException"></exception> private static Name.Component decodeNameComponent(TlvDecoder decoder, bool copy) { int savePosition = decoder.getOffset(); int type = decoder.readVarNumber(); // Restore the position. decoder.seek(savePosition); Blob value_ren = new Blob(decoder.readBlobTlv(type), copy); if (type == net.named_data.jndn.encoding.tlv.Tlv.ImplicitSha256DigestComponent) return net.named_data.jndn.Name.Component.fromImplicitSha256Digest(value_ren); else return new Name.Component(value_ren); }
/// <summary> /// Decode input as an interest in NDN-TLV and set the fields of the interest /// object. /// </summary> /// /// <param name="interest">The Interest object whose fields are updated.</param> /// <param name="input"></param> /// <param name="signedPortionBeginOffset">name component and ends just before the final name component (which is assumed to be a signature for a signed interest).</param> /// <param name="signedPortionEndOffset">name component and ends just before the final name component (which is assumed to be a signature for a signed interest).</param> /// <param name="copy">unchanged while the Blob values are used.</param> /// <exception cref="EncodingException">For invalid encoding.</exception> public override void decodeInterest(Interest interest, ByteBuffer input, int[] signedPortionBeginOffset, int[] signedPortionEndOffset, bool copy) { TlvDecoder decoder = new TlvDecoder(input); int endOffset = decoder.readNestedTlvsStart(net.named_data.jndn.encoding.tlv.Tlv.Interest); decodeName(interest.getName(), signedPortionBeginOffset, signedPortionEndOffset, decoder, copy); if (decoder.peekType(net.named_data.jndn.encoding.tlv.Tlv.Selectors, endOffset)) decodeSelectors(interest, decoder, copy); // Require a Nonce, but don't force it to be 4 bytes. ByteBuffer nonce = decoder.readBlobTlv(net.named_data.jndn.encoding.tlv.Tlv.Nonce); interest.setInterestLifetimeMilliseconds(decoder .readOptionalNonNegativeIntegerTlv(net.named_data.jndn.encoding.tlv.Tlv.InterestLifetime, endOffset)); if (decoder.peekType(net.named_data.jndn.encoding.tlv.Tlv.Data, endOffset)) { // Get the bytes of the Link TLV. int linkBeginOffset = decoder.getOffset(); int linkEndOffset = decoder.readNestedTlvsStart(net.named_data.jndn.encoding.tlv.Tlv.Data); decoder.seek(linkEndOffset); interest.setLinkWireEncoding( new Blob(decoder.getSlice(linkBeginOffset, linkEndOffset), copy), this); } else interest.unsetLink(); interest.setSelectedDelegationIndex((int) decoder .readOptionalNonNegativeIntegerTlv(net.named_data.jndn.encoding.tlv.Tlv.SelectedDelegation, endOffset)); if (interest.getSelectedDelegationIndex() >= 0 && !interest.hasLink()) throw new EncodingException( "Interest has a selected delegation, but no link object"); // Set the nonce last because setting other interest fields clears it. interest.setNonce(new Blob(nonce, copy)); decoder.finishNestedTlvs(endOffset); }
/// <summary> /// Decode input as an NDN-TLV LpPacket and set the fields of the lpPacket object. /// </summary> /// /// <param name="lpPacket">The LpPacket object whose fields are updated.</param> /// <param name="input"></param> /// <param name="copy">unchanged while the Blob values are used.</param> /// <exception cref="EncodingException">For invalid encoding.</exception> public override void decodeLpPacket(LpPacket lpPacket, ByteBuffer input, bool copy) { lpPacket.clear(); TlvDecoder decoder = new TlvDecoder(input); int endOffset = decoder.readNestedTlvsStart(net.named_data.jndn.encoding.tlv.Tlv.LpPacket_LpPacket); while (decoder.getOffset() < endOffset) { // Imitate TlvDecoder.readTypeAndLength. int fieldType = decoder.readVarNumber(); int fieldLength = decoder.readVarNumber(); int fieldEndOffset = decoder.getOffset() + fieldLength; if (fieldEndOffset > input.limit()) throw new EncodingException( "TLV length exceeds the buffer length"); if (fieldType == net.named_data.jndn.encoding.tlv.Tlv.LpPacket_Fragment) { // Set the fragment to the bytes of the TLV value. lpPacket.setFragmentWireEncoding(new Blob(decoder.getSlice( decoder.getOffset(), fieldEndOffset), copy)); decoder.seek(fieldEndOffset); // The fragment is supposed to be the last field. break; } else if (fieldType == net.named_data.jndn.encoding.tlv.Tlv.LpPacket_Nack) { NetworkNack networkNack = new NetworkNack(); int code = (int) decoder.readOptionalNonNegativeIntegerTlv( net.named_data.jndn.encoding.tlv.Tlv.LpPacket_NackReason, fieldEndOffset); // The enum numeric values are the same as this wire format, so use as is. if (code < 0 || code == net.named_data.jndn.NetworkNack.Reason.NONE.getNumericType()) // This includes an omitted NackReason. networkNack.setReason(net.named_data.jndn.NetworkNack.Reason.NONE); else if (code == net.named_data.jndn.NetworkNack.Reason.CONGESTION.getNumericType()) networkNack.setReason(net.named_data.jndn.NetworkNack.Reason.CONGESTION); else if (code == net.named_data.jndn.NetworkNack.Reason.DUPLICATE.getNumericType()) networkNack.setReason(net.named_data.jndn.NetworkNack.Reason.DUPLICATE); else if (code == net.named_data.jndn.NetworkNack.Reason.NO_ROUTE.getNumericType()) networkNack.setReason(net.named_data.jndn.NetworkNack.Reason.NO_ROUTE); else { // Unrecognized reason. networkNack.setReason(net.named_data.jndn.NetworkNack.Reason.OTHER_CODE); networkNack.setOtherReasonCode(code); } lpPacket.addHeaderField(networkNack); } else if (fieldType == net.named_data.jndn.encoding.tlv.Tlv.LpPacket_IncomingFaceId) { IncomingFaceId incomingFaceId = new IncomingFaceId(); incomingFaceId.setFaceId(decoder .readNonNegativeInteger(fieldLength)); lpPacket.addHeaderField(incomingFaceId); } else { // Unrecognized field type. The conditions for ignoring are here: // http://redmine.named-data.net/projects/nfd/wiki/NDNLPv2 bool canIgnore = (fieldType >= net.named_data.jndn.encoding.tlv.Tlv.LpPacket_IGNORE_MIN && fieldType <= net.named_data.jndn.encoding.tlv.Tlv.LpPacket_IGNORE_MAX && (fieldType & 0x01) == 1); if (!canIgnore) throw new EncodingException( "Did not get the expected TLV type"); // Ignore. decoder.seek(fieldEndOffset); } decoder.finishNestedTlvs(fieldEndOffset); } decoder.finishNestedTlvs(endOffset); }
/// <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"); } }