javascript

examples

examples.js
/**
 * 6.4 TypedArrays and Binary Data - Examples
 */

// ============================================
// ARRAYBUFFER BASICS
// ============================================

console.log('=== ArrayBuffer Basics ===');

// Create a 16-byte buffer
const buffer = new ArrayBuffer(16);
console.log('Buffer byte length:', buffer.byteLength);

// ArrayBuffer is just raw bytes - need views to access
console.log('Buffer:', buffer); // ArrayBuffer { byteLength: 16 }

// ============================================
// TYPEDARRAY CREATION
// ============================================

console.log('\n=== TypedArray Creation ===');

// From length (initialized to zeros)
const zeros = new Uint8Array(4);
console.log('Zeros:', zeros); // Uint8Array [0, 0, 0, 0]

// From array
const fromArray = new Int16Array([1, 2, 3, 4, 5]);
console.log('From array:', fromArray);

// From ArrayBuffer
const buf = new ArrayBuffer(8);
const uint8View = new Uint8Array(buf);
const uint16View = new Uint16Array(buf);
const uint32View = new Uint32Array(buf);

console.log('Same buffer, different views:');
console.log('  Uint8Array length:', uint8View.length); // 8
console.log('  Uint16Array length:', uint16View.length); // 4
console.log('  Uint32Array length:', uint32View.length); // 2

// ============================================
// SHARED BUFFER DEMONSTRATION
// ============================================

console.log('\n=== Shared Buffer ===');

const sharedBuf = new ArrayBuffer(4);
const bytes = new Uint8Array(sharedBuf);
const ints = new Uint32Array(sharedBuf);

// Set bytes individually
bytes[0] = 0x12;
bytes[1] = 0x34;
bytes[2] = 0x56;
bytes[3] = 0x78;

console.log(
  'Bytes:',
  Array.from(bytes).map((b) => b.toString(16))
);
console.log('As Uint32:', ints[0].toString(16)); // Depends on endianness

// ============================================
// TYPEDARRAY METHODS
// ============================================

console.log('\n=== TypedArray Methods ===');

const arr = new Uint8Array([10, 20, 30, 40, 50]);

// Properties
console.log('Length:', arr.length);
console.log('Byte length:', arr.byteLength);
console.log('Bytes per element:', arr.BYTES_PER_ELEMENT);

// Array methods work
const doubled = arr.map((x) => x * 2);
console.log('Doubled:', doubled);

const sum = arr.reduce((a, b) => a + b, 0);
console.log('Sum:', sum);

const filtered = arr.filter((x) => x > 25);
console.log('Filtered (>25):', filtered);

// slice() creates new TypedArray with new buffer
const sliced = arr.slice(1, 4);
console.log('Sliced:', sliced);

// subarray() creates view into same buffer
const sub = arr.subarray(1, 4);
console.log('Subarray:', sub);

// set() copies values
const target = new Uint8Array(10);
target.set([1, 2, 3], 0); // At offset 0
target.set([7, 8, 9], 5); // At offset 5
console.log('After set():', target);

// ============================================
// OVERFLOW BEHAVIOR
// ============================================

console.log('\n=== Overflow Behavior ===');

// Regular TypedArray wraps around
const uint8 = new Uint8Array([255]);
uint8[0] = 256;
console.log('Uint8Array overflow:', uint8[0]); // 0

uint8[0] = -1;
console.log('Uint8Array underflow:', uint8[0]); // 255

// Uint8ClampedArray clamps values
const clamped = new Uint8ClampedArray([255]);
clamped[0] = 256;
console.log('Clamped overflow:', clamped[0]); // 255

clamped[0] = -10;
console.log('Clamped underflow:', clamped[0]); // 0

// ============================================
// DATAVIEW
// ============================================

console.log('\n=== DataView ===');

const dvBuffer = new ArrayBuffer(16);
const view = new DataView(dvBuffer);

// Write different types at different offsets
view.setInt8(0, 127);
view.setInt16(1, 1000, true); // Little-endian
view.setInt32(3, 100000, true);
view.setFloat64(7, 3.14159);

// Read back
console.log('Int8 at 0:', view.getInt8(0));
console.log('Int16 at 1:', view.getInt16(1, true));
console.log('Int32 at 3:', view.getInt32(3, true));
console.log('Float64 at 7:', view.getFloat64(7));

// ============================================
// ENDIANNESS
// ============================================

console.log('\n=== Endianness ===');

const endianBuf = new ArrayBuffer(4);
const endianView = new DataView(endianBuf);
const endianBytes = new Uint8Array(endianBuf);

// Write 0x12345678 in different endianness
endianView.setUint32(0, 0x12345678, false); // Big-endian
console.log(
  'Big-endian bytes:',
  Array.from(endianBytes).map((b) => b.toString(16))
);

endianView.setUint32(0, 0x12345678, true); // Little-endian
console.log(
  'Little-endian bytes:',
  Array.from(endianBytes).map((b) => b.toString(16))
);

