
net.named_data.jndn.encoding.tlv.TlvDecoder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jndn-android-with-async-io Show documentation
Show all versions of jndn-android-with-async-io Show documentation
jNDN is a new implementation of a Named Data Networking client library written in Java. It is wire format compatible with the new NDN-TLV encoding, with NDNx and PARC's CCNx.
/**
* Copyright (C) 2014-2017 Regents of the University of California.
* @author: Jeff Thompson
*
* 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 .
* A copy of the GNU Lesser General Public License is in the file COPYING.
*/
package net.named_data.jndn.encoding.tlv;
import java.nio.ByteBuffer;
import java.nio.BufferUnderflowException;
import net.named_data.jndn.encoding.EncodingException;
import net.named_data.jndn.util.Common;
/**
* A TlvDecoder has methods to decode an input according to NDN-TLV.
*/
public class TlvDecoder {
/**
* Create a new TlvDecoder 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
TlvDecoder(ByteBuffer input)
{
input_ = input.duplicate();
}
/**
* Decode a VAR-NUMBER in NDN-TLV and return it. Update the input buffer
* position.
* @return The decoded VAR-NUMBER as a Java 32-bit int.
* @throws EncodingException if the VAR-NUMBER is 64-bit or read past the end
* of the input.
*/
public final int
readVarNumber() throws EncodingException
{
try {
int firstOctet = (int)input_.get() & 0xff;
if (firstOctet < 253)
return firstOctet;
else
return readExtendedVarNumber(firstOctet);
} catch (BufferUnderflowException ex) {
throw new EncodingException("Read past the end of the input");
}
}
/**
* Do the work of readVarNumber, given the firstOctet which is greater than
* or equal to 253. Update the input buffer position.
* @param firstOctet The first octet which is greater than or equal to 253,
* used to decode the remaining bytes.
* @return The decoded VAR-NUMBER as a Java 32-bit int.
* @throws EncodingException if the VAR-NUMBER is 64-bit or read past the end
* of the input.
*/
public final int
readExtendedVarNumber(int firstOctet) throws EncodingException
{
try {
if (firstOctet == 253)
return (((int)input_.get() & 0xff) << 8) +
((int)input_.get() & 0xff);
else if (firstOctet == 254)
return (((int)input_.get() & 0xff) << 24) +
(((int)input_.get() & 0xff) << 16) +
(((int)input_.get() & 0xff) << 8) +
((int)input_.get() & 0xff);
else
// we are returning a 32-bit int, so can't handle 64-bit.
throw new EncodingException
("Decoding a 64-bit VAR-NUMBER is not supported");
} catch (BufferUnderflowException ex) {
throw new EncodingException("Read past the end of the input");
}
}
/**
* Decode the type and length from this's input starting at the input buffer
* position, expecting the type to be expectedType and return the length.
* Update the input buffer position. Also make sure the decoded length does
* not exceed the number of bytes remaining in the input.
* @param expectedType The expected type as a 32-bit Java int.
* @return The length of the TLV as a 32-bit Java int.
* @throws EncodingException if did not get the expected TLV type, or the TLV
* length exceeds the buffer length, or the type is encoded as a 64-bit value,
* or the length is encoded as a 64-bit value.
*/
public final int
readTypeAndLength(int expectedType) throws EncodingException
{
int type = readVarNumber();
if (type != expectedType)
throw new EncodingException("Did not get the expected TLV type");
int length = readVarNumber();
if (length > input_.remaining())
throw new EncodingException("TLV length exceeds the buffer length");
return length;
}
/**
* Decode the type and length from the input starting at the input buffer
* position, expecting the type to be expectedType. Update the input buffer
* position. Also make sure the decoded length does not exceed the number of
* bytes remaining in the input. Return the input buffer position (offset) of
* the end of this parent TLV, which is used in decoding optional nested TLVs.
* After reading all nested TLVs, you should call finishNestedTlvs.
* @param expectedType The expected type as a 32-bit Java int.
* @return The input buffer position (offset) of the end of the parent TLV.
* @throws EncodingException if did not get the expected TLV type, or the TLV
* length exceeds the buffer length, or the type is encoded as a 64-bit value,
* or the length is encoded as a 64-bit value.
*/
public final int
readNestedTlvsStart(int expectedType) throws EncodingException
{
return readTypeAndLength(expectedType) + input_.position();
}
/**
* Call this after reading all nested TLVs to skip any remaining unrecognized
* TLVs and to check if the input buffer position after the final nested TLV
* matches the endOffset returned by readNestedTlvsStart. Update the input
* buffer position as needed if skipping TLVs.
* @param endOffset The offset of the end of the parent TLV, returned
* by readNestedTlvsStart.
* @throws EncodingException if the TLV length does not equal the total length
* of the nested TLVs.
*/
public final void
finishNestedTlvs(int endOffset) throws EncodingException
{
// We expect the position to be endOffset, so check this first.
if (input_.position() == endOffset)
return;
// Skip remaining TLVs.
while (input_.position() < endOffset) {
// Skip the type VAR-NUMBER.
readVarNumber();
// Read the length and update the position.
int length = readVarNumber();
int newPosition = input_.position() + length;
// Check newPosition before updating input_position since it would
// throw its own exception.
if (newPosition > input_.limit())
throw new EncodingException("TLV length exceeds the buffer length");
input_.position(newPosition);
}
if (input_.position() != endOffset)
throw new EncodingException
("TLV length does not equal the total length of the nested TLVs");
}
/**
* Decode the type from the input starting at the input buffer position, and
* if it is the expectedType, then return true, else false. However, if the
* input buffer position is greater than or equal to endOffset, then return
* false and don't try to read the type. Do not update the input buffer
* position.
* @param expectedType The expected type as a 32-bit Java int.
* @param endOffset The offset of the end of the parent TLV, returned
* by readNestedTlvsStart.
* @return true if the type of the next TLV is the expectedType, otherwise
* false.
*/
public final boolean
peekType(int expectedType, int endOffset) throws EncodingException
{
if (input_.position() >= endOffset)
// No more sub TLVs to look at.
return false;
else {
int savePosition = input_.position();
int type = readVarNumber();
// Restore the position.
input_.position(savePosition);
return type == expectedType;
}
}
/**
* Decode a non-negative integer in NDN-TLV and return it. Update the input
* buffer position by length.
* @param length The number of bytes in the encoded integer.
* @return The integer as a Java 64-bit long.
* @throws EncodingException if length is an invalid length for a TLV
* non-negative integer or read past the end of the input.
*/
public final long
readNonNegativeInteger(int length) throws EncodingException
{
try {
if (length == 1)
return (long)input_.get() & 0xff;
else if (length == 2)
return (((long)input_.get() & 0xff) << 8) +
((long)input_.get() & 0xff);
else if (length == 4)
return (((long)input_.get() & 0xff) << 24) +
(((long)input_.get() & 0xff) << 16) +
(((long)input_.get() & 0xff) << 8) +
((long)input_.get() & 0xff);
else if (length == 8)
return (((long)input_.get() & 0xff) << 56) +
(((long)input_.get() & 0xff) << 48) +
(((long)input_.get() & 0xff) << 40) +
(((long)input_.get() & 0xff) << 32) +
(((long)input_.get() & 0xff) << 24) +
(((long)input_.get() & 0xff) << 16) +
(((long)input_.get() & 0xff) << 8) +
((long)input_.get() & 0xff);
else
throw new EncodingException("Invalid length for a TLV nonNegativeInteger");
} catch (BufferUnderflowException ex) {
throw new EncodingException("Read past the end of the input");
}
}
/**
* Decode the type and length from the input starting at the input buffer
* position, expecting the type to be expectedType. Then decode a non-negative
* integer in NDN-TLV and return it. Update the input buffer position.
* @param expectedType The expected type as a 32-bit Java int.
* @return The integer as a Java 64-bit long.
* @throws EncodingException if did not get the expected TLV type or can't
* decode the value.
*/
public final long
readNonNegativeIntegerTlv(int expectedType) throws EncodingException
{
int length = readTypeAndLength(expectedType);
return readNonNegativeInteger(length);
}
/**
* Peek at the next TLV, and if it has the expectedType then call
* readNonNegativeIntegerTlv and return the integer. Otherwise, return -1.
* However, if the input buffer position is greater than or equal to
* endOffset, then return -1 and don't try to read the type.
* @param expectedType The expected type as a 32-bit Java int.
* @param endOffset The offset of the end of the parent TLV, returned
* by readNestedTlvsStart.
* @return The integer as a Java 64-bit long or -1 if the next TLV doesn't
* have the expected type.
*/
public final long
readOptionalNonNegativeIntegerTlv
(int expectedType, int endOffset) throws EncodingException
{
if (peekType(expectedType, endOffset))
return readNonNegativeIntegerTlv(expectedType);
else
return -1;
}
/**
* Decode the type and length from the input starting at the input buffer
* position, expecting the type to be expectedType. Then return a ByteBuffer
* of the bytes in the value. Update the input buffer position.
* @param expectedType The expected type as a 32-bit Java int.
* @return The bytes in the value as a slice on the input buffer. This is
* not a copy of the bytes in the input buffer. If you need a copy, then you
* must make a copy of the return value.
* @throws EncodingException if did not get the expected TLV type.
*/
public final ByteBuffer
readBlobTlv(int expectedType) throws EncodingException
{
int length = readTypeAndLength(expectedType);
int saveLimit = input_.limit();
input_.limit(input_.position() + length);
ByteBuffer result = input_.slice();
// Restore the limit.
input_.limit(saveLimit);
// readTypeAndLength already checked if length exceeds the input buffer.
input_.position(input_.position() + length);
return result;
}
/**
* Peek at the next TLV, and if it has the expectedType then call readBlobTlv
* and return the value. Otherwise, return null. However, if the input buffer
* position is greater than or equal to endOffset, then return null and don't
* try to read the type.
* @param expectedType The expected type as a 32-bit Java int.
* @param endOffset The offset of the end of the parent TLV, returned
* by readNestedTlvsStart.
* @return The bytes in the value as a slice on the input buffer or null if
* the next TLV doesn't have the expected type. This is not a copy of the
* bytes in the input buffer. If you need a copy, then you must make a copy of
* the return value.
*/
public final ByteBuffer
readOptionalBlobTlv(int expectedType, int endOffset) throws EncodingException
{
if (peekType(expectedType, endOffset))
return readBlobTlv(expectedType);
else
return null;
}
/**
* Peek at the next TLV, and if it has the expectedType then read a type and
* value, ignoring the value, and return true. Otherwise, return false.
* However, if the input buffer position is greater than or equal to
* endOffset, then return false and don't try to read the type and value.
* @param expectedType The expected type as a 32-bit Java int.
* @param endOffset The offset of the end of the parent TLV, returned
* by readNestedTlvsStart.
* @return true, or else false if the next TLV doesn't have the
* expected type.
*/
public final boolean
readBooleanTlv(int expectedType, int endOffset) throws EncodingException
{
if (peekType(expectedType, endOffset)) {
int length = readTypeAndLength(expectedType);
// We expect the length to be 0, but update offset anyway.
input_.position(input_.position() + length);
return true;
}
else
return false;
}
/**
* Get the input buffer position (offset), used for the next read.
* @return The input buffer position (offset).
*/
public final int
getOffset()
{
return input_.position();
}
/**
* Set the offset into the input, used for the next read.
* @param offset The new offset.
*/
public final void
seek(int offset)
{
input_.position(offset);
}
/**
* Return a ByteBuffer slice of the input for the given offset range.
* @param beginOffset The offset in the input of the beginning of the slice.
* @param endOffset The offset in the input of the end of the slice.
* @return A slice on the input buffer. This is not a copy of the bytes in
* the input buffer. If you need a copy, then you must make a copy of the
* return value.
*/
public final ByteBuffer
getSlice(int beginOffset, int endOffset)
{
ByteBuffer result = input_.duplicate();
// First set position to 0 to be sure that endOffset won't be before it.
result.position(0);
result.limit(endOffset);
result.position(beginOffset);
return result;
}
private final ByteBuffer input_;
// This is to force an import of net.named_data.jndn.util.
private static Common dummyCommon_ = new Common();
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy