npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@ndn/tlv

v0.0.20240630

Published

NDNts: TLV

Downloads

13

Readme

@ndn/tlv

This package is part of NDNts, Named Data Networking libraries for the modern web.

This package implements Type-Length-Value structure encoder and decoder as specified in NDN Packet Format v0.3. It has full support for TLV evolvability guidelines.

import { Encoder, Decoder, EvDecoder, NNI, StructBuilder, StructFieldNNI, StructFieldText } from "@ndn/tlv";

// other imports for examples
import { Name, TT as l3TT, StructFieldName } from "@ndn/packet";
import assert from "node:assert/strict";

Encoder

The Encoder prepends encodable items to an internal ArrayBuffer. It reallocates a larger buffer when necessary.

// Encode TLV object that implements EncodableObj interface:
let encoder = new Encoder();
encoder.encode(new Name("/A"));
// Look at the output:
assert.deepEqual(encoder.output, Uint8Array.of(0x07, 0x03, 0x08, 0x01, 0x41));

// Prepend a TLV structure with specified TLV-TYPE and TLV-VALUE:
encoder = new Encoder();
encoder.encode([0xB0, Uint8Array.of(0xC0, 0xC1)]);
assert.deepEqual(encoder.output, Uint8Array.of(0xB0, 0x02, 0xC0, 0xC1));

// Prepend a non-negative integer
encoder.encode(NNI(0x200110));
// We are using the same Encoder instance, so it gets prepended:
assert.deepEqual(encoder.output, Uint8Array.of(0x00, 0x20, 0x01, 0x10, 0xB0, 0x02, 0xC0, 0xC1));

// Put multiple encodable items in TLV-VALUE:
encoder = new Encoder();
encoder.encode([0xB0, Uint8Array.of(0xC0, 0xC1), new Name("/A")]);
assert.deepEqual(encoder.output,
  Uint8Array.of(0xB0, 0x07, 0xC0, 0xC1, 0x07, 0x03, 0x08, 0x01, 0x41));

// `Encoder.encode()` is a shortcut for encoding one item and obtaining the output:
const wireB = Encoder.encode(new Name("/B"));
assert.deepEqual(wireB, Uint8Array.of(0x07, 0x03, 0x08, 0x01, 0x42));

Decoder

The Decoder is a basic sequential decoder.

// Read Type-Length-Value manually:
let decoder = new Decoder(Uint8Array.of(0x08, 0x01, 0x41, 0xFF));
const { type, length, value } = decoder.read();
assert.equal(type, 0x08);
assert.equal(length, 1);
assert.deepEqual(value, Uint8Array.of(0x41));
// The remaining [0xFF] is still in the buffer.
assert.equal(decoder.eof, false);
// If you continue reading, you get an error due to incomplete TLV.
assert.throws(() => decoder.read());

// Decode into TLV object:
decoder = new Decoder(Uint8Array.of(0x07, 0x03, 0x08, 0x01, 0x41));
const nameA = decoder.decode(Name);
assert(nameA instanceof Name);
assert.equal(nameA.toString(), "/8=A");
// We have fully consumed the buffer.
assert.equal(decoder.eof, true);

// `Decoder.decode()` is a shortcut for decoding one item and checking for EOF.
const nameB = Decoder.decode(wireB, Name);
assert(nameB instanceof Name);
assert.equal(nameB.toString(), "/8=B");
// It throws if there's junk after the TLV.
assert.throws(() => Decoder.decode(Uint8Array.of(...wireB, 0xFF), Name));

EvDecoder

The EvDecoder is a decoder that is aware of TLV evolvability guidelines. It's used to implement decoding functions of TLV objects, such as Interest.decodeFrom.

Suppose we want to decode Adjacency type in NLSR's LSDB Dataset:

Adjacency = ADJACENCY-TYPE TLV-LENGTH
              Name
              Uri
              Cost
Uri = URI-TYPE TLV-LENGTH *VCHAR
Cost = COST-TYPE TLV-LENGTH nonNegativeInteger

