/**
* Copyright (C) 2014-2018 Regents of the University of California.
* @author: Jeff Thompson <jefft0@remap.ucla.edu>
*
* 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 <http://www.gnu.org/licenses/>.
* A copy of the GNU Lesser General Public License is in the file COPYING.
*/
/** @ignore */
var DynamicBuffer = require('../../util/dynamic-buffer.js').DynamicBuffer; /** @ignore */
var Tlv = require('./tlv.js').Tlv;
/**
* Create a new TlvEncoder with an initialCapacity for the encoding buffer.
* @constructor
* @param {number} initialCapacity (optional) The initial capacity of the
* encoding buffer. If omitted, use a default value.
*/
var TlvEncoder = function TlvEncoder(initialCapacity)
{
initialCapacity = initialCapacity || 16;
this.output = new DynamicBuffer(initialCapacity);
// length is the number of bytes that have been written to the back of
// this.output.array.
this.length = 0;
};
exports.TlvEncoder = TlvEncoder;
/**
* Get the number of bytes that have been written to the output. You can
* save this number, write sub TLVs, then subtract the new length from this
* to get the total length of the sub TLVs.
* @return {number} The number of bytes that have been written to the output.
*/
TlvEncoder.prototype.getLength = function()
{
return this.length;
};
/**
* Encode varNumber as a VAR-NUMBER in NDN-TLV and write it to this.output just
* before this.length from the back. Advance this.length.
* @param {number} varNumber The non-negative number to encode.
*/
TlvEncoder.prototype.writeVarNumber = function(varNumber)
{
if (varNumber < 253) {
this.length += 1;
this.output.ensureLengthFromBack(this.length);
this.output.array[this.output.array.length - this.length] = varNumber & 0xff;
}
else if (varNumber <= 0xffff) {
this.length += 3;
this.output.ensureLengthFromBack(this.length);
var offset = this.output.array.length - this.length;
this.output.array[offset] = 253;
this.output.array[offset + 1] = (varNumber >> 8) & 0xff;
this.output.array[offset + 2] = varNumber & 0xff;
}
else if (varNumber <= 0xffffffff) {
this.length += 5;
this.output.ensureLengthFromBack(this.length);
var offset = this.output.array.length - this.length;
this.output.array[offset] = 254;
this.output.array[offset + 1] = (varNumber >> 24) & 0xff;
this.output.array[offset + 2] = (varNumber >> 16) & 0xff;
this.output.array[offset + 3] = (varNumber >> 8) & 0xff;
this.output.array[offset + 4] = varNumber & 0xff;
}
else {
this.length += 9;
this.output.ensureLengthFromBack(this.length);
var offset = this.output.array.length - this.length;
this.output.array[offset] = 255;
var highBytes = Tlv.getHighBytes(varNumber);
this.output.array[offset + 1] = (highBytes >> 24) & 0xff;
this.output.array[offset + 2] = (highBytes >> 16) & 0xff;
this.output.array[offset + 3] = (highBytes >> 8) & 0xff;
this.output.array[offset + 4] = (highBytes) & 0xff;
this.output.array[offset + 5] = (varNumber >> 24) & 0xff;
this.output.array[offset + 6] = (varNumber >> 16) & 0xff;
this.output.array[offset + 7] = (varNumber >> 8) & 0xff;
this.output.array[offset + 8] = varNumber & 0xff;
}
};
/**
* Encode the type and length as VAR-NUMBER and write to this.output just before
* this.length from the back. Advance this.length.
* @param {number} type The type of the TLV.
* @param {number} length The non-negative length of the TLV.
*/
TlvEncoder.prototype.writeTypeAndLength = function(type, length)
{
// Write backwards.
this.writeVarNumber(length);
this.writeVarNumber(type);
};
/**
* Write value as a non-negative integer and write it to this.output just before
* this.length from the back. Advance this.length.
* @param {number} value The non-negative integer to encode.
*/
TlvEncoder.prototype.writeNonNegativeInteger = function(value)
{
if (value < 0)
throw new Error("TLV integer value may not be negative");
// JavaScript doesn't distinguish int from float, so round.
value = Math.round(value);
if (value <= 0xff) {
this.length += 1;
this.output.ensureLengthFromBack(this.length);
this.output.array[this.output.array.length - this.length] = value & 0xff;
}
else if (value <= 0xffff) {
this.length += 2;
this.output.ensureLengthFromBack(this.length);
var offset = this.output.array.length - this.length;
this.output.array[offset] = (value >> 8) & 0xff;
this.output.array[offset + 1] = value & 0xff;
}
else if (value <= 0xffffffff) {
this.length += 4;
this.output.ensureLengthFromBack(this.length);
var offset = this.output.array.length - this.length;
this.output.array[offset] = (value >> 24) & 0xff;
this.output.array[offset + 1] = (value >> 16) & 0xff;
this.output.array[offset + 2] = (value >> 8) & 0xff;
this.output.array[offset + 3] = value & 0xff;
}
else {
this.length += 8;
this.output.ensureLengthFromBack(this.length);
var offset = this.output.array.length - this.length;
var highBytes = Tlv.getHighBytes(value);
this.output.array[offset] = (highBytes >> 24) & 0xff;
this.output.array[offset + 1] = (highBytes >> 16) & 0xff;
this.output.array[offset + 2] = (highBytes >> 8) & 0xff;
this.output.array[offset + 3] = (highBytes) & 0xff;
this.output.array[offset + 4] = (value >> 24) & 0xff;
this.output.array[offset + 5] = (value >> 16) & 0xff;
this.output.array[offset + 6] = (value >> 8) & 0xff;
this.output.array[offset + 7] = value & 0xff;
}
};
/**
* Write the type, then the length of the encoded value then encode value as a
* non-negative integer and write it to this.output just before this.length from
* the back. Advance this.length.
* @param {number} type The type of the TLV.
* @param {number} value The non-negative integer to encode.
*/
TlvEncoder.prototype.writeNonNegativeIntegerTlv = function(type, value)
{
// Write backwards.
var saveNBytes = this.length;
this.writeNonNegativeInteger(value);
this.writeTypeAndLength(type, this.length - saveNBytes);
};
/**
* If value is negative or null then do nothing, otherwise call
* writeNonNegativeIntegerTlv.
* @param {number} type The type of the TLV.
* @param {number} value If negative or null do nothing, otherwise the integer
* to encode.
*/
TlvEncoder.prototype.writeOptionalNonNegativeIntegerTlv = function(type, value)
{
if (value != null && value >= 0)
this.writeNonNegativeIntegerTlv(type, value);
};
/**
* Write the buffer value to this.output just before this.length from the back.
* Advance this.length.
* @param {Buffer} buffer The byte array with the bytes to write. If value is
* null, then do nothing.
*/
TlvEncoder.prototype.writeBuffer = function(buffer)
{
if (buffer == null)
return;
this.length += buffer.length;
this.output.copyFromBack(buffer, this.length);
};
/**
* Write the type, then the length of the buffer then the buffer value to
* this.output just before this.length from the back. Advance this.length.
* @param {number} type The type of the TLV.
* @param {Buffer} value The byte array with the bytes of the blob. If value is
null, then just write the type and length 0.
*/
TlvEncoder.prototype.writeBlobTlv = function(type, value)
{
if (value == null) {
this.writeTypeAndLength(type, 0);
return;
}
// Write backwards, starting with the blob array.
this.writeBuffer(value);
this.writeTypeAndLength(type, value.length);
};
/**
* If the byte array is null or zero length then do nothing, otherwise call
* writeBlobTlv.
* @param {number} type The type of the TLV.
* @param {Buffer} value If null or zero length do nothing, otherwise the byte
* array with the bytes of the blob.
*/
TlvEncoder.prototype.writeOptionalBlobTlv = function(type, value)
{
if (value != null && value.length > 0)
this.writeBlobTlv(type, value);
};
/**
* Get a slice of the encoded bytes.
* @return {Buffer} A slice backed by the encoding Buffer.
*/
TlvEncoder.prototype.getOutput = function()
{
return this.output.array.slice(this.output.array.length - this.length);
};