CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-google-protobuf

Protocol Buffers JavaScript runtime library for serializing structured data

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

binary-io.mddocs/

Binary I/O Operations

The binary I/O system provides low-level reading and writing of Protocol Buffer wire format data. It consists of BinaryReader for parsing binary data and BinaryWriter for generating binary data, along with supporting encoder/decoder classes.

BinaryReader { .api }

The BinaryReader class reads Protocol Buffer binary data with type checking and field navigation:

const { BinaryReader } = require('google-protobuf');

// Create reader from binary data
const reader = new BinaryReader(bytes, start, length, options);

// Alternative: Use cached allocation
const reader = BinaryReader.alloc(bytes, start, length, options);

// Navigate through fields
while (reader.nextField()) {
  const fieldNumber = reader.getFieldNumber();
  const wireType = reader.getWireType();
  
  // Read field based on type
  switch (fieldNumber) {
    case 1:
      const stringValue = reader.readString();
      break;
    case 2: 
      const int32Value = reader.readInt32();
      break;
  }
}

// Clean up (returns to cache if allocated)
reader.free();

Type Definitions:

/**
 * @constructor
 * @param {!Uint8Array} bytes Binary data to read
 * @param {number=} start Starting offset (default: 0)  
 * @param {number=} length Length to read (default: bytes.length)
 * @param {BinaryReaderOptions=} options Reader options
 */

/**
 * @typedef {Object} BinaryReaderOptions
 * @property {boolean=} discardUnknownFields Whether to discard unknown fields
 * @property {boolean=} aliasBytesFields Whether bytes fields should alias original buffer
 */

Reader Navigation { .api }

const { BinaryReader } = require('google-protobuf');

// Field navigation
const hasNextField = reader.nextField();
const hasExpectedTag = reader.nextFieldIfTagEqualTo(expectedTag);

// Position information
const cursor = reader.getCursor();
const fieldCursor = reader.getFieldCursor(); // Position before current field
reader.advance(byteCount);

// Field information
const tag = reader.getTag();
const fieldNumber = reader.getFieldNumber();
const wireType = reader.getWireType();
const isEndOfGroup = reader.isEndGroup();
const isDelimited = reader.isDelimited();

// Buffer access
const buffer = reader.getBuffer();
const byteString = reader.getBufferAsByteString();
const isImmutable = reader.dataIsImmutable();

// Example: Manual field parsing
const reader = new BinaryReader(data);
while (reader.nextField()) {
  console.log(`Field ${reader.getFieldNumber()}, Wire Type: ${reader.getWireType()}`);
  
  if (reader.getWireType() === 2) { // DELIMITED
    console.log(`Delimited field at cursor: ${reader.getCursor()}`);
  }
}

Field Reading Methods { .api }

Varint Fields

// Integer types
const uint32 = reader.readUint32();
const int32 = reader.readInt32(); 
const sint32 = reader.readSint32();
const uint64 = reader.readUint64();
const int64 = reader.readInt64();
const sint64 = reader.readSint64();

// Boolean
const boolValue = reader.readBool();

// Enum (reads as varint)
const enumValue = reader.readEnum();

// 64-bit values with split high/low parts (for precision)
const uint64Split = reader.readSplitUint64();
const int64Split = reader.readSplitInt64();
const sint64Split = reader.readSplitSint64();

// Example: Reading various integer types
while (reader.nextField()) {
  switch (reader.getFieldNumber()) {
    case 1:
      const id = reader.readUint32();
      break;
    case 2:
      const count = reader.readInt32();
      break;
    case 3:
      const flag = reader.readBool();
      break;
  }
}

Fixed Fields

// 32-bit fixed
const fixed32 = reader.readFixed32();
const sfixed32 = reader.readSfixed32();
const float = reader.readFloat();

// 64-bit fixed  
const fixed64 = reader.readFixed64();
const sfixed64 = reader.readSfixed64();
const double = reader.readDouble();

// Split 64-bit reads (for precision)
const fixed64Split = reader.readSplitFixed64();
const sfixed64Split = reader.readSplitSfixed64();

