UNPKG

caprese

Version:
688 lines (637 loc) 21.4 kB
// Generated by CoffeeScript 1.7.1 (function() { var Caprese, DB_MAX_SIZE, DB_MIN_SIZE, DEFAULT_SIZE, ENTRY_DATE_LENGTH, ENTRY_HEADER_LENGTH, ENTRY_LENGTH_LENGTH, ENTRY_TYPE_LENGTH, EventEmitter, HEADER_SIZE, MAX_MESSAGE_SIZE, NOOP, Query, fs, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; fs = require('fs'); Query = require('./query'); EventEmitter = require('events').EventEmitter; HEADER_SIZE = 12; ENTRY_TYPE_LENGTH = 1; ENTRY_DATE_LENGTH = 4; ENTRY_LENGTH_LENGTH = 2; ENTRY_HEADER_LENGTH = ENTRY_TYPE_LENGTH + ENTRY_DATE_LENGTH + ENTRY_LENGTH_LENGTH; DB_MIN_SIZE = ENTRY_HEADER_LENGTH; DB_MAX_SIZE = 4294967295; MAX_MESSAGE_SIZE = 65535; NOOP = function() {}; DEFAULT_SIZE = 1024 * 1024; Caprese = (function(_super) { __extends(Caprese, _super); Caprese.prototype.fd = null; Caprese.prototype.db = null; Caprese.prototype.size = 0; Caprese.prototype.index = null; Caprese.prototype.first = null; Caprese.prototype.lock = true; Caprese.prototype.queued = null; Caprese.prototype._closeOnDone = false; Caprese.INFO = 0x01; Caprese.ERROR = 0x03; Caprese.WARNING = 0x02; /* new Caprese() - create 1MB resident capped log new Caprese('./file.cap') - create 1MB capped log new Caprese('./file.cap', {size: 1024}) - create 1KB capped log new Caprese('./file.cap', {size: 1024}, function() {}) - create 1KB capped log and call a callback function new Caprese({size: 1024}) - create 1KB resident capped log new Caprese({size: 1024}, function() {}) - create 1KB resident capped log and call a callback function new Caprese(function() {}) - create 1MB resident capped log and call a callback function new Caprese('./file.cap', {size: 1024, resident: true}) - create 1KB resident capped log */ function Caprese() { var callback; Caprese.__super__.constructor.call(this); this.index = []; this.queued = []; if (arguments.length) { if (typeof arguments[0] === 'string') { this.file = arguments[0]; if (arguments.length > 1) { if (typeof arguments[1] === 'object') { this.options = arguments[1]; if (arguments.length > 2) { if (typeof arguments[2] === 'function') { callback = arguments[2]; if (arguments.length > 3) { throw new Error("Invalid arguments."); } } else { throw new Error("Invalid arguments."); } } else { callback = NOOP; } } else if (typeof arguments[1] === 'function') { this.options = { size: DEFAULT_SIZE }; callback = arguments[1]; } else { throw new Error("Invalid arguments."); } } else { this.options = { size: DEFAULT_SIZE }; callback = NOOP; } } else if (typeof arguments[0] === 'object') { this.options = arguments[0]; this.options.resident = true; if (arguments.length > 1) { if (typeof arguments[1] === 'function') { callback = arguments[1]; if (arguments.length > 2) { throw new Error("Invalid arguments."); } } else { throw new Error("Invalid arguments."); } } else { callback = NOOP; } } else if (typeof arguments[0] === 'function') { callback = arguments[0]; this.options = { size: DEFAULT_SIZE, resident: true }; if (arguments.length > 1) { throw new Error("Invalid arguments."); } } else { throw new Error("Invalid arguments."); } } else { this.options = { size: DEFAULT_SIZE, resident: true }; callback = NOOP; } if (arguments.length === 0) { this.options = { resident: true, size: 1024 * 1024 }; callback = NOOP; } else if (arguments.length === 1) { if (arguments[0] instanceof Function) { this.options = { resident: true, size: 1024 * 1024 }; callback = arguments[0]; } else if (typeof arguments[0] === 'string') { this.file = arguments[0]; this.options = { resident: true, size: 1024 * 1024 }; callback = NOOP; } } if (this.options instanceof Function) { callback = this.options; this.options = DEFAULT_OPTIONS; } if (this.options == null) { this.options = DEFAULT_OPTIONS; } if (callback == null) { callback = NOOP; } if (this.options.resident) { this.create(null, callback); } else { if (this.options.overwrite) { this.create(this.file, callback); } else { fs.exists(this.file, (function(_this) { return function(exist) { if (exist) { return fs.open(_this.file, 'r+', function(err, fd) { _this.fd = fd; if (err) { return callback(err); } return _this.load(callback); }); } else { return _this.create(_this.file, callback); } }; })(this)); } } } Caprese.prototype._diff = function(from, to) { if (to > from) { return to - from; } else if (to < from) { return to + (this.size - from); } else { return 0; } }; Caprese.prototype._move = function(offset, length) { var next; next = offset + length; if (length > 0) { if (next >= this.size) { next = next - this.size; } } else { if (next < 0) { next = this.size + next; } } return next; }; Caprese.prototype.add = function(type, date, message, callback) { var buffer, length; if (callback == null) { callback = NOOP; } if (arguments.length === 2) { message = date; date = new Date(); } if (arguments.length === 3) { if (message instanceof Function) { callback = message; message = date; date = new Date(); } } if (this.lock) { return this.queue(type, date, message, callback); } if (typeof message !== 'string') { message = String(message); } length = Buffer.byteLength(message, 'utf8'); if (length + ENTRY_HEADER_LENGTH > this.size) { return callback(new Error("Message (" + length + " bytes) is larger than db limit of " + (this.size - ENTRY_HEADER_LENGTH) + " bytes")); } if (length > MAX_MESSAGE_SIZE) { return callback(new Error("Message (" + length + " bytes) is larger than entry limit of " + MAX_MESSAGE_SIZE + " bytes")); } this.lock = true; buffer = new Buffer(length + ENTRY_HEADER_LENGTH); buffer.writeUInt8(type, 0); buffer.writeUInt32LE(Math.floor(date.getTime() / 1000), 1); buffer.writeUInt16LE(length, 5); if (length) { buffer.write(message, 7, length, 'utf8'); } return this.clear(buffer.length, (function(_this) { return function(err) { if (err) { _this.lock = false; return callback(err); } return _this.write(buffer, _this.cursor, function(err, nextbyte) { if (err) { _this.lock = false; return callback(err); } buffer = new Buffer(4); buffer.writeUInt32LE(nextbyte, 0); return _this._write(buffer, 0, 4, _this.size + 4, function(err) { if (err) { _this.lock = false; return callback(err); } _this.index.push({ offset: _this._move(_this.cursor, ENTRY_HEADER_LENGTH), length: length, type: type, date: date }); _this.cursor = nextbyte; _this.lock = false; _this.unqueue(); _this.emit('add', type, date, message); return typeof callback === "function" ? callback(null) : void 0; }); }); }; })(this)); }; Caprese.prototype.clear = function(length, callback) { var buffer, edit, first; if (!this.index.length) { return callback(null); } edit = false; while (this.index.length && this._diff(this.cursor, this._move(this.index[0].offset, -ENTRY_HEADER_LENGTH)) < length) { edit = true; this.index.shift(); } if (!edit) { return callback(null); } if (this.index.length) { first = this._move(this.index[0].offset, -ENTRY_HEADER_LENGTH); } else { first = this.cursor; } buffer = new Buffer(4); buffer.writeUInt32LE(first, 0); return this._write(buffer, 0, 4, this.size + 8, (function(_this) { return function(err) { if (err) { return callback(null); } _this.first = first; return callback(null); }; })(this)); }; Caprese.prototype.close = function(callback) { if (callback == null) { callback = NOOP; } if (this.lock) { this._closeOnDone = callback != null ? callback : true; } else { this.first = 0; this.cursor = 0; this.index = []; this.queued = []; this.lock = true; if (this.fd) { fs.close(this.fd, callback); return this.fd = null; } else if (this.db) { this.db = null; return setImmediate((function(_this) { return function() { return callback(null); }; })(this)); } } }; Caprese.prototype.count = function() { return this.index.length; }; Caprese.prototype._create = function(size, callback) { var buffer; buffer = new Buffer([0x00]); return this._write(buffer, 0, 1, 0, (function(_this) { return function(err) { if (err) { return callback(err); } buffer = new Buffer(HEADER_SIZE); buffer.fill(0x00); buffer.write('cap', 0, 3, 'ascii'); buffer.writeUInt8(2, 3); buffer.writeUInt32LE(0, 4); buffer.writeUInt32LE(0, 4); return _this._write(buffer, 0, HEADER_SIZE, size, function(err) { if (err) { return callback(err); } return _this.load(callback); }); }; })(this)); }; Caprese.prototype.create = function(file, callback) { var size; size = this.options.size - HEADER_SIZE; if (size < DB_MIN_SIZE) { return callback(new Error("Minimum size is " + (DB_MIN_SIZE + HEADER_SIZE))); } if (size > DB_MAX_SIZE) { return callback(new Error("Maximum size is " + (DB_MAX_SIZE + HEADER_SIZE))); } if (this.options.resident) { return setImmediate((function(_this) { return function() { var ex; try { _this.db = new Buffer(_this.options.size); return _this._create(size, callback); } catch (_error) { ex = _error; return callback(ex); } }; })(this)); } else { return fs.open(file, 'w+', (function(_this) { return function(err, fd) { _this.fd = fd; if (err) { return callback(err); } return _this._create(size, callback); }; })(this)); } }; Caprese.prototype.load = function(callback) { if (this.fd) { return fs.fstat(this.fd, (function(_this) { return function(err, stats) { var header; if (err) { return callback(err); } _this.size = stats.size - HEADER_SIZE; if (_this.size < DB_MIN_SIZE) { return callback(new Error("Invalid cap file (SIZE:" + stats.size + ")")); } header = new Buffer(HEADER_SIZE); return _this._read(header, 0, HEADER_SIZE, _this.size, function(err) { if (err) { return callback(err); } if (!(header[0] === 0x63 && header[1] === 0x61 && header[2] === 0x70)) { return callback(new Error("Invalid cap file (HEADER)")); } if (header[3] !== 0x02) { return callback(new Error("Invalid cap file (VERSION)")); } _this.cursor = header.readUInt32LE(4); if (_this.cursor >= _this.size) { return callback(new Error("Invalid cap file (CURSOR)")); } _this.first = header.readUInt32LE(8); if (_this.first >= _this.size) { return callback(new Error("Invalid cap file (FIRST)")); } return _this.reindex(function(err) { if (err) { return callback(err); } _this.lock = false; _this.unqueue(); return callback(null); }); }); }; })(this)); } else if (this.db) { return setImmediate((function(_this) { return function() { _this.size = _this.db.length - HEADER_SIZE; _this.cursor = 0; _this.first = 0; _this.lock = false; _this.unqueue(); return callback(null); }; })(this)); } else { return setImmediate(function() { return callback(new Error("Failed to load database")); }); } }; Caprese.prototype.queue = function(type, date, message, callback) { return this.queued.push({ type: type, date: date, message: message, callback: callback }); }; Caprese.prototype.unqueue = function() { var msg; if (this.queued.length) { msg = this.queued.shift(); return this.add(msg.type, msg.date, msg.message, msg.callback); } else { if (this._closeOnDone) { this.close(this._closeOnDone); } } }; Caprese.prototype.select = function() { return new Query(this); }; Caprese.prototype.stats = function() { console.log("Size:", this.size); console.log("First:", this.first); console.log("Cursor:", this.cursor); console.log("Index:", this.index); if (this.fd) { return console.log("Buffer:", new Buffer(fs.readFileSync(this.file, 'binary'))); } else if (this.db) { return console.log("Buffer:", this.db); } }; Caprese.prototype._read = function(buffer, offset, length, position, callback) { if (this.fd) { return fs.read(this.fd, buffer, offset, length, position, callback); } else if (this.db) { return setImmediate((function(_this) { return function() { var ex; try { _this.db.copy(buffer, offset, position, position + length); return callback(null); } catch (_error) { ex = _error; return callback(ex); } }; })(this)); } else { return setImmediate(function() { return callback(new Error("No database is initialized.")); }); } }; Caprese.prototype.read = function(offset, length, callback) { var buffer, next, part; if (length > this.size) { return callback(new RangeError("Reading more data than size of capped file")); } if (offset < 0 || offset >= this.size) { return callback(new Error("Out of bounds")); } next = this._move(offset, length); buffer = new Buffer(length); if (offset + length > this.size) { part = this.size - offset; return this._read(buffer, 0, part, offset, (function(_this) { return function(err) { if (err) { return callback(err); } return _this._read(buffer, part, length - part, 0, function(err) { return callback(err, buffer, next); }); }; })(this)); } else { return this._read(buffer, 0, length, offset, (function(_this) { return function(err) { return callback(err, buffer, next); }; })(this)); } }; Caprese.prototype.reindex = function(callback) { var buffer, cb, cursor, eof, fn, test; buffer = new Buffer(1); cursor = this.first; eof = false; test = function() { return eof; }; fn = (function(_this) { return function(next) { return _this.read(cursor, 1, function(err, buffer, nextbyte) { var type; if (err) { return next(err); } if (buffer[0] === 0x00) { eof = true; return next(null); } else if (buffer[0] === Caprese.INFO || buffer[0] === Caprese.ERROR || buffer[0] === Caprese.WARNING) { type = buffer.readUInt8(0); cursor = nextbyte; return _this.read(cursor, 4, function(err, buffer, nextbyte) { var date; date = buffer.readUInt32LE(0); cursor = nextbyte; return _this.read(cursor, 2, function(err, buffer, nextbyte) { var length; if (err) { return next(err); } length = buffer.readUInt16LE(0); _this.index.push({ offset: nextbyte, length: length, type: type, date: new Date(date * 1000) }); cursor = _this._move(nextbyte, length); if (cursor === _this.cursor) { eof = true; } return next(null); }); }); } else { return next(new Error("Unknown type '" + buffer[0] + "' on offset '" + cursor + "'")); } }); }; })(this); cb = function(err) { if (err) { return callback(err); } if (!eof) { return fn(cb); } return callback(null); }; return fn(cb); }; Caprese.prototype._write = function(buffer, offset, length, position, callback) { if (this.fd) { return fs.write(this.fd, buffer, offset, length, position, callback); } else if (this.db) { return setImmediate((function(_this) { return function() { var ex; try { buffer.copy(_this.db, position, offset, offset + length); return callback(null); } catch (_error) { ex = _error; return callback(ex); } }; })(this)); } else { return setImmediate(function() { return callback(new Error("No database is initialized.")); }); } }; Caprese.prototype.write = function(buffer, offset, callback) { var next, part; if (buffer.length > this.size) { return callback(new RangeError("Writing more data than size of capped file")); } if (offset < 0 || offset >= this.size) { return callback(new Error("Out of bounds")); } next = this._move(offset, buffer.length); if (offset + buffer.length > this.size) { part = this.size - offset; return this._write(buffer, 0, part, offset, (function(_this) { return function(err) { if (err) { return callback(err); } return _this._write(buffer, part, buffer.length - part, 0, function(err) { return callback(err, next); }); }; })(this)); } else { return this._write(buffer, 0, buffer.length, offset, (function(_this) { return function(err) { return callback(err, next); }; })(this)); } }; return Caprese; })(EventEmitter); module.exports = Caprese; }).call(this);