Preliminary binary-type commit

Note: Files will be moved around as much of the new type-handling stuff gets
mvoved into a new ejson package.

Testing to come
This commit is contained in:
Naomi Seyfer
2013-01-11 15:53:02 -08:00
committed by David Glasser
parent 8ae6e71715
commit 950f1e0213
4 changed files with 191 additions and 0 deletions

127
packages/livedata/base64.js Normal file
View File

@@ -0,0 +1,127 @@
// Base 64 encoding
(function () {
var BASE_64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var BASE_64_VALS = {};
for (var i = 0; i < BASE_64_CHARS.length; i++) {
BASE_64_VALS[BASE_64_CHARS.substr(i, 1)] = i;
};
Meteor._base64Encode = function (array) {
var answer = [];
var a = null;
var b = null;
var c = null;
var d = null;
for (var i = 0; i < array.length; i++) {
switch (i % 3) {
case 0:
a = (array[i] >> 2) & 0x3F;
b = (array[i] & 0x03) << 4;
break;
case 1:
b = b | (array[i] >> 4) & 0xF;
c = (array[i] & 0xF) << 2;
break;
case 2:
c = c | (array[i] >> 6) & 0x03;
d = array[i] & 0x3F;
answer.push(getChar(a));
answer.push(getChar(b));
answer.push(getChar(c));
answer.push(getChar(d));
a = null;
b = null;
c = null;
d = null;
break;
}
}
if (a != null) {
answer.push(getChar(a));
answer.push(getChar(b));
if (c == null)
answer.push('=');
else
answer.push(getChar(c));
if (d == null)
answer.push('=');
}
return answer.join("");
};
var getChar = function (val) {
return BASE_64_CHARS.substr(val, 1);
};
var getVal = function (ch) {
if (ch === '=') {
return -1;
}
return BASE_64_VALS[ch];
};
var newBuffer = function (len) {
if (typeof Uint8Array === 'undefined' || typeof ArrayBuffer === 'undefined') {
var ret = [];
for (var i = 0; i < len; i++) {
ret.push(0);
}
ret.$Uint8ArrayPolyfill = true;
return ret;
}
return new Uint8Array(new ArrayBuffer(len));
};
Meteor._base64Decode = function (str) {
var len = Math.floor((str.length*3)/4);
if (str.substr(str.length - 1) == '=') {
len--;
if (str.substr(str.length - 2, 1) == '=')
len--;
}
var arr = newBuffer(len);
var one = null;
var two = null;
var three = null;
var j = 0;
for (var i = 0; i < str.length; i++) {
var c = str.substr(i, 1);
var v = getVal(c);
switch (i % 4) {
case 0:
if (v < 0)
throw new Error('invalid base64 string');
one = v << 2;
break;
case 1:
if (v < 0)
throw new Error('invalid base64 string');
one = one | (v >> 4);
arr[j++] = one;
two = (v & 0x0F) << 4;
break;
case 2:
if (v > 0) {
two = two | (v >> 2);
arr[j++] = two;
three = (v & 0x03) << 6;
}
break;
case 3:
if (v > 0) {
arr[j++] = three | v;
}
break;
}
}
return arr;
};
})();

View File

@@ -99,6 +99,21 @@ var builtinConverters = [
return new Date(obj.$date);
}
},
{ // Binary
matchJSONValue: function (obj) {
return _.has(obj, '$binary') && _.size(obj) === 1;
},
matchObject: function (obj) {
return typeof Uint8Array !== 'undefined' && obj instanceof Uint8Array
|| (obj && _.has(obj, '$Uint8ArrayPolyfill'));
},
toJSONValue: function (obj) {
return {$binary: Meteor._base64Encode(obj)};
},
fromJSONValue: function (obj) {
return Meteor._base64Decode(obj.$binary);
}
},
{ // Literal
matchJSONValue: function (obj) {
return _.has(obj, '$literal') && _.size(obj) === 1;

View File

@@ -13,6 +13,7 @@ Package.on_use(function (api) {
api.add_files('writefence.js', 'server');
api.add_files('crossbar.js', 'server');
api.add_files('base64.js', ['client', 'server']);
api.add_files('livedata_common.js', ['client', 'server']);
@@ -35,4 +36,5 @@ Package.on_test(function (api) {
api.add_files('livedata_tests.js', ['client', 'server']);
api.add_files('livedata_test_service.js', ['client', 'server']);
api.add_files('session_view_tests.js', ['server']);
api.add_files('test_base64.js', ['client', 'server']);
});

View File

@@ -0,0 +1,47 @@
(function () {
var asciiToArray = function (str) {
var arr = new Uint8Array(new ArrayBuffer(str.length));
for (var i = 0; i < str.length; i++) {
var c = str.charCodeAt(i);
if (c > 0xFF) {
throw new Error("Not ascii");
}
arr[i] = c;
}
return arr;
};
var arrayToAscii = function (arr) {
var res = [];
for (var i = 0; i < arr.length; i++) {
res.push(String.fromCharCode(arr[i]));
}
return res.join("");
};
Tinytest.add("base64 - testing the test", function (test) {
test.equal(arrayToAscii(asciiToArray("The quick brown fox jumps over the lazy dog")),
"The quick brown fox jumps over the lazy dog");
});
Tinytest.add("base64 - empty", function (test) {
test.equal(Meteor._base64Encode(new Uint8Array(new ArrayBuffer(0))), "");
test.equal(Meteor._base64Decode(""), new Uint8Array(new ArrayBuffer(0)));
});
Tinytest.add("base64 - wikipedia examples", function (test) {
var tests = [
{txt: "pleasure.", res: "cGxlYXN1cmUu"},
{txt: "leasure.", res: "bGVhc3VyZS4="},
{txt: "easure.", res: "ZWFzdXJlLg=="},
{txt: "asure.", res: "YXN1cmUu"},
{txt: "sure.", res: "c3VyZS4="}
];
_.each(tests, function(t) {
test.equal(Meteor._base64Encode(asciiToArray(t.txt)), t.res);
test.equal(arrayToAscii(Meteor._base64Decode(t.res)), t.txt);
});
});
})();