node package manager
It’s your turn. Help us improve JavaScript. Take the 2017 JavaScript Ecosystem Survey »


Advanced Buffers

Using the magic of Proxies to bring new types of buffers to JavaScript.

  • ViewBuffer is a polymorphous buffer interface that allows each instance to encompass all the features of DataView, Buffer, and the Typed Arrays.
  • MatrixBuffer wraps vector objects like buffers in a two dimensional interface, allowing two level indexed access and various transforms. It also allows for the equivelent of subarrays on arbitrary rectangular sections and nesting.
  • CombinerBuffer wrap any set of vector objects like buffers and presents a single combined index that addresses them in the order they're organized in the internal array. Items can be pushed onto the end of the internal array. Changes to size are automatically reflected.
npm install view-buffer


var b = require('./view-buffer');
var str = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
var buff = new b.ViewBuffer(str+str+str);
var test1 = new b.MatrixBuffer(buff);
var test2 = test1.subview(3, 3, -4, -3);
var test3 = test2.subview(2, 2, -2, -2);
test2.fill(' ');
GHI        RST
UVW        567
89a  0123  jkl
mno  efgh  xyz
ABC  stuv  LMN
OPQ        Z01
234        def
//and the original buffer 
0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHI        RSTUVW        56789a  0123  jklmno  efgh  xyzABC  stuv  LMNOPQ        Z01234        defghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ


Functions from Array.prototype: forEach, map, reduce, reduceRight, join, reverse


  • get(index, [view]), set(index, value, [view]): functional version of indexed accessors also allowing a specific view. If the view has a different size index is in terms of that view.
  • setView(view): functional way to set view property
  • subarray([start], [end]): new ViewBuffer using the same underlying buffer where start and end are indexes based on the current view
  • slice([start], [end]): like subarray but it's always based on bytes
  • write(value, [start], [length], [view]): write a value with indexes and a length like a string, array, or buffer. Writes based on indexes relative to current view or the optionally provided one
  • clone(): create's a new buffer and copies the current ones' data to it using the current view
  • fill([v], [start], [end]): fill with a value or zero, from start to end of 0 to length if none given. Relative to current view's indexes
  • toString([encoding]): convert to encoding or current string format or hex if current is numeric and joins to a string
  • copy(target, [targetOffset], [start], [end]): copy data from current buffer to target. Target can be anything with indices and a length. Iterates based on current view.


  • numeric - Int8, Uint8, Int16, Uint16, Int32, Uint32, Float32, Float64
  • string - Ascii, Ucs2, Hex
  • other - Binary (individual bits)


The matrix is used over top an existing buffer to view it two dimensionally. Any underlying object that provides a lengrg and indexed properties (string, array, any kind of buffer) can act as the backing for this.

  • subview(left, top, right, bottom): create a new view on top of a matrix that scopes in to a particular part. Positive numbers are from the top/left, negative numbers measure from the bottom/right. -0 works to specify the very right or bottom. Decimals work as percentages.
  • fill(value): fill with a value.
  • write(settings, values): settings is an array which will have missing values filled to the tune of [0,0,width,height] to specify where to write. Values can either be a matching 2d vector structure or a callback that will be called for each index to generate the value.
  • join(x, y): two dimensional version of join
  • toArray: returns a nested array with the same structure
  • bisect(cols, rows): splits the matrix into equal part sub-matrices covering the same area


A combiner buffer's main job is to take any amount of input vector objects and provide a single "buffer" that combines them all. The exposed interface is indexed and has a length so it's a suitable tarket for most array/buffer apis. The magic is that it will dynamically reflect any changes to the underlying data objects transparently, making it incredibly versatile.

  • fill(value, start end): write a single value
  • clone(to): copy the entire buffer onto target object. You must provide the object, as opposed to most clone functions.
  • write(input, start, length): write a vector value to the indices of the Combiner
  • copy(target, offset, start, end): write values from the combiner to a target
  • append(...): adds all the arguments to be data sources for the buffer. This doesn't added data to the end, it adds data providers.
  • push(): Add a data provider to the end of the internal set.
  • pop(): Remove a single data provider from the end.
  • toArray(): Converts to array using indices.


buffer creation

A ViewBuffer can be used to wrap an existing Buffer or TypedArray or it can be used to create new buffers instead of using ArrayBuffer or Buffer. The constructor can handle the same options as other buffers as well as a few others. The simplest usage is simply passing a size in bytes.

var ViewBuffer = require('view-buffer');
var b = ViewBuffer(10);
b[3] = 100;
{ view: 'Uint8',
  bytes: 10,
  length: 10,
  '0': 0,
  '1': 0,
  '2': 0,
  '3': 100,
  '4': 0,
  '5': 0,
  '6': 0,
  '7': 0,
  '8': 0,
  '9': 0 }

Changings Views

Views are important because most of the functions ViewBuffer has iterate based on the current view's length and indices, and values are formatted based on the current view.

b.view = 'uint32';
{ view: 'Uint32',
  bytes: 10,
  length: 2,
  '0': 1677721600,
  '1': 0 }
b.view = 'uint16';
{ view: 'Uint16',
  bytes: 10,
  length: 5,
  '0': 0,
  '1': 25600,
  '2': 0,
  '3': 0,
  '4': 0 }


When writing a string the default will be to use ascii and one byte per character. The view will automatically be changed internally to do the write and then changed back. Changing the view to a text format will cause the returned values to actually be strings.

{ view: 'Uint16',
  bytes: 10,
  length: 5,
  '0': 26998,
  '1': 30565,
  '2': 30050,
  '3': 26214,
  '4': 29285 }
b.view = 'ascii';
{ view: 'Ascii',
  bytes: 10,
  length: 10,
  '0': 'v',
  '1': 'i',
  '2': 'e',
  '3': 'w',
  '4': 'b',
  '5': 'u',
  '6': 'f',
  '7': 'f',
  '8': 'e',
  '9': 'r' }

The magic of the ViewBuffer is that it allows for seamlessly abstracting away the underlying data sizes and offsets and types. Writing from a ascii view to a ucs2 view is simple.

var ucs2 = ViewBuffer('ucs2', 20);
{ view: 'Ucs2',
  bytes: 20,
  length: 10,
  '0': 'v',
  '1': 'i',
  '2': 'e',
  '3': 'w',
  '4': 'b',
  '5': 'u',
  '6': 'f',
  '7': 'f',
  '8': 'e',
  '9': 'r' }
ucs2.view = 'ascii';
{ view: 'Ascii',
  bytes: 20,
  length: 20,
  '0': 'v',
  '1': '\u0000',
  '2': 'i',
  '3': '\u0000',
  '4': 'e',
  '5': '\u0000',
  '6': 'w',
  '7': '\u0000',
  '8': 'b',
  '9': '\u0000',
  '10': 'u',
  '11': '\u0000',
  '12': 'f',
  '13': '\u0000',
  '14': 'f',
  '15': '\u0000',
  '16': 'e',
  '17': '\u0000',
  '18': 'r',
  '19': '\u0000' }