Skip to content

Commit

Permalink
Support undefined values and array holes.
Browse files Browse the repository at this point in the history
  • Loading branch information
benjamn committed Jan 29, 2016
1 parent c96a609 commit a62a460
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 10 deletions.
50 changes: 42 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
var UNDEFINED_INDEX = -1;
var ARRAY_HOLE_INDEX = -2;

function encode(value) {
return JSON.stringify(tabulate(value));
}
Expand All @@ -8,6 +11,11 @@ function tabulate(value) {
var indexMap = typeof Map === "function" && new Map;

function getIndex(value) {
if (typeof value === "undefined") {
// An out-of-bounds array access always returns undefined!
return UNDEFINED_INDEX;
}

var index;

if (indexMap) {
Expand All @@ -31,17 +39,38 @@ function tabulate(value) {
// Assign the root value to values[0].
getIndex(value);

for (var v = 0; v < values.length; ++v) {
value = values[v];
function copy(value) {
var result = value;

if (value && typeof value === "object") {
var copy = table[v] = Array.isArray(value) ? [] : {};
Object.keys(value).forEach(function (key) {
copy[key] = getIndex(value[key]);
var keys = Object.keys(value);

if (Array.isArray(value)) {
result = Array(value.length);
var len = value.length;
if (len > keys.length) {
// The array has holes, so make sure we fill them with the
// ARRAY_HOLE_INDEX constant.
for (var i = 0; i < len; ++i) {
result[i] = ARRAY_HOLE_INDEX;
}
}
} else {
result = {};
}

keys.forEach(function (key) {
result[key] = getIndex(value[key]);
});
} else {
table[v] = value;
}

return result;
}

// Note that this for loop cannot be a forEach loop, because
// values.length is expected to change during iteration.
for (var v = 0; v < values.length; ++v) {
table[v] = copy(values[v]);
}

return table;
Expand All @@ -53,7 +82,12 @@ function decode(encoding) {
table.forEach(function (entry) {
if (entry && typeof entry === "object") {
Object.keys(entry).forEach(function (key) {
entry[key] = table[entry[key]];
var index = entry[key];
if (index === ARRAY_HOLE_INDEX) {
delete entry[key];
} else {
entry[key] = table[index];
}
});
}
});
Expand Down
18 changes: 16 additions & 2 deletions test/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var arson = require("../index.js");
describe("encoding and decoding", function () {
it("should work with primitive values", function () {
function check(value) {
assert.strictEqual(arson.decode(arson.encode(value)), value);
assert.deepEqual(value, arson.decode(arson.encode(value)));
}

check(0);
Expand All @@ -14,12 +14,26 @@ describe("encoding and decoding", function () {
check("asdf");
check("");
check(null);
check(void 0);
check({ foo: void 0 });

// TODO It would be nice if these cases worked:
// check(void 0);
// check(/asdf/);
});

it("should work for sparse arrays", function () {
function check(array) {
assert.deepEqual(array, arson.decode(arson.encode(array)));
}

check([,]);
check([,,]);
check([,,,]);
check([1,,3]);
check([1,,3,,4]);
check([1,,3,,4,,]);
});

it("should work with circular references", function () {
var obj = {};
obj.self = obj;
Expand Down

0 comments on commit a62a460

Please sign in to comment.