var util = require('util'),
events = require('events'),
Stream = require('stream').Stream;
var GridStore = require('mongodb').GridStore,
Db = require('mongodb').Db,
Server = require('mongodb').Server,
Chunk = require('mongodb').Chunk;
var GridStream = exports;
function GridReadStream(dbname, filename, options, dbopts){
Stream.call(this);
var self = this;
if(!options) options = {};
var host = process.env['MONGO_NODE_DRIVER_HOST'] || 'localhost';
var port = process.env['MONGO_NODE_DRIVER_PORT'] || 27017;
dbopts = dbopts || { w: 1, journal: false, fsync: false };
this.db = new Db(dbname, new Server(host, port, {}), dbopts);
this.filename = filename;
this.readable = true
this.writable = false;
this.paused = false;
this.encoding = null;
this.options = options;
this.head = 0;
this.options['root'] = this.options['root'] || GridStore.DEFAULT_ROOT_COLLECTION;
this.options['chunk_size'] = this.options['chunk_size'] || Chunk.DEFAULT_CHUNK_SIZE;
this.gridStore = new GridStore(this.db, this.filename, 'r', this.options);
this.db.open(function(err){
if(err) throw err;
self.gridStore.open(function(err, gs){
if(err) throw err;
self.gridStore = gs;
self._read();
});
});
}
util.inherits(GridReadStream, Stream);
GridReadStream.prototype.setEncoding = function(encoding){
if(encoding === 'utf8'||encoding === 'ascii'||encoding === 'base64')
this.encoding = encoding;
else
this._throw(new Error('An unknown encoding was used.'));
}
GridReadStream.prototype.pause = function(){
this.paused = true;
this.emit('pause');
}
GridReadStream.prototype.resume = function(){
this.paused = false;
this._read();
this.emit('resume');
}
GridReadStream.prototype.destroy = function(){
var self = this;
this.gridStore.close(function(err,result){
if(err) self._throw(err);
this.writable = false;
this.readable = false;
self.db.close();
self.emit('close');
});
}
GridReadStream.prototype.destroySoon = GridReadStream.prototype.destroy;
GridReadStream.prototype._throw = function(err){
this.readable = false;
this.emit('error', err);
}
GridReadStream.prototype._read = function(){
var self = this;
if(this.readable){
if(!this.paused){
var len;
if(this.head + this.options.chunk_size > this.gridStore.length)
len = this.gridStore.length - this.head;
else
len = this.options.chunk_size;
this.gridStore.seek(self.head, function(err, gridStore){
gridStore.read(len, function(err, data){
if(self.encoding !== null)
data = data.toString(self.encoding);
self.emit('data',data);
self.head += len;
if(self.head !== self.gridStore.length)
self._read();
else{
self.emit('end');
self.destroy();
}
});
});
}
}else{
this._throw(new Error('This stream is not readable.'));
}
}
GridStream.createGridReadStream = function(dbname, filename, options, dbopts){
return new GridReadStream(dbname, filename, options, dbopts);
}
function GridWriteStream(dbname, filename, mode, options, dbopts){
Stream.call(this);
var self = this;
if(!options) options = {};
mode = mode === 'w' ? 'w' : 'w+';
var host = process.env['MONGO_NODE_DRIVER_HOST'] || 'localhost';
var port = process.env['MONGO_NODE_DRIVER_PORT'] || 27017;
dbopts = dbopts || { w: 1, journal: false, fsync: false };
this.db = new Db(dbname, new Server(host, port, {}), dbopts);
this.filename = filename;
this.readable = false;
this.writable = true;
this.options = options;
this.opQueue = [];
this.emitter = new events.EventEmitter();
this.busy = false;
this.connected = false;
this.emitter.on('_op', function(){
self.busy = false;
if(self.opQueue.length > 0) self._flush();
});
this.options['root'] = this.options['root'] || GridStore.DEFAULT_ROOT_COLLECTION;
this.options['chunk_size'] = this.options['chunk_size'] || Chunk.DEFAULT_CHUNK_SIZE;
this.gridStore = new GridStore(this.db, this.filename, mode, this.options);
this.db.open(function(err){
if(err) throw err;
self.gridStore.open(function(err, gs){
if(err) throw err;
self.gridStore = gs;
self.connected = true;
if(self.opQueue.length > 0)
self._flush();
});
});
}
util.inherits(GridWriteStream, Stream);
GridWriteStream.prototype._flush = function(){
if(this.connected && !this.busy){
this.busy = true;
var op = this.opQueue.shift(),
func = op.shift(),
args = op.pop();
func.apply(this, args);
}
}
GridWriteStream.prototype.write = function(buffer){
var args = arguments;
this.opQueue.push([this._write, args]);
this._flush();
}
GridWriteStream.prototype.end = function(buffer){
var args = arguments;
this.opQueue.push([this._end, args]);
this._flush();
}
GridWriteStream.prototype._write = function(buffer){
var self = this;
if(this.writable){
if(!(buffer instanceof Buffer)) buffer = new Buffer(buffer);
this.gridStore.write(buffer,function(err){
if(err) self._throw(err);
self.emitter.emit('_op');
self.emit('drain');
});
return true;
}else{
this._throw(new Error('This stream is not writable.'));
this.emitter.emit('_op');
}
}
GridWriteStream.prototype._end = function(buffer){
var self = this;
if(this.writable){
if(!buffer){
return this.destroy();
}
if(!(buffer instanceof Buffer)) buffer = new Buffer(buffer);
this.destroySoon();
this._write(buffer);
}else{
this._throw(new Error('This stream is not writable.'));
self.emitter.emit('_op');
}
}
GridWriteStream.prototype.destroy = function(){
var self = this;
this.writable = false;
this.readable = false;
this.connected = false;
this.busy = false;
this.gridStore.close(function(err,result){
if(err) self._throw(err);
self.db.close();
self.emit('close');
});
}
GridWriteStream.prototype.destroySoon = function(){
var self = this;
if(this.writable){
if(this.opQueue.length > 1){
this.emitter.on('_op', function(){
if(self.opQueue.length === 1){
self.emitter.removeAllListeners('_op');
self.emitter.once('_op', function(){
self.destroy();
});
}
});
}else{
this.emitter.removeAllListeners('_op');
this.emitter.once('_op', function(){
self.destroy();
});
}
}else{
this.destroy();
}
}
GridWriteStream.prototype._throw = function(err){
this.emit('error', err);
this.writable = false;
}
GridStream.createGridWriteStream = function(dbname, filename, mode, options, dbopts){
return new GridWriteStream(dbname, filename, mode, options, dbopts);
}
exports.GridWriteStream = GridWriteStream;
exports.GridReadStream = GridReadStream;