Comparing version 0.2.3 to 0.2.4
@@ -5,28 +5,16 @@ var _ = require('underscore'); | ||
function findLast(path, character, from) { | ||
/* | ||
* Finds the last occurrence of the character supplied within the file | ||
* specified. While searching white space characters are ignored. | ||
* | ||
* path: exact filepath that points to the file on disk | ||
* character: the character you're looking for | ||
* from: location to start searching from | ||
* | ||
* Returns: | ||
* >=0: which is the location of the character you wanted | ||
* -1: couldn't find the specific character and ended up finding another | ||
* non whitespace character | ||
* -2: couldn't find the character since the file contained nothing | ||
* other than whitespace characters | ||
*/ | ||
var CHAR_NOT_FOUND = -1; | ||
var EMPTY_FILE = -2; | ||
var READ_SIZE = 1024; | ||
function findLast(fd, stats, character, from) { | ||
var skip = from ? (stats.size - from) : 0; | ||
function searchFor(fd, stats, totalBytesRead, character) { | ||
var chunkSize = 1024; | ||
var skip = (stats.size - from) || 0; | ||
var buffer = new Buffer(chunkSize); | ||
var index = 0; | ||
var buffer = new Buffer(READ_SIZE); | ||
if (totalBytesRead < stats.size) { | ||
var position = stats.size - totalBytesRead - chunkSize; | ||
return fs.readAsync(fd, buffer, 0, chunkSize, position) | ||
var position = Math.max(0, stats.size - totalBytesRead - READ_SIZE); | ||
return fs.readAsync(fd, buffer, 0, READ_SIZE, position) | ||
.then(function(bytesRead) { | ||
@@ -38,4 +26,4 @@ var chunk = buffer.slice(0, bytesRead).toString(); | ||
// non whitespace is found. | ||
for (index = bytesRead - 1; index >= 0; index--) { | ||
if (skip > 0) { | ||
for (var index = bytesRead - 1; index >= 0; index--) { | ||
if (skip > 0 ) { | ||
skip--; | ||
@@ -50,8 +38,9 @@ continue; | ||
if (chunk[index] === character) { | ||
return Promise.resolve(stats.size - (totalBytesRead + (bytesRead - index))); | ||
return stats.size - (totalBytesRead + (bytesRead - index)); | ||
} | ||
return Promise.resolve(-1); | ||
return CHAR_NOT_FOUND; | ||
} | ||
return searchFor(fd, stats, totalBytesRead + bytesRead, character); | ||
return searchFor(fd, stats, totalBytesRead + bytesRead); | ||
}); | ||
@@ -61,15 +50,6 @@ } | ||
// If the whole file is empty or comprised of simply whitespace | ||
return Promise.resolve(-2); | ||
return Promise.resolve(EMPTY_FILE); | ||
} | ||
return fs.openAsync(path, 'r') | ||
.then(function(fd) { | ||
return fs.fstatAsync(fd) | ||
.then(function(stats) { | ||
return searchFor(fd, stats, 0, character); | ||
}) | ||
.finally(function() { | ||
return fs.closeAsync(fd); | ||
}); | ||
}); | ||
return searchFor(fd, stats, 0, character); | ||
} | ||
@@ -91,36 +71,40 @@ | ||
*/ | ||
options = _.extend({ | ||
replacer: null, | ||
space: 4 | ||
}, options); | ||
function stringify(object) { | ||
return JSON.stringify(object, options.replacer, options.space); | ||
} | ||
return fs.statAsync(filename) | ||
.catch(function(err) { | ||
if (err.toString().match(/ENOENT/)) { | ||
return fs.writeFileAsync(filename, '[]'); | ||
return fs.writeFileAsync(filename, '[]') | ||
.then(function() { | ||
return fs.statAsync(filename); | ||
}); | ||
} else { | ||
return Promise.reject(err); | ||
throw err; | ||
} | ||
}) | ||
.then(function() { | ||
.then(function(stats) { | ||
if (element.length === 0) { | ||
// nothing to write | ||
return Promise.resolve(); | ||
return; | ||
} | ||
options = _.extend({ | ||
replacer: null, | ||
space: 4 | ||
}, options); | ||
function stringify(object) { | ||
return JSON.stringify(object, options.replacer, options.space); | ||
} | ||
var output; | ||
var offset = 0; | ||
return fs.openAsync(filename, 'r+') | ||
.then(function(fd) { | ||
return findLast(filename, ']') | ||
return findLast(fd, stats, ']', 0) | ||
.then(function(lastSquareBracket) { | ||
if (lastSquareBracket === -1) { | ||
return Promise.reject(new Error(filename + ' not a valid JSON format')); | ||
if (lastSquareBracket === CHAR_NOT_FOUND) { | ||
throw new Error(filename + ' not a valid JSON format'); | ||
} | ||
var elementString; | ||
if (element instanceof Array) { | ||
if (_.isArray(element)) { | ||
elementString = stringify(element); | ||
@@ -133,10 +117,7 @@ elementString = elementString.slice(1, | ||
var output; | ||
if (lastSquareBracket === -2) { | ||
output = '['; | ||
output += elementString + ']'; | ||
return fs.writeAsync(fd, output); | ||
if (lastSquareBracket === EMPTY_FILE) { | ||
output = '[' + elementString + ']'; | ||
} else { | ||
// If we can't find a } then this is a file with simply [] in it | ||
return findLast(filename, '}', lastSquareBracket) | ||
return findLast(fd, stats, '}', lastSquareBracket) | ||
.then(function(lastCurlyBracket) { | ||
@@ -149,8 +130,11 @@ if (lastCurlyBracket > 0) { | ||
output += elementString + ']'; | ||
return fs.writeAsync(fd, output, lastSquareBracket); | ||
offset = lastSquareBracket; | ||
}); | ||
} | ||
}) | ||
.finally(function() { | ||
return fs.closeAsync(fd); | ||
.then(function() { | ||
return fs.writeAsync(fd, output, offset) | ||
.finally(function() { | ||
return fs.closeAsync(fd); | ||
}); | ||
}); | ||
@@ -157,0 +141,0 @@ }); |
{ | ||
"name": "jsoner", | ||
"version": "0.2.3", | ||
"version": "0.2.4", | ||
"description": "Simple, fast, minimalist JSON library for node", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -8,4 +8,4 @@ var expect = require('chai').expect; | ||
it('appends 20,000 small JSON objects, one by one', function(done) { | ||
var iterations = 20000; | ||
it('appends 50,000 small JSON objects, one by one', function(done) { | ||
var iterations = 50000; | ||
var tmpFilename = tmp.tmpNameSync(); | ||
@@ -12,0 +12,0 @@ |
@@ -26,3 +26,3 @@ var _ = require('underscore'); | ||
it('fails to append to a file in an inexistent path', function() { | ||
return jsoner.appendFile('/no/mans/land/foo.juttle') | ||
return jsoner.appendFile('/no/mans/land/foo.juttle', {}) | ||
.then(function() { | ||
@@ -32,3 +32,3 @@ throw Error('previous statement should have failed'); | ||
.catch(function(err) { | ||
expect(err.toString()).to.match(/ENOENT. no such file or directory/); | ||
expect(err.toString()).to.match(/ENOENT/); | ||
}); | ||
@@ -143,3 +143,3 @@ }); | ||
var objects = []; | ||
for (var index = 0; index < 1024; index++) { | ||
for (var index = 0; index < 10; index++) { | ||
objects.push({ | ||
@@ -150,2 +150,3 @@ foo: 'bar', | ||
} | ||
return fs.writeFileAsync(tmpFilename, '[]') | ||
@@ -164,17 +165,6 @@ .then(function() { | ||
.then(function() { | ||
return new Promise(function(resolve, reject) { | ||
var stream = fs.createReadStream(tmpFilename); | ||
var processed = 0; | ||
jsoner.parse(stream) | ||
.on('object', function(object) { | ||
expect(object).to.deep.equal({ foo: 'bar', index: processed }); | ||
processed++; | ||
}) | ||
.on('end', function() { | ||
expect(processed).to.equal(1024); | ||
resolve(); | ||
}) | ||
.on('error', function(err) { | ||
reject(err); | ||
}); | ||
return fs.readFileAsync(tmpFilename) | ||
.then(function(data) { | ||
var jsonData = JSON.parse(data.toString()); | ||
expect(jsonData).to.deep.equal(objects); | ||
}); | ||
@@ -181,0 +171,0 @@ }); |
34219
17
847