// Example: Reading fixed-width fields
while (reader.nextField()) {
  switch (reader.getFieldNumber()) {
    case 1:
      const timestamp = reader.readFixed64();
      break;
    case 2:
      const temperature = reader.readFloat();
      break;
    case 3:
      const coordinate = reader.readDouble();
      break;
  }
}

Delimited Fields

// String and bytes
const stringValue = reader.readString();
const bytesValue = reader.readBytes();

// Messages (nested)
const messageBytes = reader.readMessage(subMessage, deserializeFn);

// Packed repeated fields
const packedInt32s = reader.readPackedInt32();
const packedUint32s = reader.readPackedUint32();
const packedBools = reader.readPackedBool();
const packedFloats = reader.readPackedFloat();
const packedDoubles = reader.readPackedDouble();
// ... other packed types

// Example: Reading delimited fields
while (reader.nextField()) {
  switch (reader.getFieldNumber()) {
    case 1:
      const name = reader.readString();
      break;
    case 2:
      const data = reader.readBytes();
      break;
    case 3:
      const nestedMessage = new NestedMessage();
      reader.readMessage(nestedMessage, NestedMessage.deserializeBinaryFromReader);
      break;
    case 4:
      const numbers = reader.readPackedInt32();
      break;
  }
}

Field Skipping { .api }

const { BinaryReader } = require('google-protobuf');

// Skip fields by wire type
reader.skipVarintField();    // Skip varint field
reader.skipDelimitedField(); // Skip delimited field  
reader.skipFixed32Field();   // Skip 32-bit fixed field
reader.skipFixed64Field();   // Skip 64-bit fixed field
reader.skipGroup();          // Skip group field (deprecated)

// Example: Selective field parsing with skipping
while (reader.nextField()) {
  const fieldNumber = reader.getFieldNumber();
  
  switch (fieldNumber) {
    case 1:
      const importantValue = reader.readString();
      break;
    case 2:
      // Skip this field - not needed
      reader.skipDelimitedField();
      break;
    case 3:
      const anotherValue = reader.readInt32();
      break;
    default:
      // Skip unknown fields
      switch (reader.getWireType()) {
        case 0: reader.skipVarintField(); break;
        case 1: reader.skipFixed64Field(); break;
        case 2: reader.skipDelimitedField(); break;
        case 5: reader.skipFixed32Field(); break;
      }
  }
}

BinaryWriter { .api }

The BinaryWriter class generates Protocol Buffer binary data:

const { BinaryWriter } = require('google-protobuf');

// Create writer
const writer = new BinaryWriter();

// Write fields
writer.writeString(1, "Hello World");
writer.writeInt32(2, 42);
writer.writeBool(3, true);

// Get result
const resultBuffer = writer.getResultBuffer();        // Uint8Array
const resultBytes = writer.getResultBufferAsByteString(); // ByteString
const resultBase64 = writer.getResultBase64String();  // Base64 string

// Reset for reuse
writer.reset();

Type Definitions:

/**
 * @constructor
 * Creates a new binary writer for Protocol Buffer wire format
 */

Field Writing Methods { .api }

Varint Fields

const { BinaryWriter } = require('google-protobuf');

const writer = new BinaryWriter();

// Integer types
writer.writeUint32(fieldNumber, uint32Value);
writer.writeInt32(fieldNumber, int32Value);
writer.writeSint32(fieldNumber, sint32Value);
writer.writeUint64(fieldNumber, uint64Value);  
writer.writeInt64(fieldNumber, int64Value);
writer.writeSint64(fieldNumber, sint64Value);

// Boolean and enum
writer.writeBool(fieldNumber, boolValue);
writer.writeEnum(fieldNumber, enumValue);

// Split 64-bit writes (for precision)
writer.writeSplitUint64(fieldNumber, lowBits, highBits);
writer.writeSplitInt64(fieldNumber, lowBits, highBits);
writer.writeSplitSint64(fieldNumber, lowBits, highBits);

// Example: Writing message fields
writer.writeString(1, message.getName());
writer.writeInt32(2, message.getAge());
writer.writeBool(3, message.getActive());
writer.writeEnum(4, message.getStatus());

Fixed Fields

// 32-bit fixed
writer.writeFixed32(fieldNumber, fixed32Value);
writer.writeSfixed32(fieldNumber, sfixed32Value);
writer.writeFloat(fieldNumber, floatValue);

