ndn-js-contrib
Version:
Reusable 'Classes' for Named Data Networking: NameTree, PIT, FIB, ContentStore, Interfaces, and Transports
183 lines (148 loc) • 5.86 kB
JavaScript
var debug = {}; debug.debug= require("debug")("ContentStore");
var lruDebug = require("debug")("lru");
var NameTree = require("./NameTree.js")
var crypto = require("ndn-js/js/crypto.js")
var Name = require("ndn-js/js/name.js").Name;
/** ContentStore constructor for building cache's and database indexes
*@constructor
*@returns {ContentStore} - a new ContentStore
*/
var ContentStore = function ContentStore(){
this._nameTree = new NameTree();
this._maxPackets = Infinity;
this._packetCount = 0;
this._stales = [];
this._EntryClass = ContentStore.Entry;
return this;
};
/**
* override the default ContentStore.Entry class, see {@link Repository} for an example
*@param {Function} clas - the constructor for the entry class to use henceforth. Must provide a compatible promise api
*@param {Object} this - for chaining
*/
ContentStore.prototype.setEntryClass = function(clas){
this._EntryClass = clas;
return this;
}
ContentStore.prototype.onMaxPackets = function ContentStore_onMaxPackets(){
//todo: impliment a basic eviction algorithm
};
ContentStore.prototype.setOnMaxPackets = function ContentStore_setOnMaxPackets(onMaxPackets){
if (typeof onMaxPackets !== "function")
throw new Error("ContentStore.setOnMaxPackets(onMaxPackets): must be a function")
this.onMaxPackets = onMaxPackets.bind(this);
}
ContentStore.Entry = function ContentStore_Entry(data, cs){
this._data = data;
this._stale = false;
this.cs = cs;
var self = this;
setTimeout( function(){
self.makeStale(cs);
}, data.getMetaInfo().getFreshnessPeriod() );
return this;
};
ContentStore.Entry.prototype.getNameWithDigest = function ContentStore_Entry_getNameWithDigest(){
if (!this._nameWithDigest){
this._nameWithDigest = this._data.name.getPrefix(this._data.name.size())
this._nameWithDigest.append("sha256digest=" + crypto.createHash('sha256')
.update(this._data
.wireEncode()
.buffer)
.digest()
.toString('hex'));
}
return this._nameWithDigest;
};
ContentStore.Entry.prototype.getData = function ContentStore_Entry_getData(){
var self = this;
return new Promise(function ContentStore_Entry_getData_Promise(resolve, reject){
resolve(self._data);
})
};
ContentStore.Entry.prototype.onDataStale = function ContentStore_Entry_onDataStale (){
};
ContentStore.Entry.prototype.makeStale = function ContentStore_Entry_makeStale(cs){
this._stale = true;
this.onDataStale(cs);
};
ContentStore.Entry.prototype.fulfillsInterest = function ContentStore_Entry_fulfillsInterest(interest){
//console.log("checking interest fulfills entry", interest, this.getNameWithDigest())
return ( interest.matchesName(this.getNameWithDigest())
&& !(interest.getMustBeFresh() && this.getStale()));
}
ContentStore.Entry.prototype.getStale = function ContentStore_Entry_getStale(){
return this._stale;
}
ContentStore.prototype.setMaxPackets = function ContentStore_setMaxPackets(int){
this._maxPackets = int;
}
ContentStore.prototype.getMaxPackets = function ContentStore_getMaxPackets(){
return this._maxPackets;
}
/**check the ContentStore for data matching a given interest (including min/max suffix, exclude, publisherKey)
*@param {ndn.Interest} interest the interest to match against
*@param {function=} callback for asynchronous cases (like levelDB). recieves return value as only argument
*@returns {Buffer | null}
*/
ContentStore.prototype.lookup = function(interest){
var self = this;
return new Promise(function ContentStore_lookup_Promise(resolve, reject){
if(interest.getChildSelector())
self._nameTree.right(interest.name);
else
self._nameTree.left(interest.name);
var suf = interest.getMinSuffixComponents()
//console.log("minsuffix", suf)
if ( suf ){
var totalDepth = suf + interest.name.size()
self._nameTree.skip(function(node){
return !(node.depth + node.prefix.size() >= totalDepth);
})
}
//console.log("begin cs check")
for (var node of self._nameTree){
//console.log("checking node ", node)
var entry = node.getItem();
if (entry && entry.fulfillsInterest(interest)){
//console.log("entry fulfills")
return entry.getData()
.then(resolve)
.catch(reject);
}
}
return reject(interest);
})
};
ContentStore.prototype.createNode = function ContentStore_createNode(data, store){
var self = this;
return new Promise(function ContentStore_createNode_Promise(resolve,reject){
var entry = new self._EntryClass(data, self);
resolve(new NameTree.Node(entry.getNameWithDigest(),entry));
})
}
/**Insert a new entry into the contentStore
*@constructor
*@param {Buffer} element the raw data packet
*@param {ndn.Data} data the ndn.Data object
*@returns {ContentStore} - for chaining
*/
ContentStore.prototype.insert = function ContentStore_insert(data, store){
var self = this;
store = store || self;
return store.createNode(data, store)
.then(function ContentStore_nameTree_insert(node){
self._nameTree.insert(node);
//console.log(node)
if (++self._packetCount >= self.getMaxPackets())
return [node.getItem(),self.onMaxPackets()];
else
return [node.getItem()]
});
};
ContentStore.prototype.removeNode = function ContentStore_removeNode(node){
this._nameTree.remove(node.prefix);
this._packetCount--;
return node.getItem();
}
module.exports = ContentStore;