
net.named_data.jndn.encoding.ProtobufTlv 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;
import com.google.protobuf.ByteString;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor.Type;
import com.google.protobuf.Message;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.List;
import net.named_data.jndn.Name;
import net.named_data.jndn.encoding.tlv.TlvDecoder;
import net.named_data.jndn.encoding.tlv.TlvEncoder;
import net.named_data.jndn.util.Blob;
/**
* ProtobufTlv has static methods to encode and decode an Protobuf Message
* object as NDN-TLV. The Protobuf tag value is used as the TLV type code. A
* Protobuf message is encoded/decoded as a nested TLV encoding. Protobuf types
* uint32, uint64 and enum are encoded/decoded as TLV nonNegativeInteger. (It is
* an error if an enum value is negative.) Protobuf types bytes and string are
* encoded/decoded as TLV bytes. The Protobuf type bool is encoded/decoded as a
* TLV boolean (a zero length value for True, omitted for False). Other Protobuf
* types are an error.
*
* Protobuf has no "outer" message type, so you need to put your TLV message
* inside an outer "typeless" message.
*/
public class ProtobufTlv {
/**
* Encode the Protobuf message object as NDN-TLV.
* @param message The Protobuf message object. This calls
* message.IsInitialized() to ensure that all required fields are present and
* throws an exception if not.
* @return The encoded buffer in a Blob object.
*/
public static Blob
encode(Message message)
{
if (!message.isInitialized())
throw new Error
("Message fields are not initialized: " + message.getInitializationErrorString());
TlvEncoder encoder = new TlvEncoder(256);
encodeMessageValue(message, encoder);
return new Blob(encoder.getOutput(), false);
}
/**
* Decode the input as NDN-TLV and update the fields of the Protobuf message
* object.
* @param message The Protobuf Message builder object. This does not first
* clear the object.
* @param input The input buffer to decode. This reads from position() to
* limit(), but does not change the position.
* @throws EncodingException For invalid encoding.
*/
public static void
decode(Message.Builder message, ByteBuffer input) throws EncodingException
{
TlvDecoder decoder = new TlvDecoder(input);
decodeMessageValue(message, decoder, input.remaining());
}
public static void
decode(Message.Builder message, Blob input) throws EncodingException
{
decode(message, input.buf());
}
/**
* Return a Name made from the component array in a Protobuf message object,
* assuming that it was defined with "repeated bytes". For example:
* message Name {
* repeated bytes component = 8;
* }
* (See the TestEncodeDecodeFibEntry example.)
* @param nameMessage The Protobuf message object containing the "repeated
* bytes" component array.
* @return A new name.
*/
public static Name
toName(Message nameMessage)
{
Name name = new Name();
Descriptor descriptor = nameMessage.getDescriptorForType();
FieldDescriptor field = (FieldDescriptor)descriptor.getFields().get(0);
for (int i = 0; i < nameMessage.getRepeatedFieldCount(field); ++i)
name.append(new Blob
(((ByteString)nameMessage.getRepeatedField(field, i)).asReadOnlyByteBuffer(),
true));
return name;
}
private static void
encodeMessageValue(Message message, TlvEncoder encoder)
{
// Note: We can't use ListFields because it sorts by field number.
Descriptor descriptor = message.getDescriptorForType();
// Go in reverse so that we encode backwards.
List fields = descriptor.getFields();
for (int i = fields.size() - 1; i >= 0; --i) {
FieldDescriptor field = (FieldDescriptor)fields.get(i);
int tlvType = field.getNumber();
int valueCount = 0;
if (field.isRepeated())
valueCount = message.getRepeatedFieldCount(field);
else {
if (message.hasField(field))
valueCount = 1;
}
// Reverse so that we encode backwards.
for (int iValue = valueCount - 1; iValue >= 0; --iValue) {
Object value;
if (field.isRepeated())
value = message.getRepeatedField(field, iValue);
else
value = message.getField(field);
if (field.getType() == Type.MESSAGE) {
int saveLength = encoder.getLength();
// Encode backwards.
encodeMessageValue((Message)value, encoder);
encoder.writeTypeAndLength(tlvType, encoder.getLength() - saveLength);
}
else if (field.getType() == Type.UINT32)
encoder.writeNonNegativeIntegerTlv(tlvType, (Integer)value);
else if (field.getType() == Type.UINT64)
encoder.writeNonNegativeIntegerTlv(tlvType, (Long)value);
else if (field.getType() == Type.ENUM) {
int intValue = ((EnumValueDescriptor)value).getNumber();
if (intValue < 0)
throw new Error("ProtobufTlv.encode: ENUM value may not be negative");
encoder.writeNonNegativeIntegerTlv(tlvType, intValue);
}
else if (field.getType() == Type.BYTES)
encoder.writeBlobTlv(tlvType, ((ByteString)value).asReadOnlyByteBuffer());
else if (field.getType() == Type.STRING)
// Use Blob to UTF-8 encode and get a ByteBuffer.
encoder.writeBlobTlv(tlvType, new Blob((String)value).buf());
else if (field.getType() == Type.BOOL) {
if ((boolean)(Boolean)value)
encoder.writeTypeAndLength(tlvType, 0);
}
else
throw new Error("ProtobufTlv.encode: Unknown field type");
}
}
}
private static void
decodeMessageValue(Message.Builder message, TlvDecoder decoder, int endOffset)
throws EncodingException
{
Descriptor descriptor = message.getDescriptorForType();
for (FieldDescriptor field : descriptor.getFields()) {
int tlvType = field.getNumber();
if (field.isOptional() && !decoder.peekType(tlvType, endOffset))
continue;
if (field.isRepeated()) {
while (decoder.peekType(tlvType, endOffset)) {
if (field.getType() == Type.MESSAGE) {
Message.Builder innerMessage = message.newBuilderForField(field);
int innerEndOffset = decoder.readNestedTlvsStart(tlvType);
decodeMessageValue(innerMessage, decoder, innerEndOffset);
decoder.finishNestedTlvs(innerEndOffset);
message.addRepeatedField(field, innerMessage.build());
}
else
message.addRepeatedField
(field, decodeFieldValue(field, tlvType, decoder, endOffset));
}
}
else {
if (field.getType() == Type.MESSAGE) {
Message.Builder innerMessage = message.newBuilderForField(field);
int innerEndOffset = decoder.readNestedTlvsStart(tlvType);
decodeMessageValue(innerMessage, decoder, innerEndOffset);
decoder.finishNestedTlvs(innerEndOffset);
message.setField(field, innerMessage.build());
}
else
message.setField
(field, decodeFieldValue(field, tlvType, decoder, endOffset));
}
}
}
/**
* This is a helper for decodeMessageValue. Decode a single field and return
* the value. Assume the field type is not Type.MESSAGE.
*/
private static Object
decodeFieldValue
(FieldDescriptor field, int tlvType, TlvDecoder decoder, int endOffset)
throws EncodingException
{
if (field.getType() == Type.UINT32)
return (int)decoder.readNonNegativeIntegerTlv(tlvType);
else if (field.getType() == Type.UINT64)
return decoder.readNonNegativeIntegerTlv(tlvType);
else if (field.getType() == Type.ENUM)
return field.getEnumType().findValueByNumber
((int)decoder.readNonNegativeIntegerTlv(tlvType));
else if (field.getType() == Type.BYTES)
return ByteString.copyFrom(decoder.readBlobTlv(tlvType));
else if (field.getType() == Type.STRING) {
try {
ByteBuffer byteBuffer = decoder.readBlobTlv(tlvType);
// Use Blob to get the byte array.
return new String(new Blob(byteBuffer, false).getImmutableArray(), "UTF-8");
} catch (UnsupportedEncodingException ex) {
// We don't expect this to happen.
throw new Error("UTF-8 decoder not supported: " + ex.getMessage());
}
}
else if (field.getType() == Type.BOOL)
return decoder.readBooleanTlv(tlvType, endOffset);
else
throw new Error("ProtobufTlv.decode: Unknown field type");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy