Skip to content

Commit

Permalink
Implement more complete StringStreamReader.add method
Browse files Browse the repository at this point in the history
  • Loading branch information
retorillo committed Jan 17, 2017
1 parent 25666ce commit 92b15ea
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 37 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules
coverage
.swp
dummy.txt
80 changes: 59 additions & 21 deletions stream/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,29 @@
var fs = require('fs');
var _stream = new WeakMap();
var _lastread = new WeakMap();
var _relays = new WeakMap();
var _end = new WeakMap();

class StringStreamReader {
constructor(stream) {
if (typeof(stream) === 'string')
_stream.set(this, fs.createReadStream(stream, { encoding: 'utf-8' }));
else
_stream.set(this, stream);
stream = fs.createReadStream(stream, { encoding: 'ucs2' });
_stream.set(this, stream);
var relays = [];
_relays.set(this, relays);
_lastread.set(this, "");
_end.set(this, false);
stream.on('readable', function() {
relays.forEach(r => {
r.readable();
});
});
stream.on('end', () => {
_end.set(this, true);
relays.forEach(r => {
r.end();
});
});
}
read(count, errorThrown) {
var mapkey = this;
Expand All @@ -29,26 +44,49 @@ class StringStreamReader {
return;
}
readBlocks.appendBlock(lastread);
var callback = () => {
var block = stream.read(count);
if (block == null && errorThrown) {
reject(errorThrown);
var unregister = function() {
var relays = _relays.get(mapkey);
var i = relays.indexOf(relay);
if (i != -1)
relays.splice(i, 1);
}
var tryresolve = function(force) {
var result = readBlocks.join('');
if (!force && readBlocks.totalRead < count)
return;
_lastread.set(mapkey, result.substr(count));
unregister();
var result = result.substr(0, count);
resolve(count > 0 && result.length == 0 ? null : result);
}
var tryreject = function(error) {
unregister();
reject(error);
}
var tryread = function() {
if (_end.get(mapkey)) {
tryresolve(true);
return;
}
if (block == null) {
_lastread.set(mapkey, "");
resolve(readBlocks.join(''));
}
var totalRead = readBlocks.appendBlock(block);
if (totalRead >= count) {
var result = readBlocks.join('');
_lastread.set(mapkey, result.substr(count));
resolve(result.substr(0, count));
}
else
stream.once('readable', callback);
};
stream.once('readable', callback);
var block = stream.read(count);
if (block == null)
return; // end of stream or unreadable state
readBlocks.appendBlock(block);
tryresolve(false);
}
var relay = {
readable: function() {
tryread();
},
end: function() {
if (errorThrown)
tryreject(errorThrown);
else
tryresolve(true);
},
}
_relays.get(mapkey).push(relay);
tryread();
});
}
}
Expand Down
67 changes: 51 additions & 16 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,77 @@ const StringStreamReader = require('../stream');
const should = require('should');
const fs = require('fs');

function stringToHex(str) {
if (str == null) return null;
var hex = [];
for (var c = 0; c < str.length; c++)
hex.push(str.charCodeAt(c).toString(16).toUpperCase());
return hex.join('|');
}

function makestr(start, length) {
var str = [];
for (var c = 0; c < length; c++)
for (var c = 0; start + c < 0xffff && c < length; c++)
str.push(String.fromCharCode(start + c));
return str.join('');
}

describe("stream", function() {
describe("StringStreamReader", function() {
before(function() {
return new Promise(function(resolve, reject) {
var s = fs.createWriteStream('dummy.txt');
var s = fs.createWriteStream('dummy.txt', { encoding: 'ucs2' });
for (var c = 0; c < 0xffff; c++)
s.write(String.fromCharCode(c));
s.end(function() {
s.close(resolve);
});
});
});
it ("can be read from file", function() {
it ("can read single block", function() {
var s = new StringStreamReader('dummy.txt');
var offset = 0;
var readc = 2048;
return s.read(readc).then(function(data){
should(data).eql(makestr(offset, readc));
});
});
var readToEndStream;
it ("can read to end", function() {
return new Promise(function(resolve, reject) {
var s = new StringStreamReader('dummy.txt');
readToEndStream = s;
var offset = 0;
var readc = 2048;
var offset = 0;
var read = function() {
s.read(readc).then(function(data) {
if (data == null) {
should(offset).eql(0xffff);
resolve();
return;
}
should(stringToHex(data)).eql(stringToHex(makestr(offset, readc)));
offset += data.length;
read();
}).catch(function(e) {
reject(e);
});
}
read();
});
});
it ("should return null after end to read", function() {
return readToEndStream.read(1024).then(function(data) {
should(stringToHex(data)).eql(null);
});
});
});

describe("read", function() {
describe("StringReader", function() {
const delim = ',';
const testSentence = 'The quick brown fox jumps over the lazy dog';
const testWords = testSentence.split(' ');
it ("read (forward)", function () {
it ("can read forward", function () {
const reader = new StringReader(testSentence);
for (var c = 1; c <= testSentence.length; c++) {
var expectedList = [];
Expand All @@ -49,7 +87,7 @@ describe("read", function() {
should(expectedList.join('')).equal(testSentence);
}
});
it ("read (backward)", function() {
it ("can read backward", function() {
const reader = new StringReader(testSentence);
for (var c = 1; c <= testSentence.length; c++) {
var expectedList = [];
Expand All @@ -67,7 +105,7 @@ describe("read", function() {
});
['\r', '\n', '\r\n'].forEach((lineEnding) => {
const reader = new StringReader();
var itmsg = "readLine (" + lineEnding.replace('\r', 'CR').replace('\n', 'LF') + ")";
var itmsg = "can readLine (" + lineEnding.replace('\r', 'CR').replace('\n', 'LF') + ")";
it (itmsg, function() {
const testLinesArray = [];

Expand All @@ -86,7 +124,7 @@ describe("read", function() {
should(reader.end).equal(true, 'must be end of string (readToEnd)')
});
});
it ("readTo (single line content)", function () {
it ("can readTo (single line content)", function () {
const reader = new StringReader(testWords.join(delim));
var readCount = 0;
var holdDelim = true;
Expand All @@ -95,25 +133,22 @@ describe("read", function() {
should.equal(read, testWords[readCount++] + (holdDelim ? delim : ''));
}
});
it("readTo (multiline content)", function() {
it("can readTo (multiline content)", function() {
const testScript = "else{\nreturn true;\n}"
const reader = new StringReader(testScript);
should(reader.readTo(/{/).toString()).equal('else');
should(reader.readTo(/}/).toString()).equal('\nreturn true;\n');
should(reader.end).equal(true, 'must be end of string');
});
it ("readTo (index of delimiter)", function() {
it ("can readTo (index of delimiter)", function() {
const testScript = "else{\nreturn true;\n}"
const reader = new StringReader(testScript);
reader.position = 0;
reader.readTo(/{/);
var delimiterTest = reader.readTo(/}/);
should(delimiterTest.delimiter.index).equal(testScript.indexOf('}'));
});
});

describe("push, pop", function() {
it("with index recovery", function () {
it("can push and pop with index-recovery", function () {
var reader = new StringReader('The quick brown fox jumps over the lazy dog.');
reader.readTo(/\s+/);
should(reader.position).equal('The '.length);
Expand All @@ -124,7 +159,7 @@ describe("push, pop", function() {
should(reader.pop()).equal("quick brown fox ");
should(reader.position).equal('The '.length);
});
it("without index recovery", function () {
it("can push and pop without index recovery", function () {
var reader = new StringReader('The quick brown fox jumps over the lazy dog.');
reader.readTo(/\s+/);
should(reader.position).equal('The '.length);
Expand Down

0 comments on commit 92b15ea

Please sign in to comment.