// Detect system endianness
function isLittleEndian() {
  const buf = new ArrayBuffer(2);
  new DataView(buf).setInt16(0, 256, true);
  return new Int16Array(buf)[0] === 256;
}
console.log('System is little-endian:', isLittleEndian());

// ============================================
// STRING ENCODING
// ============================================

console.log('\n=== String Encoding ===');

// TextEncoder/TextDecoder for UTF-8
const encoder = new TextEncoder();
const decoder = new TextDecoder();

const encoded = encoder.encode('Hello, World! 🌍');
console.log('Encoded:', encoded);
console.log('Byte length:', encoded.length);

const decoded = decoder.decode(encoded);
console.log('Decoded:', decoded);

// ============================================
// HEX CONVERSION
// ============================================

console.log('\n=== Hex Conversion ===');

function bytesToHex(bytes) {
  return Array.from(bytes)
    .map((b) => b.toString(16).padStart(2, '0'))
    .join('');
}

function hexToBytes(hex) {
  const bytes = new Uint8Array(hex.length / 2);
  for (let i = 0; i < hex.length; i += 2) {
    bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
  }
  return bytes;
}

const testBytes = new Uint8Array([0xde, 0xad, 0xbe, 0xef]);
const hex = bytesToHex(testBytes);
console.log('Bytes to hex:', hex);

const backToBytes = hexToBytes(hex);
console.log('Hex to bytes:', backToBytes);

// ============================================
// BASE64 CONVERSION
// ============================================

console.log('\n=== Base64 Conversion ===');

function bufferToBase64(buffer) {
  const bytes = new Uint8Array(buffer);
  let binary = '';
  bytes.forEach((b) => (binary += String.fromCharCode(b)));
  return btoa(binary);
}

function base64ToBuffer(base64) {
  const binary = atob(base64);
  const bytes = new Uint8Array(binary.length);
  for (let i = 0; i < binary.length; i++) {
    bytes[i] = binary.charCodeAt(i);
  }
  return bytes.buffer;
}

const testBuffer = new Uint8Array([72, 101, 108, 108, 111]).buffer;
const base64 = bufferToBase64(testBuffer);
console.log('Buffer to Base64:', base64);

const backToBuffer = base64ToBuffer(base64);
console.log('Base64 to Buffer:', new Uint8Array(backToBuffer));

// ============================================
// PRACTICAL: BINARY PROTOCOL PARSING
// ============================================

console.log('\n=== Binary Protocol Parsing ===');

// Simulate a binary message: [type:1][length:2][data:n]
function createMessage(type, data) {
  const dataBytes = encoder.encode(data);
  const buffer = new ArrayBuffer(3 + dataBytes.length);
  const view = new DataView(buffer);

  view.setUint8(0, type);
  view.setUint16(1, dataBytes.length, true);
  new Uint8Array(buffer, 3).set(dataBytes);

  return buffer;
}

function parseMessage(buffer) {
  const view = new DataView(buffer);
  const type = view.getUint8(0);
  const length = view.getUint16(1, true);
  const dataBytes = new Uint8Array(buffer, 3, length);
  const data = decoder.decode(dataBytes);

  return { type, length, data };
}

const msg = createMessage(1, 'Hello Protocol');
console.log('Created message bytes:', new Uint8Array(msg));

const parsed = parseMessage(msg);
console.log('Parsed:', parsed);

// ============================================
// PRACTICAL: FILE SIGNATURE CHECK
// ============================================

console.log('\n=== File Signatures ===');

const signatures = {
  png: [0x89, 0x50, 0x4e, 0x47],
  jpg: [0xff, 0xd8, 0xff],
  pdf: [0x25, 0x50, 0x44, 0x46],
  zip: [0x50, 0x4b, 0x03, 0x04],
};

function detectFileType(bytes) {
  for (const [type, sig] of Object.entries(signatures)) {
    if (sig.every((byte, i) => bytes[i] === byte)) {
      return type;
    }
  }
  return 'unknown';
}

// Simulate PNG header
const pngHeader = new Uint8Array([
  0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
]);
console.log('File type:', detectFileType(pngHeader));

// ============================================
// PRACTICAL: COLOR MANIPULATION
// ============================================

console.log('\n=== Color Manipulation ===');

// RGBA color as Uint8Array
function createRGBA(r, g, b, a = 255) {
  return new Uint8Array([r, g, b, a]);
}

function invertColor(rgba) {
  const inverted = new Uint8Array(4);
  inverted[0] = 255 - rgba[0]; // R
  inverted[1] = 255 - rgba[1]; // G
  inverted[2] = 255 - rgba[2]; // B
  inverted[3] = rgba[3]; // A (keep)
  return inverted;
}

function grayscale(rgba) {
  const gray = Math.round(0.299 * rgba[0] + 0.587 * rgba[1] + 0.114 * rgba[2]);
  return new Uint8Array([gray, gray, gray, rgba[3]]);
}

const red = createRGBA(255, 0, 0);
console.log('Red:', red);
console.log('Inverted:', invertColor(red));
console.log('Grayscale:', grayscale(red));

console.log('\n=== Examples Complete ===');
Examples - JavaScript Tutorial | DeepML