// 64-bit fixed
writer.writeFixed64(fieldNumber, fixed64Value);
writer.writeSfixed64(fieldNumber, sfixed64Value);  
writer.writeDouble(fieldNumber, doubleValue);

// Split 64-bit writes
writer.writeSplitFixed64(fieldNumber, lowBits, highBits);
writer.writeSplitSfixed64(fieldNumber, lowBits, highBits);

// Example: Writing numeric data
writer.writeFloat(1, temperature);
writer.writeDouble(2, coordinates.latitude);
writer.writeFixed64(3, timestamp);

Delimited Fields

// String and bytes
writer.writeString(fieldNumber, stringValue);
writer.writeBytes(fieldNumber, bytesValue);

// Messages (nested)
writer.writeMessage(fieldNumber, message, serializeFn);

// Pre-serialized messages
writer.writeSerializedMessage(serializedBytes, start, end);

// Group fields (deprecated)
writer.writeGroup(fieldNumber, message, serializeFn);

// Example: Writing complex fields
writer.writeString(1, "User Name");
writer.writeBytes(2, profilePicture);

// Write nested message
writer.writeMessage(3, address, (msg, writer) => {
  writer.writeString(1, msg.getStreet());
  writer.writeString(2, msg.getCity());
});

Repeated and Packed Fields { .api }

const { BinaryWriter } = require('google-protobuf');

const writer = new BinaryWriter();

// Repeated fields (write multiple times with same field number)
const names = ["Alice", "Bob", "Charlie"];
names.forEach(name => {
  writer.writeString(1, name);
});

// Packed repeated fields (more efficient for numeric types)
const numbers = [1, 2, 3, 4, 5];
writer.writePackedInt32(2, numbers);

// Other packed types
writer.writePackedUint32(3, uint32Array);
writer.writePackedBool(4, boolArray);
writer.writePackedFloat(5, floatArray);
writer.writePackedDouble(6, doubleArray);
writer.writePackedFixed32(7, fixed32Array);
writer.writePackedFixed64(8, fixed64Array);
// ... other packed types available

// Example: Efficient numeric array serialization
const measurements = [1.1, 2.2, 3.3, 4.4, 5.5];
writer.writePackedFloat(1, measurements);

const timestamps = [100n, 200n, 300n, 400n];  
writer.writePackedUint64(2, timestamps);

Advanced Writing { .api }

const { BinaryWriter } = require('google-protobuf');

const writer = new BinaryWriter();

// Write any field type dynamically
const fieldType = 9; // STRING type
writer.writeAny(fieldType, fieldNumber, value);

// Write unknown fields (preserves unrecognized data)
writer.writeUnknownFields(unknownFieldsMap);

// Direct buffer writing for pre-serialized data
writer.writeSerializedMessage(preSerializedBytes, 0, preSerializedBytes.length);

// Example: Dynamic field writing
const fields = [
  { type: 9, number: 1, value: "string value" },    // STRING
  { type: 5, number: 2, value: 42 },                // INT32  
  { type: 8, number: 3, value: true }               // BOOL
];

fields.forEach(field => {
  writer.writeAny(field.type, field.number, field.value);
});

const result = writer.getResultBuffer();

Binary Constants and Types { .api }

const { BinaryConstants } = require('google-protobuf');

// Field types
const fieldTypes = {
  INVALID: BinaryConstants.FieldType.INVALID,    // -1
  DOUBLE: BinaryConstants.FieldType.DOUBLE,      // 1
  FLOAT: BinaryConstants.FieldType.FLOAT,        // 2
  INT64: BinaryConstants.FieldType.INT64,        // 3
  UINT64: BinaryConstants.FieldType.UINT64,      // 4
  INT32: BinaryConstants.FieldType.INT32,        // 5
  FIXED64: BinaryConstants.FieldType.FIXED64,    // 6
  FIXED32: BinaryConstants.FieldType.FIXED32,    // 7
  BOOL: BinaryConstants.FieldType.BOOL,          // 8
  STRING: BinaryConstants.FieldType.STRING,      // 9
  MESSAGE: BinaryConstants.FieldType.MESSAGE,    // 11
  BYTES: BinaryConstants.FieldType.BYTES,        // 12
  UINT32: BinaryConstants.FieldType.UINT32,      // 13
  ENUM: BinaryConstants.FieldType.ENUM,          // 14
  SFIXED32: BinaryConstants.FieldType.SFIXED32,  // 15
  SFIXED64: BinaryConstants.FieldType.SFIXED64,  // 16
  SINT32: BinaryConstants.FieldType.SINT32,      // 17
  SINT64: BinaryConstants.FieldType.SINT64       // 18
};

// Wire types  
const wireTypes = {
  INVALID: BinaryConstants.WireType.INVALID,      // -1
  VARINT: BinaryConstants.WireType.VARINT,        // 0
  FIXED64: BinaryConstants.WireType.FIXED64,      // 1
  DELIMITED: BinaryConstants.WireType.DELIMITED,  // 2
  FIXED32: BinaryConstants.WireType.FIXED32       // 5
};

// Utility functions
const wireType = BinaryConstants.FieldTypeToWireType(fieldType);
const isValidWire = BinaryConstants.isValidWireType(wireType);

// Constants
const invalidField = BinaryConstants.INVALID_FIELD_NUMBER; // -1
const invalidTag = BinaryConstants.INVALID_TAG;            // -1

Type Definitions:

/**
 * @enum {number} Field types for Protocol Buffer fields
 */

/**
 * @enum {number} Wire types for Protocol Buffer encoding
 */

Advanced Binary Operations { .api }

For complex binary operations, use the high-level BinaryReader and BinaryWriter classes which provide comprehensive wire format support:

const { BinaryReader, BinaryWriter } = require('google-protobuf');

// Complex message parsing
function parseComplexMessage(bytes) {
  const reader = new BinaryReader(bytes);
  const result = {};
  
  while (reader.nextField()) {
    const fieldNumber = reader.getFieldNumber();
    const wireType = reader.getWireType();
    
    // Handle different field types based on wire type
    switch (wireType) {
      case 0: // VARINT
        result[fieldNumber] = reader.readUint64();
        break;
      case 1: // FIXED64
        result[fieldNumber] = reader.readDouble();
        break;
      case 2: // DELIMITED
        result[fieldNumber] = reader.readString();
        break;
      case 5: // FIXED32
        result[fieldNumber] = reader.readFloat();
        break;
    }
  }
  
  return result;
}

// Complex message generation
function generateComplexMessage(data) {
  const writer = new BinaryWriter();
  
  Object.entries(data).forEach(([fieldNum, value]) => {
    const fieldNumber = parseInt(fieldNum);
    
    if (typeof value === 'string') {
      writer.writeString(fieldNumber, value);
    } else if (typeof value === 'number') {
      writer.writeDouble(fieldNumber, value);
    }
  });
  
  return writer.getResultBuffer();
}

Performance and Memory Considerations

Reader Optimization

// Use allocation cache for frequently created readers
const reader = BinaryReader.alloc(bytes); // From cache
// ... use reader
reader.free(); // Return to cache

// Configure reader options for performance
const options = {
  aliasBytesFields: true,    // Avoid copying bytes (careful with mutability)
  discardUnknownFields: true // Skip unknown fields entirely
};
const reader = new BinaryReader(bytes, 0, bytes.length, options);

Writer Optimization

// Reuse writer instances
const writer = new BinaryWriter();

function serializeMessage(message) {
  writer.reset(); // Clear previous data
  
  // Write fields
  writer.writeString(1, message.name);
  writer.writeInt32(2, message.value);
  
  return writer.getResultBuffer();
}

// Multiple serializations with same writer
const results = messages.map(serializeMessage);

Memory Management

// Efficient buffer handling
const reader = new BinaryReader(buffer);

// Avoid creating unnecessary intermediate objects
while (reader.nextField()) {
  const fieldNum = reader.getFieldNumber();
  
  // Process fields directly without intermediate storage
  switch (fieldNum) {
    case 1:
      processString(reader.readString());
      break;
    case 2:
      processNumber(reader.readInt32());
      break;
  }
}

docs

binary-io.md

bytestring-utilities.md

debug-utilities.md

extension-fields.md

index.md

map-operations.md

message-operations.md

tile.json