total.js
Version:
MVC framework for Node.js
716 lines (570 loc) • 16 kB
JavaScript
// Copyright 2018-2020 (c) Peter Širka <petersirka@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
/**
* @module NoSQL Stream
* @version 1.2.0
*/
require('./index');
const Fs = require('fs');
const BUFFERSIZE = 1024 * 32;
const BUFFERDOCS = 15;
const NEWLINEBUFFER = Buffer.from('\n', 'utf8');
const DEFSTATS = { size: 0 };
function NoSQLStream(filename) {
this.filename = filename;
this.fd = null;
this.stats = DEFSTATS;
this.type = null;
this.bytesread = 0;
this.ticks = 0;
this.position = 0;
this.cache = [null, null];
this.buffer = null;
this.divider = ',';
this.remchar = '-';
this.buffercount = BUFFERDOCS;
this.buffersize = BUFFERSIZE;
this.linesize = 0;
this.start = 0;
this.indexer = 0;
// this.canceled = false;
// this.docs = '';
// this.docscount = 0;
}
const NoSQLStreamProto = NoSQLStream.prototype;
// Because of performance
NoSQLStreamProto.readhelpers = function() {
var self = this;
self.cb_read = function() {
self.read();
};
self.cb_readbuffer = function(err, size, chunk) {
self.position += size;
var beg = 0;
if (self.buffer) {
self.cache[0] = self.buffer;
self.cache[1] = chunk;
beg = self.buffer.length - 1;
if (beg < 0)
beg = 0;
self.buffer = Buffer.concat(self.cache);
} else
self.buffer = chunk;
var index = self.buffer.lastIndexOf(NEWLINEBUFFER);
if (index === -1) {
self.read();
return;
}
var tmp = self.buffer.toString('utf8', 0, index).split('\n');
for (var i = 0; i < tmp.length; i++) {
if (tmp[i][0] !== self.remchar) {
if (self.array)
self.docs.push(tmp[i]);
else
self.docs += (self.docs ? self.divider : '') + tmp[i];
self.docscount++;
self.indexer++;
}
}
self.buffer = self.buffer.slice(index + 1);
if (self.ondocuments() === false)
self.canceled = true;
self.docs = self.array ? [] : '';
self.docscount = 0;
self.ticks++;
if (self.ticks % 5 === 0)
setImmediate(self.cb_readticks);
else
self.read();
};
self.cb_readticks = function() {
self.read();
};
self.cb_readreverse = function() {
self.readreverse2();
};
self.cb_readreversebuffer = function(err, size, chunk) {
self.bytesread += size;
if (self.buffer) {
self.cache[0] = chunk;
self.cache[1] = self.buffer;
self.buffer = Buffer.concat(self.cache);
} else
self.buffer = chunk;
var index = self.buffer.indexOf(NEWLINEBUFFER);
if (index === -1) {
self.readreverse2();
return;
}
var tmp = self.buffer.toString('utf8', index).trim().split('\n');
for (var i = 0; i < tmp.length; i++) {
if (tmp[i][0] !== self.remchar) {
if (self.array)
self.docs.push(tmp[i]);
else
self.docs += (self.docs ? self.divider : '') + tmp[i];
self.docscount++;
self.indexer++;
}
}
if (self.ondocuments() === false)
self.canceled = true;
// self.buffer = self.buffer.slice(0, index + 1);
self.buffer = self.buffer.slice(0, index + 1);
self.docs = self.array ? [] : '';
self.docscount = 0;
self.ticks++;
if (self.ticks % 5 === 0)
setImmediate(self.cb_readreverseticks);
else
self.readreverse2();
};
self.cb_readreverseticks = function() {
self.readreverse2();
};
self.cb_readstream = function(chunk) {
if (self.canceled)
return;
var beg = 0;
if (self.buffer) {
self.cache[0] = self.buffer;
self.cache[1] = chunk;
self.buffer = Buffer.concat(self.cache);
beg = self.cache[0].length - 1;
if (beg < 0)
beg = 0;
} else
self.buffer = chunk;
var index = self.buffer.lastIndexOf(NEWLINEBUFFER);
if (index === -1)
return;
var tmp = self.buffer.toString('utf8', 0, index).split('\n');
for (var i = 0; i < tmp.length; i++) {
if (tmp[i][0] !== self.remchar) {
if (self.array)
self.docs.push(tmp[i]);
else
self.docs += (self.docs ? self.divider : '') + tmp[i];
self.docscount++;
self.indexer++;
}
}
self.buffer = self.buffer.slice(index + 1);
if (self.ondocuments() === false)
self.canceled = true;
if (self.canceled) {
self.stream.destroy && self.stream.destroy();
} else {
self.docs = self.array ? [] : '';
self.docscount = 0;
self.ticks++;
}
};
};
// Because of performance
NoSQLStreamProto.writehelpers = function() {
var self = this;
self.cb_writeAddUpdAdd = function(err, size) {
if (err) {
console.log('ERROR --> NoSQLstream.writer (add)', err);
self.canceled = true;
self.bufferstacknew.length = 0;
self.bufferstack.length = 0;
self.writing = false;
} else {
self.positionappend += size;
var item = self.bufferstack.shift();
Fs.write(self.fd, item.data, item.position, 'utf8', self.cb_writeAddUpdUpd);
}
};
self.cb_writeAddUpdUpd = function(err) {
self.writing = false;
if (err) {
console.log('ERROR --> NoSQLstream.writer (upd)', err);
self.canceled = true;
self.bufferstack.length = 0;
self.bufferstacknew.length = 0;
} else
self.$write();
};
self.cb_writeAdd = function(err, size) {
self.writing = false;
self.positionappend += size;
if (err) {
console.log('ERROR --> NoSQLstream.writer (add)', err);
self.canceled = true;
} else
self.$write();
};
self.cb_writeUpd = function(err) {
self.writing = false;
if (err) {
console.log('ERROR --> NoSQLstream.writer (upd)', err);
self.canceled = true;
} else
self.$write();
};
self.cb_flush = function() {
self.flush();
};
self.cb_readwritebuffer2 = function(err, size, chunk) {
self.position += size;
var beg = 0;
var index;
if (self.buffer) {
self.cache[0] = self.buffer;
self.cache[1] = chunk;
beg = self.buffer.length - 1;
if (beg < 0)
beg = 0;
self.buffer = Buffer.concat(self.cache);
} else
self.buffer = chunk;
if (self.linesize) {
while (self.buffer.length >= self.linesize) {
var tmp = self.buffer.toString('utf8', 0, self.linesize - 1);
if (self.array)
self.docs.push(tmp);
else
self.docs += (self.docs ? self.divider : '') + tmp;
self.docsbuffer.push({ length: self.linesize - 1, doc: tmp, position: self.positionupdate });
self.docscount++;
if (self.docsbuffer.length >= self.buffercount) {
if (self.ondocuments() === false)
self.canceled = true;
self.docsbuffer = [];
self.docs = self.array ? [] : '';
if (self.canceled)
break;
}
self.positionupdate += self.linesize;
self.buffer = self.buffer.slice(self.linesize);
}
} else {
index = self.buffer.indexOf(NEWLINEBUFFER, beg);
while (index !== -1) {
var tmp = self.buffer.toString('utf8', 0, index);
if (tmp[0] !== self.remchar) {
if (self.array)
self.docs.push(tmp);
else
self.docs += (self.docs ? self.divider : '') + tmp;
self.docsbuffer.push({ length: index, doc: tmp, position: self.positionupdate });
self.docscount++;
if (self.docsbuffer.length >= self.buffercount) {
if (self.ondocuments() === false)
self.canceled = true;
self.docsbuffer = [];
self.docs = self.array ? [] : '';
if (self.canceled)
break;
}
}
self.positionupdate += Buffer.byteLength(tmp, 'utf8') + 1;
self.buffer = self.buffer.slice(index + 1);
index = self.buffer.indexOf(NEWLINEBUFFER);
if (index === -1)
break;
}
}
if (self.bufferstack.length || self.bufferstacknew.length)
setImmediate(self.cb_writeticks);
else
self.readupdate();
};
self.cb_readwritebuffer = function(err, size, chunk) {
self.position += size;
var beg = 0;
var index;
if (self.buffer) {
self.cache[0] = self.buffer;
self.cache[1] = chunk;
beg = self.buffer.length - 1;
if (beg < 0)
beg = 0;
self.buffer = Buffer.concat(self.cache);
} else
self.buffer = chunk;
var index = self.buffer.lastIndexOf(NEWLINEBUFFER);
if (index === -1) {
self.readupdate();
return;
}
var tmp = self.buffer.toString('utf8', 0, index).split('\n');
for (var i = 0; i < tmp.length; i++) {
var size = Buffer.byteLength(tmp[i], 'utf8');
if (tmp[i][0] !== self.remchar) {
if (self.array)
self.docs.push(tmp[i]);
else
self.docs += (self.docs ? self.divider : '') + tmp[i];
self.docsbuffer.push({ length: size, doc: tmp[i], position: self.positionupdate });
self.docscount++;
self.indexer++;
}
self.positionupdate += size + 1;
}
if (self.ondocuments() === false)
self.canceled = true;
self.docsbuffer = [];
self.docs = self.array ? [] : '';
self.buffer = self.buffer.slice(index + 1);
if (self.bufferstack.length || self.bufferstacknew.length)
setImmediate(self.cb_writeticks);
else
self.readupdate();
};
self.cb_writeticks = function() {
if (self.bufferstack.length || self.bufferstacknew.length)
setImmediate(self.cb_writeticks);
else
self.readupdate();
};
};
NoSQLStreamProto.openread = function() {
var self = this;
self.type = 'r';
self.position = self.start;
self.open();
return self;
};
NoSQLStreamProto.openreadreverse = function() {
var self = this;
self.type = 'r';
self.position = self.start;
self.$reverse = true;
self.open();
return self;
};
NoSQLStreamProto.openupdate = function() {
var self = this;
self.type = 'r+';
F.stats.performance.open++;
Fs.open(self.filename, self.type, function(err, fd) {
if (err) {
self.$callback(err);
return;
}
Fs.fstat(fd, function(err, stats) {
self.docs = self.array ? [] : '';
self.docscount = 0;
if (err) {
Fs.close(fd, NOOP);
self.$callback(err);
return;
}
self.docs = self.array ? [] : '';
self.docscount = 0;
self.fd = fd;
self.stats = stats;
self.position = self.start;
self.positionappend = self.stats.size;
self.positionupdate = self.start;
self.bufferstack = [];
self.bufferstacknew = [];
self.docsbuffer = [];
self.writehelpers();
self.readupdate();
});
});
return self;
};
// For e.g. files on URL address
NoSQLStreamProto.openstream = function(stream) {
var self = this;
var close = function() {
if (self.docscount) {
self.ondocuments();
self.docscount = 0;
self.docs = self.array ? [] : '';
}
self.$callback && self.$callback();
self.$callback = null;
};
self.docs = self.array ? [] : '';
self.docscount = 0;
self.readhelpers();
self.stream = stream;
self.stream.on('error', close);
self.stream.on('end', close);
self.stream.on('data', self.cb_readstream);
return self;
};
NoSQLStreamProto.open = function() {
var self = this;
F.stats.performance.open++;
Fs.open(self.filename, self.type, function(err, fd) {
if (err) {
self.$callback(err);
return;
}
Fs.fstat(fd, function(err, stats) {
self.docs = self.array ? [] : '';
self.docscount = 0;
self.fd = fd;
self.stats = stats;
self.position = self.start;
if (err) {
Fs.close(fd, NOOP);
self.$callback(err);
return;
}
self.readhelpers();
if (self.$reverse)
self.readreverse();
else
self.read();
});
});
};
NoSQLStreamProto.close = function() {
var self = this;
if (self.fd) {
self.stream = null;
Fs.close(self.fd, function(err) {
err && F.error(err);
self.$callback && self.$callback();
});
if (self.buffer) {
self.buffer = null;
self.cache[0] = null;
self.cache[1] = null;
self.bytesread = 0;
}
self.canceled = false;
self.fd = null;
self.type = null;
self.docscache = null;
self.docs = null;
} else if (self.$callback)
self.$callback && self.$callback();
return self;
};
NoSQLStreamProto.write = function(doc, position) {
var self = this;
self.bufferstack.push({ position: position, data: doc });
!self.writing && self.$write();
return self;
};
NoSQLStreamProto.write2 = function(doc) {
var self = this;
self.bufferstacknew.push(Buffer.from(doc));
!self.writing && self.$write();
return self;
};
NoSQLStreamProto.$write = function() {
var self = this;
if (self.bufferstacknew.length && self.bufferstack.length) {
self.writing = true;
var buf = self.bufferstacknew.splice(0, 5);
buf = buf.length > 1 ? Buffer.concat(buf) : buf[0];
Fs.write(self.fd, buf, 0, buf.length, self.positionappend, self.cb_writeAddUpdAdd);
} else if (self.bufferstacknew.length) {
self.writing = true;
var buf = self.bufferstacknew.splice(0, 5);
buf = buf.length > 1 ? Buffer.concat(buf) : buf[0];
Fs.write(self.fd, buf, 0, buf.length, self.positionappend, self.cb_writeAdd);
} else if (self.bufferstack.length) {
self.writing = true;
var item = self.bufferstack.shift();
Fs.write(self.fd, item.data, item.position, 'utf8', self.cb_writeUpd);
}
};
NoSQLStreamProto.flush = function() {
var self = this;
if (self.writing)
setTimeout(self.cb_flush, 100);
else
self.close();
return self;
};
NoSQLStreamProto.read = function() {
var self = this;
var size = self.stats.size - self.position;
if (!self.fd || size <= 0 || self.canceled) {
if (!self.canceled && self.buffer && self.buffer.length) {
self.cb_readbuffer(null, 1, NEWLINEBUFFER);
self.buffer = Buffer.alloc(0);
return;
}
if (self.docscount) {
self.ondocuments();
self.docscount = 0;
self.docs = self.array ? [] : '';
}
self.close();
} else {
size = size < self.buffersize ? size : self.buffersize;
var buffer = Buffer.alloc(size);
Fs.read(self.fd, buffer, 0, size, self.position, self.cb_readbuffer);
}
};
NoSQLStreamProto.readreverse = function() {
var self = this;
self.position = self.stats.size;
self.readreverse2();
return self;
};
NoSQLStreamProto.readreverse2 = function() {
var self = this;
if (!self.fd || self.position <= self.start || self.canceled) {
if (!self.canceled && self.buffer && self.buffer.length) {
self.cb_readreversebuffer(null, 1, NEWLINEBUFFER);
self.buffer = Buffer.alloc(0);
return;
}
if (self.docscount) {
self.ondocuments();
self.docs = self.array ? [] : '';
self.docscount = 0;
}
self.close();
} else {
var size = self.stats.size - self.bytesread;
size = size < self.buffersize ? size : self.buffersize;
self.position -= size;
var buffer = Buffer.alloc(size);
Fs.read(self.fd, buffer, 0, size, self.position, self.cb_readreversebuffer);
}
};
NoSQLStreamProto.readupdate = function() {
var self = this;
var size = self.stats.size - self.position;
if (!self.fd || size <= 0 || self.canceled) {
if (!self.canceled && self.buffer && self.buffer.length) {
self.positionappend++;
self.cb_readwritebuffer(null, 1, NEWLINEBUFFER);
self.buffer = Buffer.alloc(0);
return;
}
if (self.docsbuffer.length) {
self.ondocuments();
self.docsbuffer = [];
self.docs = self.array ? [] : '';
}
self.flush();
} else {
size = size < self.buffersize ? size : self.buffersize;
var buffer = Buffer.alloc(size);
Fs.read(self.fd, buffer, 0, size, self.position, self.cb_readwritebuffer);
}
};
module.exports = NoSQLStream;