net.named_data.jndn.encoding.der.DerNode Maven / Gradle / Ivy
/**
* Copyright (C) 2013-2019 Regents of the University of California.
* @author: Jeff Thompson
* @author: From PyNDN der_node.py by Adeola Bannis .
* @author: Originally from code in ndn-cxx by Yingdi Yu
*
* 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.der;
import java.nio.ByteBuffer;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import net.named_data.jndn.encoding.OID;
import net.named_data.jndn.util.Blob;
import net.named_data.jndn.util.Common;
import net.named_data.jndn.util.DynamicByteBuffer;
/**
* DerNode implements the DER node types used in encoding/decoding DER-formatted
* data.
*/
public class DerNode {
/**
* Create a generic DER node with the given nodeType. This is a private
* constructor used by one of the public DerNode subclasses defined below.
* @param nodeType The DER node type, a value from DerNodeType.
*/
private DerNode(int nodeType)
{
nodeType_ = nodeType;
}
public int
getSize()
{
// payload_ is not flipped yet.
return header_.remaining() + payload_.position();
}
/**
* Encode the given size and update the header.
* @param size The payload size to encode.
*/
protected final void
encodeHeader(int size)
{
DynamicByteBuffer buffer = new DynamicByteBuffer(10);
buffer.ensuredPut((byte)nodeType_);
if (size < 0)
// We don't expect this to happen since this is a protected method and
// always called with the non-negative size() of some buffer.
throw new Error("encodeHeader: DER object has negative length");
else if (size <= 127)
buffer.ensuredPut((byte)(size & 0xff));
else {
DynamicByteBuffer tempBuf = new DynamicByteBuffer(10);
// We encode backwards from the back.
tempBuf.position(tempBuf.limit());
int val = size;
int n = 0;
while (val != 0) {
tempBuf.ensuredPutFromBack((byte)(val & 0xff));
val >>= 8;
n += 1;
}
tempBuf.ensuredPutFromBack((byte)(((1<<7) | n) & 0xff));
buffer.ensuredPut(tempBuf.buffer());
}
header_ = buffer.flippedBuffer();
}
/**
* Extract the header from an input buffer and return the size.
* @param inputBuf The input buffer to read from. This reads from
* startIdx (regardless of the buffer's position) and does not change the
* position.
* @param startIdx The offset into the buffer.
* @return The parsed size in the header.
*/
protected final int
decodeHeader(ByteBuffer inputBuf, int startIdx) throws DerDecodingException
{
int idx = startIdx;
int nodeType = ((int)inputBuf.get(idx)) & 0xff;
idx += 1;
nodeType_ = nodeType;
int sizeLen = ((int)inputBuf.get(idx)) & 0xff;
idx += 1;
DynamicByteBuffer header = new DynamicByteBuffer(10);
header.ensuredPut((byte)nodeType);
header.ensuredPut((byte)sizeLen);
int size = sizeLen;
boolean isLongFormat = (sizeLen & (1 << 7)) != 0;
if (isLongFormat) {
int lenCount = sizeLen & ((1<<7) - 1);
size = 0;
while (lenCount > 0) {
if (inputBuf.limit() <= idx)
throw new DerDecodingException
("DerNode.parse: The input length is too small");
byte b = inputBuf.get(idx);
idx += 1;
header.ensuredPut(b);
size = 256 * size + (((int)b) & 0xff);
lenCount -= 1;
}
}
header_ = header.flippedBuffer();
return size;
}
/**
* Get the raw data encoding for this node.
* @return The raw data encoding.
*/
public Blob
encode()
{
DynamicByteBuffer buffer = new DynamicByteBuffer(getSize());
buffer.ensuredPut(header_);
buffer.ensuredPut(payload_.flippedBuffer());
return new Blob(buffer.flippedBuffer(), false);
}
/**
* Decode and store the data from an input buffer.
* @param inputBuf The input buffer to read from. This reads from
* startIdx (regardless of the buffer's position) and does not change the
* position.
* @param startIdx The offset into the buffer.
*/
protected void
decode(ByteBuffer inputBuf, int startIdx) throws DerDecodingException
{
int idx = startIdx;
int payloadSize = decodeHeader(inputBuf, idx);
int skipBytes = header_.remaining();
if (payloadSize > 0) {
idx += skipBytes;
payload_.ensuredPut(inputBuf, idx, idx + payloadSize);
}
}
/**
* Parse the data from the input buffer recursively and return the root as an
* object of a subclass of DerNode.
* @param inputBuf The input buffer to read from. This reads from
* startIdx (regardless of the buffer's position) and does not change the
* position.
* @param startIdx The offset into the buffer.
* @return An object of a subclass of DerNode.
*/
public static DerNode
parse(ByteBuffer inputBuf, int startIdx) throws DerDecodingException
{
if (inputBuf.limit() <= startIdx)
throw new DerDecodingException
("DerNode.parse: The input length is too small");
int nodeType = ((int)inputBuf.get(startIdx)) & 0xff;
// Don't increment idx. We're just peeking.
DerNode newNode;
if (nodeType == DerNodeType.Boolean)
newNode = new DerBoolean();
else if (nodeType == DerNodeType.Integer)
newNode = new DerInteger();
else if (nodeType == DerNodeType.BitString)
newNode = new DerBitString();
else if (nodeType == DerNodeType.OctetString)
newNode = new DerOctetString();
else if (nodeType == DerNodeType.Null)
newNode = new DerNull();
else if (nodeType == DerNodeType.ObjectIdentifier)
newNode = new DerOid();
else if (nodeType == DerNodeType.Sequence)
newNode = new DerSequence();
else if (nodeType == DerNodeType.PrintableString)
newNode = new DerPrintableString();
else if (nodeType == DerNodeType.GeneralizedTime)
newNode = new DerGeneralizedTime();
else if ((nodeType & 0xe0) == DerNodeType.ExplicitlyTagged)
newNode = new DerExplicitlyTagged(nodeType & 0x1f);
else
throw new DerDecodingException("Unimplemented DER type " + nodeType);
newNode.decode(inputBuf, startIdx);
return newNode;
}
/**
* Parse the data from the input buffer recursively and return the root as an
* object of a subclass of DerNode.
* @param inputBuf The input buffer to read from. This reads from
* position and does not change the position.
* @return An object of a subclass of DerNode.
*/
public static DerNode
parse(ByteBuffer inputBuf) throws DerDecodingException
{
return parse(inputBuf, inputBuf.position());
}
/**
* Convert the encoded data to a standard representation. Overridden by some
* subclasses (e.g. DerBoolean).
* @return The encoded data as a Blob.
*/
public Object
toVal() throws DerDecodingException
{
return encode();
}
/**
* Get a copy of the payload bytes.
* @return A copy of the payload.
*/
public final Blob
getPayload()
{
return new Blob(payload_.flippedBuffer(), true);
}
/**
* If this object is a DerSequence, get the children of this node. Otherwise,
* throw an exception. (DerSequence overrides to implement this method.)
* @return The children as a List of DerNode.
* @throws DerDecodingException if this object is not a DerSequence.
*/
public List
getChildren() throws DerDecodingException
{
throw new DerDecodingException("getChildren: This DerNode is not DerSequence");
}
/**
* Check that index is in bounds for the children list, cast
* children.get(index) to DerSequence and return it.
* @param children The list of DerNode, usually returned by another
* call to getChildren.
* @param index The index of the children.
* @return children.get(index) cast to DerSequence.
* @throws DerDecodingException if index is out of bounds or if
* children.get(index) is not a DerSequence.
*/
public static DerSequence
getSequence(List children, int index) throws DerDecodingException
{
if (index < 0 || index >= children.size())
throw new DerDecodingException("getSequence: Child index is out of bounds");
if (!(children.get(index) instanceof DerSequence))
throw new DerDecodingException
("getSequence: Child DerNode is not DerSequence");
return (DerSequence)children.get(index);
}
/**
* A DerStructure extends DerNode to hold other DerNodes.
*/
public static class DerStructure extends DerNode {
/**
* Create a DerStructure with the given nodeType. This is a private
* constructor. To create an object, use DerSequence.
* @param nodeType The DER node type, a value from DerNodeType.
*/
private DerStructure(int nodeType)
{
super(nodeType);
}
/**
* Get the total length of the encoding, including children.
* @return The total (header + payload) length.
*/
public int
getSize()
{
if (childChanged_) {
updateSize();
childChanged_ = false;
}
encodeHeader(size_);
return size_ + header_.remaining();
}
/**
* Get the children of this node.
* @return The children as a List of DerNode.
*/
public final List
getChildren()
{
return nodeList_;
}
private void
updateSize()
{
int newSize = 0;
for (int i = 0; i < nodeList_.size(); ++i) {
DerNode n = nodeList_.get(i);
newSize += n.getSize();
}
size_ = newSize;
childChanged_ = false;
}
/**
* Add a child to this node.
* @param node The child node to add.
* @param notifyParent Set to true to cause any containing nodes to update
* their size.
*/
public final void
addChild(DerNode node, boolean notifyParent)
{
node.parent_ = this;
nodeList_.add(node);
if (notifyParent) {
if (parent_ != null)
parent_.setChildChanged();
}
childChanged_ = true;
}
public final void
addChild(DerNode node)
{
addChild(node, false);
}
/**
* Mark the child list as dirty, so that we update size when necessary.
*/
private void
setChildChanged()
{
if (parent_ != null)
parent_.setChildChanged();
childChanged_ = true;
}
/**
* Override the base encode to return raw data encoding for this node and
* its children
* @return The raw data encoding.
*/
public Blob
encode()
{
DynamicByteBuffer temp = new DynamicByteBuffer(10);
updateSize();
encodeHeader(size_);
temp.ensuredPut(header_);
for (int i = 0; i < nodeList_.size(); ++i) {
DerNode n = nodeList_.get(i);
Blob encodedChild = n.encode();
temp.ensuredPut(encodedChild.buf());
}
return new Blob(temp.flippedBuffer(), false);
}
/**
* Override the base decode to decode and store the data from an input
* buffer. Recursively populates child nodes.
* @param inputBuf The input buffer to read from. This reads from
* startIdx (regardless of the buffer's position) and does not change the
* position.
* @param startIdx The offset into the buffer.
*/
protected void
decode(ByteBuffer inputBuf, int startIdx) throws DerDecodingException
{
int idx = startIdx;
size_ = decodeHeader(inputBuf, idx);
idx += header_.remaining();
int accSize = 0;
while (accSize < size_) {
DerNode node = parse(inputBuf, idx);
int size = node.getSize();
idx += size;
accSize += size;
addChild(node, false);
}
}
private boolean childChanged_ = false;
private final ArrayList nodeList_ = new ArrayList();
private int size_ = 0;
}
////////
// Now for all the node types...
////////
/**
* A DerByteString extends DerNode to handle byte strings.
*/
public static class DerByteString extends DerNode {
/**
* Create a DerByteString with the given inputData and nodeType. This is a
* private constructor used by one of the public subclasses such as
* DerOctetString or DerPrintableString.
* @param inputData An input buffer containing the string to encode. This
* copies from the buffer's position to limit, but does not change position.
* @param nodeType The specific DER node type, a value from DerNodeType.
*/
private DerByteString(ByteBuffer inputData, int nodeType)
{
super(nodeType);
if (inputData != null) {
payload_.ensuredPut(inputData);
encodeHeader(inputData.remaining());
}
}
/**
* Override to return just the byte string.
* @return The byte string as a copy of the payload ByteBuffer.
*/
public Object
toVal() throws DerDecodingException
{
return getPayload();
}
}
/**
* DerBoolean extends DerNode to encode a boolean value.
*/
public static class DerBoolean extends DerNode {
/**
* Create a new DerBoolean for the value.
* @param value The value to encode.
*/
public DerBoolean(boolean value)
{
super(DerNodeType.Boolean);
byte val = value ? (byte)0xff : (byte)0x00;
payload_.ensuredPut(val);
encodeHeader(1);
}
private DerBoolean()
{
super(DerNodeType.Boolean);
}
public Object
toVal() throws DerDecodingException
{
byte val = payload_.buffer().get(0);
return val != 0x00;
}
}
/**
* DerInteger extends DerNode to encode an integer value.
*/
public static class DerInteger extends DerNode {
/**
* Create a new DerInteger for the value.
* @param integer The value to encode.
*/
public DerInteger(int integer) throws DerEncodingException
{
super(DerNodeType.Integer);
if (integer < 0)
throw new DerEncodingException
("DerInteger: Negative integers are not currently supported");
// Convert the integer to bytes the easy/slow way.
DynamicByteBuffer temp = new DynamicByteBuffer(10);
// We encode backwards from the back.
temp.position(temp.limit());
while (true) {
temp.ensuredPutFromBack((byte)(integer & 0xff));
integer >>= 8;
if (integer <= 0)
// We check for 0 at the end so we encode one byte if it is 0.
break;
}
if ((((int)temp.buffer().get(temp.position())) & 0xff) >= 0x80)
// Make it a non-negative integer.
temp.ensuredPutFromBack((byte)0);
payload_.ensuredPut(temp.buffer().slice());
encodeHeader(payload_.position());
}
/**
* Create a new DerInteger from the bytes in the buffer. If bytes represent
* a positive integer, you must ensure that the first byte is less than 0x80.
* @param buffer The buffer containing the bytes of the integer. This
* copies from the buffer's position to limit, but does not change position.
* @throws DerEncodingException if the first byte is not less than 0x80.
*/
public DerInteger(ByteBuffer buffer) throws DerEncodingException
{
super(DerNodeType.Integer);
if (buffer.remaining() > 0 &&
(((int)buffer.get(buffer.position())) & 0xff) >= 0x80)
throw new DerEncodingException
("DerInteger: Negative integers are not currently supported");
if (buffer.remaining() == 0)
payload_.ensuredPut((byte)0);
else
payload_.ensuredPut(buffer);
encodeHeader(payload_.position());
}
public DerInteger()
{
super(DerNodeType.Integer);
}
public Object
toVal() throws DerDecodingException
{
if (payload_.buffer().position() > 0 &&
(((int)payload_.buffer().get(0)) & 0xff) >= 0x80)
throw new DerDecodingException
("DerInteger: Negative integers are not currently supported");
int result = 0;
// payload_ is not flipped yet.
for (int i = 0; i < payload_.buffer().position(); ++i) {
result <<= 8;
// Use & 0xff in case byte was in the range -128 to -1.
result += ((int)payload_.buffer().get(i)) & 0xff;
}
return result;
}
}
/**
* A DerBitString extends DerNode to handle a bit string.
*/
public static class DerBitString extends DerNode {
/**
* Create a DerBitString with the given padding and inputBuf.
* @param inputBuf An input buffer containing the bit octets to encode. This
* copies from the buffer's position to limit, but does not change position.
* @param paddingLen The number of bits of padding at the end of the bit
* string. Should be less than 8.
*/
public DerBitString(ByteBuffer inputBuf, int paddingLen)
{
super(DerNodeType.BitString);
if (inputBuf != null) {
payload_.ensuredPut((byte)(paddingLen & 0xff));
payload_.ensuredPut(inputBuf);
encodeHeader(payload_.position());
}
}
private DerBitString()
{
super(DerNodeType.BitString);
}
}
/**
* DerOctetString extends DerByteString to encode a string of bytes.
*/
public static class DerOctetString extends DerByteString {
/**
* Create a new DerOctetString for the inputData.
* @param inputData An input buffer containing the string to encode. This
* copies from the buffer's position to limit, but does not change position.
*/
public DerOctetString(ByteBuffer inputData)
{
super(inputData, DerNodeType.OctetString);
}
private DerOctetString()
{
super(null, DerNodeType.OctetString);
}
}
/**
* A DerNull extends DerNode to encode a null value.
*/
public static class DerNull extends DerNode {
/**
* Create a DerNull.
*/
public DerNull()
{
super(DerNodeType.Null);
encodeHeader(0);
}
}
/**
* A DerOid extends DerNode to represent an object identifier
*/
public static class DerOid extends DerNode {
/**
* Create a DerOid with the given object identifier. The object identifier
* string must begin with 0,1, or 2 and must contain at least 2 digits.
* @param oidStr The OID string to encode.
*/
public DerOid(String oidStr) throws DerEncodingException
{
super(DerNodeType.ObjectIdentifier);
String[] splitString = oidStr.split("\\.");
int[] parts = new int[splitString.length];
for (int i = 0; i < parts.length; ++i)
parts[i] = Integer.parseInt(splitString[i]);
prepareEncoding(parts);
}
/**
* Create a DerOid with the given object identifier. The object identifier
* must begin with 0,1, or 2 and must contain at least 2 digits.
* @param oid The OID to encode.
*/
public DerOid(OID oid) throws DerEncodingException
{
super(DerNodeType.ObjectIdentifier);
prepareEncoding(oid.getIntegerList());
}
private DerOid()
{
super(DerNodeType.ObjectIdentifier);
}
/**
* Encode a sequence of integers into an OID object and set the payload.
* @param value The array of integers.
*/
private void
prepareEncoding(int[] value) throws DerEncodingException
{
int firstNumber = 0;
if (value.length == 0)
throw new DerEncodingException("No integer in OID");
else {
if (value[0] >= 0 && value[0] <= 2)
firstNumber = value[0] * 40;
else
throw new DerEncodingException("First integer in OID is out of range");
}
if (value.length >= 2) {
if (value[1] >= 0 && value[1] <= 39)
firstNumber += value[1];
else
throw new DerEncodingException("Second integer in OID is out of range");
}
DynamicByteBuffer encodedBuffer = new DynamicByteBuffer(10);
encodedBuffer.ensuredPut(encode128(firstNumber));
if (value.length > 2) {
for (int i = 2; i < value.length; ++i)
encodedBuffer.ensuredPut(encode128(value[i]));
}
encodeHeader(encodedBuffer.position());
payload_.ensuredPut(encodedBuffer.flippedBuffer());
}
/**
* Compute the encoding for one part of an OID, where values greater than 128 must be encoded as multiple bytes.
* @param value A component of an OID.
* @return The encoded buffer.
*/
private static ByteBuffer
encode128(int value)
{
int mask = (1 << 7) - 1;
DynamicByteBuffer outBytes = new DynamicByteBuffer(10);
// We encode backwards from the back.
outBytes.position(outBytes.limit());
if (value < 128)
outBytes.ensuredPutFromBack((byte)(value & mask));
else {
outBytes.ensuredPutFromBack((byte)(value & mask));
value >>= 7;
while (value != 0) {
outBytes.ensuredPutFromBack((byte)((value & mask) | (1 << 7)));
value >>= 7;
}
}
return outBytes.buffer().slice();
}
/**
* Convert an encoded component of the encoded OID to the original integer.
* @param offset The offset into this node's payload.
* @param skip Set skip[0] to the number of payload bytes to skip.
* @return The original integer.
*/
private int
decode128(int offset, int[] skip)
{
int flagMask = 0x80;
int result = 0;
int oldOffset = offset;
while ((payload_.buffer().get(offset) & flagMask) != 0) {
result = 128 * result + ((int)payload_.buffer().get(offset) & 0xff) - 128;
offset += 1;
}
result = result * 128 + ((int)payload_.buffer().get(offset) & 0xff);
skip[0] = offset - oldOffset + 1;
return result;
}
/**
* Override to return the string representation of the OID.
* @return The string representation of the OID.
*/
public Object
toVal() throws DerDecodingException
{
int offset = 0;
ArrayList components = new ArrayList(); // of Integer.
while (offset < payload_.position()) {
int[] skip = new int[1];
int nextVal = decode128(offset, skip);
offset += skip[0];
components.add(nextVal);
}
// for some odd reason, the first digits are represented in one byte
int firstByte = (Integer)components.get(0);
int firstDigit = firstByte / 40;
int secondDigit = firstByte % 40;
String result = firstDigit + "." + secondDigit;
for (int i = 1; i < components.size(); ++i)
result += "." + (Integer)components.get(i);
return result;
}
}
/**
* A DerSequence extends DerStructure to contains an ordered sequence of other
* nodes.
*/
public static class DerSequence extends DerStructure {
/**
* Create a DerSequence.
*/
public DerSequence()
{
super(DerNodeType.Sequence);
}
}
/**
* A DerPrintableString extends DerByteString to handle a a printable string. No
* escaping or other modification is done to the string
*/
public static class DerPrintableString extends DerByteString {
/**
* Create a DerPrintableString with the given inputData.
* @param inputData An input buffer containing the string to encode. This
* copies from the buffer's position to limit, but does not change position.
*/
public DerPrintableString(ByteBuffer inputData)
{
super(inputData, DerNodeType.PrintableString);
}
private DerPrintableString()
{
super(null, DerNodeType.PrintableString);
}
}
/**
* A DerGeneralizedTime extends DerNode to represent a date and time, with
* millisecond accuracy.
*/
public static class DerGeneralizedTime extends DerNode {
/**
* Create a DerGeneralizedTime with the given milliseconds since 1970.
* @param msSince1970 The timestamp as milliseconds since Jan 1, 1970.
*/
public DerGeneralizedTime(double msSince1970)
{
super(DerNodeType.GeneralizedTime);
String derTime = toDerTimeString(msSince1970);
// Use Blob to convert to a ByteBuffer.
payload_.ensuredPut(new Blob(derTime).buf());
encodeHeader(payload_.position());
}
private DerGeneralizedTime()
{
super(DerNodeType.GeneralizedTime);
}
/**
* Convert a UNIX timestamp to the internal string representation.
* @param msSince1970 Timestamp as milliseconds since Jan 1, 1970.
* @return The string representation.
*/
private static String
toDerTimeString(double msSince1970)
{
Date utcTime = Common.millisecondsSince1970ToDate
((long)Math.round(msSince1970));
return dateFormat_.format(utcTime);
}
/**
* Compute the date format for storing in the static variable dateFormat_.
*/
private static SimpleDateFormat
getDateFormat()
{
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
return dateFormat;
}
/**
* Override to return the milliseconds since 1970.
* @return The timestamp value as milliseconds since 1970 as a Double value.
*/
public Object
toVal() throws DerDecodingException
{
// Use Blob to convert to a string.
String timeStr = "" + new Blob(payload_.flippedBuffer(), false);
try {
Date date = dateFormat_.parse(timeStr);
return (double)Common.dateToMillisecondsSince1970(date);
} catch (ParseException ex) {
throw new DerDecodingException
("DerGeneralizedTime: Error decoding the date string: " + ex);
}
}
private static final SimpleDateFormat dateFormat_ = getDateFormat();
}
/**
* A DerExplicitlyTagged extends DerNode to represent an explicitly-tagged
* type which wraps another DerNode.
*/
public static class DerExplicitlyTagged extends DerNode {
/**
* Create a DerExplicitlyTagged with the given tag number.
* @param tagNumber The explicit tag number from 0x00 to 0x1f.
*/
public DerExplicitlyTagged(int tagNumber)
{
super(DerNodeType.ExplicitlyTagged);
tagNumber_ = tagNumber;
}
/**
* Override the base encode to return raw data encoding for the explicit tag
* and encoded inner node.
* @return The raw data encoding.
*/
public Blob
encode()
{
throw new UnsupportedOperationException
("DerExplicitlyTagged.encode is not implemented");
}
/**
* Override the base decode to decode and store the inner DerNode.
* @param inputBuf The input buffer to read from. This reads from
* startIdx (regardless of the buffer's position) and does not change the
* position.
* @param startIdx The offset into the buffer.
*/
protected void
decode(ByteBuffer inputBuf, int startIdx) throws DerDecodingException
{
super.decode(inputBuf, startIdx);
innerNode_ = parse(getPayload().buf());
}
/**
* Get the tag number.
* @return The tag number.
*/
public final int
getTagNumber() { return tagNumber_; }
/**
* Get the inner node that is wrapped by the explicit tag.
* @return The inner node, or null if node specified.
*/
public final DerNode
getInnerNode() { return innerNode_; }
private final int tagNumber_;
private DerNode innerNode_ = null;
}
protected DerStructure parent_ = null;
// A value from DerNodeType.
private int nodeType_;
protected ByteBuffer header_ = ByteBuffer.allocate(0);
// NOTE: We never "flip" the internal buffer. Its data is from 0 to position().
protected DynamicByteBuffer payload_ = new DynamicByteBuffer(0);
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy