or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

binary-io.mdbytestring-utilities.mddebug-utilities.mdextension-fields.mdindex.mdmap-operations.mdmessage-operations.md
tile.json

bytestring-utilities.mddocs/

ByteString Utilities

ByteString is a specialized class for handling binary data (bytes fields) in Protocol Buffer messages. It provides safe, immutable handling of byte arrays with conversion utilities between different formats.

Core ByteString Class { .api }

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

// Create from base64 string
const fromB64 = ByteString.fromBase64("SGVsbG8gV29ybGQ=");

// Create from Uint8Array or array of numbers
const fromBytes = ByteString.fromUint8Array(new Uint8Array([72, 101, 108, 108, 111]));

// Create empty ByteString
const empty = ByteString.empty();

// Check if empty
const isEmpty = byteString.isEmpty();

// Get length
const length = byteString.getLength();

Type Definitions:

/**
 * @constructor
 * Immutable wrapper for bytes fields
 */

/**
 * @param {string} value Base64 encoded string (RFC 4648 section 4)
 * @return {!ByteString}
 */

/**
 * @param {!Uint8Array|!Array<number>} value Byte array data
 * @return {!ByteString}
 */

ByteString Conversion Methods { .api }

Base64 Conversion

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

// Create from base64
const bs = ByteString.fromBase64("SGVsbG8gV29ybGQ=");

// Convert to base64
const base64String = bs.toBase64();
console.log(base64String); // "SGVsbG8gV29ybGQ="

// URL-safe base64 (alternative encoding)
const urlSafeB64 = bs.toBase64(true); // Uses URL-safe alphabet

Byte Array Conversion

// Create from Uint8Array
const bytes = new Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]);
const bs = ByteString.fromUint8Array(bytes);

// Convert to Uint8Array
const restored = bs.toUint8Array();
console.log(restored); // Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100])

// Create from regular array of numbers
const numberArray = [65, 66, 67];
const bsFromArray = ByteString.fromUint8Array(numberArray);

String Conversion

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

// Create from UTF-8 string
const message = "Hello, 世界!";
const utf8Bytes = new TextEncoder().encode(message);
const bs = ByteString.fromUint8Array(utf8Bytes);

// Convert back to string (requires manual UTF-8 decoding)
const restoredBytes = bs.toUint8Array();
const restoredMessage = new TextDecoder().decode(restoredBytes);
console.log(restoredMessage); // "Hello, 世界!"

// Work with binary strings
const binaryData = "\x48\x65\x6C\x6C\x6F"; // "Hello" as binary string
const binaryBytes = new Uint8Array([...binaryData].map(c => c.charCodeAt(0)));
const bsFromBinary = ByteString.fromUint8Array(binaryBytes);

ByteString Comparison and Equality { .api }

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

const bs1 = ByteString.fromBase64("SGVsbG8="); // "Hello"
const bs2 = ByteString.fromUint8Array([72, 101, 108, 108, 111]); // Same data
const bs3 = ByteString.fromBase64("V29ybGQ="); // "World"

// ByteString comparison
const areEqual = bs1.equals(bs2);
console.log(areEqual); // true

const areDifferent = bs1.equals(bs3);
console.log(areDifferent); // false

// Compare with null/undefined
const nullComparison = bs1.equals(null);
console.log(nullComparison); // false

// Hash code for use in maps/sets
const hash1 = bs1.hashCode();
const hash2 = bs2.hashCode();
console.log(hash1 === hash2); // true (same data = same hash)

Type Definitions:

/**
 * @param {?ByteString} other ByteString to compare with
 * @return {boolean} Whether the ByteStrings contain identical data
 */

/**
 * @return {number} Hash code for this ByteString
 */

Integration with Protocol Buffers { .api }

Message Field Usage

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

// Example: Working with bytes fields in generated messages
class DocumentMessage extends Message {
  getContent() { return this.content_; }
  setContent(value) { this.content_ = value; }
  
  getThumbnail() { return this.thumbnail_; }
  setThumbnail(value) { this.thumbnail_ = value; }
}

// Set bytes field using ByteString
const document = new DocumentMessage();

// From file data
const fileData = new Uint8Array([/* file bytes */]);
const contentBytes = ByteString.fromUint8Array(fileData);
document.setContent(contentBytes);

// From base64 encoded thumbnail
const thumbnailB64 = "iVBORw0KGgoAAAANSUhEUgAA..."; // Base64 image data
const thumbnailBytes = ByteString.fromBase64(thumbnailB64);
document.setThumbnail(thumbnailBytes);

// Serialize message (ByteString handles conversion automatically)
const serialized = document.serializeBinary();

// Deserialize and access bytes
const restored = DocumentMessage.deserializeBinary(serialized);
const restoredContent = restored.getContent(); // Returns ByteString
const contentAsBytes = restoredContent.toUint8Array();

Binary I/O Integration

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

// Writing ByteString with BinaryWriter
const writer = new BinaryWriter();
const data = ByteString.fromBase64("SGVsbG8=");

writer.writeBytes(1, data); // ByteString automatically handled
const result = writer.getResultBuffer();

// Reading ByteString with BinaryReader
const reader = new BinaryReader(result);
while (reader.nextField()) {
  if (reader.getFieldNumber() === 1) {
    const readBytes = reader.readBytes(); // Returns Uint8Array by default
    const asByteString = ByteString.fromUint8Array(readBytes);
    console.log(asByteString.toBase64()); // "SGVsbG8="
  }
}

Performance and Memory Considerations { .api }

Immutability Benefits

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

// ByteString is immutable - safe to share references
const originalData = ByteString.fromBase64("SGVsbG8=");
const sharedRef1 = originalData;
const sharedRef2 = originalData;

// No risk of accidental mutation
console.log(sharedRef1 === sharedRef2); // true - same object reference
console.log(sharedRef1.equals(sharedRef2)); // true - same data

// Operations create new instances
const base64String = originalData.toBase64(); // Safe conversion
const byteArray = originalData.toUint8Array(); // Creates new Uint8Array

Memory Optimization

// Efficient for repeated operations
const cache = new Map();

function getOrCreateByteString(base64Data) {
  if (!cache.has(base64Data)) {
    cache.set(base64Data, ByteString.fromBase64(base64Data));
  }
  return cache.get(base64Data);
}

// Reuse ByteString instances for identical data
const bs1 = getOrCreateByteString("SGVsbG8=");
const bs2 = getOrCreateByteString("SGVsbG8=");
console.log(bs1 === bs2); // true - same cached instance

Conversion Best Practices

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

// Efficient: Create once, use multiple times
const data = ByteString.fromUint8Array(largeByteArray);
const base64 = data.toBase64();
const restored = data.toUint8Array();

// Less efficient: Multiple conversions
// Don't do this in performance-critical code:
function inefficientUsage(base64Data) {
  return ByteString.fromBase64(base64Data)
    .toUint8Array()
    .map(b => b.toString(16))
    .join('');
}

// Better: Convert once, work with result
function efficientUsage(base64Data) {
  const bs = ByteString.fromBase64(base64Data);
  const bytes = bs.toUint8Array();
  return bytes.map(b => b.toString(16)).join('');
}

Error Handling { .api }

Invalid Input Handling

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

// Base64 validation
try {
  const invalid = ByteString.fromBase64("Invalid!Base64@"); // May throw
} catch (error) {
  console.error("Invalid base64:", error.message);
}

// Safe creation with validation
function createSafeByteString(base64Input) {
  try {
    return ByteString.fromBase64(base64Input);
  } catch (error) {
    console.warn("Invalid base64 input, creating empty ByteString");
    return ByteString.empty();
  }
}

// Array input validation
const validBytes = [0, 255, 128]; // Valid byte values
const invalidBytes = [256, -1, 300]; // Will be truncated to valid range

const bs1 = ByteString.fromUint8Array(validBytes);
const bs2 = ByteString.fromUint8Array(invalidBytes); // Values converted to valid bytes

Null and Undefined Handling

// Safe handling of null/undefined
function safeByteStringOps(maybeByteString) {
  if (!maybeByteString) {
    return ByteString.empty();
  }
  
  if (maybeByteString instanceof ByteString) {
    return maybeByteString;
  }
  
  // Handle string input
  if (typeof maybeByteString === 'string') {
    try {
      return ByteString.fromBase64(maybeByteString);
    } catch {
      return ByteString.empty();
    }
  }
  
  // Handle array input
  if (Array.isArray(maybeByteString) || maybeByteString instanceof Uint8Array) {
    return ByteString.fromUint8Array(maybeByteString);
  }
  
  return ByteString.empty();
}

Common Use Cases

File Upload/Download

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

// File upload: Convert File to ByteString
async function fileToByteString(file) {
  const arrayBuffer = await file.arrayBuffer();
  const uint8Array = new Uint8Array(arrayBuffer);
  return ByteString.fromUint8Array(uint8Array);
}

// File download: Convert ByteString to downloadable blob
function byteStringToBlob(byteString, mimeType = 'application/octet-stream') {
  const bytes = byteString.toUint8Array();
  return new Blob([bytes], { type: mimeType });
}

// Usage example
async function handleFileUpload(fileInput) {
  const file = fileInput.files[0];
  const byteString = await fileToByteString(file);
  
  // Store in Protocol Buffer message
  const message = new FileMessage();
  message.setContent(byteString);
  message.setFileName(file.name);
  message.setMimeType(file.type);
  
  return message;
}

Image Processing

// Working with image data
async function processImage(imageByteString) {
  // Convert to blob for canvas processing
  const blob = new Blob([imageByteString.toUint8Array()], { type: 'image/jpeg' });
  const imageUrl = URL.createObjectURL(blob);
  
  // Load into canvas
  const img = new Image();
  img.src = imageUrl;
  
  await new Promise(resolve => img.onload = resolve);
  
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  canvas.width = img.width;
  canvas.height = img.height;
  ctx.drawImage(img, 0, 0);
  
  // Process and return as ByteString
  const processedBlob = await new Promise(resolve => canvas.toBlob(resolve));
  const processedBytes = new Uint8Array(await processedBlob.arrayBuffer());
  
  URL.revokeObjectURL(imageUrl);
  return ByteString.fromUint8Array(processedBytes);
}