ADJACENCY-TYPE = 0x84
URI-TYPE = 0x8D
COST-TYPE = 0x8C
// Declare a class to represent this type.
class Adjacency {
  public name = new Name();
  public uri = "";
  public cost = 0;
}

// Declare constants for TLV-TYPE numbers.
const TT = {
  ...l3TT,
  Adjacency: 0x84,
  Cost: 0x8C,
  Uri: 0x8D,
} as const;

// Create the decoder.
const EVD = new EvDecoder<Adjacency>("Adjacency", TT.Adjacency)
  .add(TT.Name, (t, { decoder }) => t.name = decoder.decode(Name), { required: true })
  .add(TT.Uri, (t, { text }) => t.uri = text, { required: true })
  .add(TT.Cost, (t, { nni }) => t.cost = nni, { required: true });
// Each rule declares a possible sub TLV.
// They are added in the order of expected appearance.
// The callback receives two arguments:
// (1) the target object we are decoding into, so that EVD instances are reusable;
// (2) a Decoder.Tlv structure, where we can selectively access just the TLV-VALUE, the whole TLV,
//     the TLV-VALUE as a Decoder, the whole TLV as a Decoder, etc.

// Suppose we receive this encoded TLV:
const adjacencyWire = Uint8Array.of(
  0x84, 0x0D,
  0x07, 0x03, 0x08, 0x01, 0x41, // Name
  0x8D, 0x01, 0x42, // Uri
  0xF0, 0x00, // unrecognized non-critical TLV-TYPE, ignored
  0x8C, 0x01, 0x80, // Cost
);

// We can decode it with the EVD.
const adjacency = EVD.decode(new Adjacency(), new Decoder(adjacencyWire));
assert.equal(adjacency.name.toString(), "/8=A");
assert.equal(adjacency.uri, "B");
assert.equal(adjacency.cost, 128);

StructBuilder

The StructBuilder is a helper for defining a class that represents a TLV structure. It allows you to define the typing, constructor, encoder, and decoder, while writing each field only once.

// Create a StructBuilder and add the fields.
const buildAdj = new StructBuilder("Adjacency", TT.Adjacency)
  .add(TT.Name, "name", StructFieldName, { required: true })
  .add(TT.Uri, "uri", StructFieldText, { required: true })
  .add(TT.Cost, "cost", StructFieldNNI, { required: true });
// You should call .add() on each successive return value, and save the last return value into the
// builder variable. This gradually builds up the typing of the TLV class.
// WRONG EXAMPLE:
//   const builder = new StructBuilder();
//   builder.add(...);
//   builder.add(...);
// In the wrong example, typing information is not saved into the builder variable.

// Declare a class to represent the Adjacency type, inheriting from a base class supplied by the builder.
class Adj extends buildAdj.baseClass<Adj>() {}

// Assign the subclass to the builder (otherwise the decoding function will not work).
buildAdj.subclass = Adj;

// We can construct an instance and encode it.
const adj0 = new Adj();
adj0.name = new Name("/A");
adj0.uri = "B";
adj0.cost = 128;
const adj0Wire = Encoder.encode(adj0);
assert.deepEqual(adj0Wire, Uint8Array.of(
  0x84, 0x0B,
  0x07, 0x03, 0x08, 0x01, 0x41, // Name
  0x8D, 0x01, 0x42, // Uri
  0x8C, 0x01, 0x80, // Cost
));

// We can decode the wire encoding.
const adj1 = Decoder.decode(adjacencyWire, Adj);
assert.equal(adj1.name.toString(), "/8=A");
assert.equal(adj1.uri, "B");
assert.equal(adj1.cost, 128);

StructBuilder enables rapid development of TLV based structures, but is less flexible than writing code with Encoder, Decoder, and EvDecoder. Some limitations are:

  • You cannot write JSDoc for individual fields.
  • You cannot decode multiple TLV-TYPE numbers into the same field (counterexample: Name with typed name components).
  • You cannot encode the structure with different TLV-TYPE numbers (counterexample: SigInfo encoded as either ISigInfo or DSigInfo).