Skip to content

Instantly share code, notes, and snippets.

@hdon
Created June 5, 2009 19:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hdon/124442 to your computer and use it in GitHub Desktop.
Save hdon/124442 to your computer and use it in GitHub Desktop.
'#!/usr/bin/gsr -zzdd'
/* A pure Javascript reference implementation of ServerJS's binary ByteArray
* class for reference, implementation, and testing purposes.
*
* For more info see: https://wiki.mozilla.org/ServerJS/Binary/B
*/
/* Do we have modules? */
if (('function' == typeof require) && require("binary")) {
print("using require('binary') for ByteString");
var ByteString = require("binary").ByteString;
} else {
/* TODO invent a pure Javascript ByteString class */
print("defining dummy ByteString");
var ByteString = function(){};
}
const ByteArray = function() {
/* Private methods */
/** ByteArray.validate(value)
* @name ByteArray.validate
* @param value
* @function
* @private
* @throws ByteArray.ValueError
*
* Throws an exception if the argument is not a valid byte value.
*/
function validate(v) {
if ('number' !== typeof v)
throw 'number expected :(';
if ((v < 0) || (v > 255))
throw 'value out of range error :(';
}
/** ByteArray.checkIndex(index)
* @name ByteArray.checkIndex
* @param index
* @function
* @private
* @throws ByteArray.TypeError
* @throws ByteArray.IndexError
*
* Returns true is the argument is greater than zero but less than the length
* of this ByteArray.
*/
function checkIndex(i) {
if ('number' !== typeof i)
throw 'number expected :(';
if (0 > i || i >= this.length)
throw 'index out of bounds :(';
}
/** ByteArray.set(index, value)
* @name ByteArray.set
* @param index
* @param value
* @function
* @private
* @throws ByteArray.ValueError
* @throws ByteArray.TypeError
* @throws ByteArray.IndexError
*
* Sets member 'index' to 'value'.
*/
function set(i, v) {
validate(v);
checkIndex.call(this, i);
this.store[i] = v;
}
/** ByteArray.grow(length)
* @name ByteArray.grow
* @param length
* @function
*
* Increases the size of internal byte store. This function *must* be called
* to put getters and setters in place, due to a limitation that getters and
* setters must be installed individually, and it would be inefficient to
* install them all ahead of time for every possible integer :)
*/
function grow (length) {
while (this.store.length < length) {
/* TODO lots of room for improvement... */
var index = this.store.length;
this.store.push(0);
// ugh! closures! wtf!
eval(('this.__defineGetter__(index, function(){return this.store[index]});' +
'this.__defineSetter__(index, function(v){set.call(this, index, v)});').
replace(/index/g, index.toString()));
}
this.length = length;
}
/* Public methods */
/* Constructor */
function ByteArray(arg) {
this.store = new Array();
/** ByteArray(string, charset)
* @name ByteArray
* @param string Initialization data
* @param charset The characterset in which to interpret the
* values of the initialization data.
* @function
* @public
* @constructor
* @throws ByteArray.InvalidConstructorArguments
*
* Returns a ByteArray of 'length' bytes each initialized to 0.
*/
if (arguments.length == 2)
throw 'unimplemented! :(';
if (arguments.length != 1)
throw 'invalid constructor arguments! :(';
/** ByteArray(length)
* @name ByteArray
* @param length
* @function
* @public
* @constructor
*
* Returns a ByteArray of 'length' bytes each initialized to 0.
*/
if ('number' == typeof arg) {
grow.call(this, arg);
return;
}
/** ByteArray(bytes:Array|ByteArray|ByteString)
* @name ByteArray
* @param bytes Initialization data
* @function
* @public
* @constructor
*
* Returns a ByteArray of initialized with bytes taken from the 'bytes'
* argument.
*/
if ((arg instanceof Array) || (arg instanceof ByteString) || (arg instanceof ByteArray)) {
grow.call(this, arg.length);
for(var i=0, l=arg.length; i<l; i++)
set.call(this, i, arg[i]);
return;
}
throw 'invalid constructor arguments! :(';
}
/** ByteArray.toString()
* @name ByteArray.toString
* @function
* @public
*
* Returns a friendly-looking string representation of the ByteArray instance.
*/
ByteArray.prototype.toString = function() {
return "[ByteArray "+this.length+"]";
}
/** ByteArray.toArray()
* @name ByteArray.toArray
* @param charset Character set with which to interpret byte data.
* @function
* @public
*
* Returns a new array of values taken from this ByteArray.
*/
ByteArray.prototype.toArray = function(charset) {
if (charset) throw 'unimplemented! :(';
return this.store.map(function(v)v);
}
/** ByteArray.decodeToString()
* @name ByteArray.decodeToString
* @param charset Character set with which to interpret byte data.
* @function
* @public
*
* Returns a nwe string with each character in the string constituted from one byte
* of data from this ByteArray.
*/
ByteArray.prototype.decodeToString = function(charset) {
if (charset) throw 'unimplemented! :(';
return this.store.map(function(c)String.fromCharCode(c)).join('');
}
/** ByteArray.toSource()
* @name ByteArray.toSource
* @function
* @public
*
* Returns a string of executable Javascript source code which can be used to
* instantiate an exact copy of this ByteArray instance.
*/
ByteArray.prototype.toString = function() {
return "(new ByteArray(["+(this.store.join(','))+"]))";
}
/** ByteArray.forEach(callback)
* @name ByteArray.forEach
* @param value
* @function
* @public
*
* Calling this function is equivalent to calling "callback(a[n], n, a)" where
* 'a' is this instance of ByteArray, 'a[n]' is the nth member of this ByteArray,
* and 'callback' is the argument to forEach().
*/
ByteArray.prototype.forEach = function(f) {
this.store.forEach(f);
}
/** ByteArray.push(value)
* @name ByteArray.push
* @param value
* @function
* @public
*
* This extends the contents of this ByteArray instance by appending 'value' to
* the end of its contents.
*/
ByteArray.prototype.push = function(v) {
validate(v);
this.store.push(v);
this.length++;
}
/** ByteArray.every(callback)
* @name ByteArray.every
* @param callback
* @function
* @public
*
* Returns true if every element in this ByteArray satisfies the provided testing function.
*/
ByteArray.prototype.every = function(f) {
return this.store.every(function(v,i)f(v,i,this));
}
/** ByteArray.some(callback)
* @name ByteArray.some
* @param callback
* @function
* @public
*
* Returns true if at least one element in this ByteArray satisfies the provided testing function.
*/
ByteArray.prototype.some = function(f) {
return this.store.some(function(v,i)f(v,i,this));
}
/** ByteArray.filter(callback)
* @name ByteArray.filter
* @param callback
* @function
* @public
*
* Returns a new array with the results of calling a provided function on every element in this
* ByteArray.
*
* TODO Option for a binary-descended result
*/
ByteArray.prototype.filter = function(f) {
return this.store.filter(function(v,i)f(v,i,this));
}
/** ByteArray.map(callback)
* @name ByteArray.map
* @param callback
* @function
* @public
*
* Returns a new array with the results of calling a provided function on every element in this
* ByteArray.
*
* TODO Option for a binary-descended result
*/
ByteArray.prototype.map = function(f) {
return this.store.map(function(v,i)f(v,i,this));
}
/** ByteArray.reduce(callback)
* @name ByteArray.reduce
* @param callback
* @function
* @public
*
* Apply a function simultaneously against two values of this ByteArray (from left-to-right) as
* to reduce it to a single value.
*/
ByteArray.prototype.reduce = function(f) {
return this.store.reduce(function(a,b,i)f(a,b,i,this));
}
/** ByteArray.reduceRight(callback)
* @name ByteArray.reduceRight
* @param callback
* @function
* @public
*
* Apply a function simultaneously against two values of this ByteArray (from right-to-left) as
* to reduce it to a single value.
*/
ByteArray.prototype.reduceRight = function(f) {
return this.store.reduceRight(function(a,b,i)f(a,b,i,this));
}
/* Return new class :) */
return ByteArray;
}();
/* Tests! */
var ba = new ByteArray(8);
for(var i=0; i<8; i++) {
ba[i] = i | 128;
}
for(var i=0; i<8; i++) {
print(ba[i]);
}
print(ba.every(function(n) n&128) === true);
print(ba.some(function(n) n&1) === true);
print(ba.some(function(n) n&8) === false);
print(new ByteArray(ba).every(function(n) n&128) === true);
print(new ByteArray([72,101,108,108,111,32,87,111,114,108,100,33]).decodeToString() === 'Hello World!');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment