fix async/sync

This commit is contained in:
Mark Nadal 2017-10-16 00:03:59 -07:00
parent 9ce98ffcc0
commit 42e0ac89ea
19 changed files with 1255 additions and 670 deletions

View File

@ -1,78 +0,0 @@
<div id="be">
<div id="view"></div>
<video id="video" style="display: none; height:300px;width:320px" autoplay="true"></video>
<canvas id="canvas" style="display: none; height:240px; width:320px"></canvas>
<textarea id="debug" style="height:240px;width:420px;"></textarea>
<div id="audio"></div>
</div>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="gun.js"></script>
<script>
;(function(){
console.log("WARNING! THIS EXAMPLE / DEMO IS NOT FINISHED! DO NOT USE IT.");
navigator.getUserMedia = navigator.getUserMedia
|| navigator.webkitGetUserMedia
|| navigator.mozGetUserMedia
|| navigator.msGetUserMedia;
var gun = Gun(location.origin + '/gun');
var blast = gun.get('audio'), sight = gun.get('video');
var be = $("#be"), vid = $('#video'), aud, NO;
blast.on(function(data){
console.log("<<<<<", data.frame);
debug.value = data.frame;
play(data.frame);
});
sight.on(function(data){
show(data.frame);
});
navigator.getUserMedia({audio: true, video: false}, function talk(stream){
return;
var arr = [], mr = new MediaRecorder(stream, {audioBitsPerSecond: 6000});
mr.start();
setTimeout(function(){ talk(stream); setTimeout(function(){ mr.stop() },5); },250);
mr.ondataavailable = function(e){
//console.info('data available', e.data);
arr.push(e.data);
}
mr.onstop = function(){
encode(arr, function(b64){
//console.log(">>>>>", b64);
blast.put({frame: b64});
});
//arr = [];
}
}, function(err){ console.log(err) });
navigator.getUserMedia({audio: false, video: true}, function(stream){
var el = vid[0], draw = canvas.getContext('2d');
el.src = (window.URL || window.webkitURL).createObjectURL(stream);
el.play(); // Start the video in webcam
;(function see(){
canvas.width = el.videoWidth;
canvas.height = el.videoHeight;
draw.drawImage(el, 0, 0);
var data = canvas.toDataURL('image/jpeg'); // Gets the Base64 encoded text for image
sight.put({frame: data});
setTimeout(see, 1000 / 10);
}());
}, function(err){ console.log(err) });
function encode(arr, cb){
var blob = new Blob(arr, {type: 'audio/ogg'});
var fileReader = new window.FileReader();
fileReader.onloadend = function() {
cb(fileReader.result);
};
fileReader.readAsDataURL(blob);
//fileReader.readAsArrayBuffer(blob);
}
function play(b64){
return;
if(NO){ return }
$('#audio').html('<audio autoplay><source src="'+b64+'"></audio>');
}
function show(frame){
//if(NO){ return console.log(val); }
$("#view").html("<img src='" + frame + "' width='75%'>");
}
}());
</script>

605
gun.js
View File

@ -587,6 +587,31 @@
module.exports = Graph;
})(require, './graph');
;require(function(module){
// request / response module, for asking and acking messages.
require('./onto'); // depends upon onto!
module.exports = function ask(cb, as){
if(!this.on){ return }
if(!(cb instanceof Function)){
if(!cb || !as){ return }
var id = cb['#'] || cb, tmp = (this.tag||empty)[id];
if(!tmp){ return }
tmp = this.on(id, as);
clearTimeout(tmp.err);
return true;
}
var id = (as && as['#']) || Math.random().toString(36).slice(2);
if(!cb){ return id }
var to = this.on(id, cb, as);
to.err = to.err || setTimeout(function(){
console.log(50, 'TIME OUT', to.err, id);
to.next({err: "Error: No ACK received yet."});
to.off();
}, 1000 * 9); // TODO: Make configurable!!!
return id;
}
})(require, './ask');
;require(function(module){
var Type = require('./type');
function Dup(opt){
@ -636,9 +661,10 @@
Gun.node = require('./node');
Gun.state = require('./state');
Gun.graph = require('./graph');
Gun.dup = require('./dup');
Gun.on = require('./onto');
Gun.ask = require('./ask');
Gun.dup = require('./dup');
Gun._ = { // some reserved key words, these are not the only ones.
node: Gun.node._ // all metadata of a node is stored in the meta property on the node.
,soul: Gun.val.rel._ // a soul is a UUID of a node but it always points to the "latest" data known.
@ -649,12 +675,11 @@
;(function(){
Gun.create = function(at){
at.on = at.on || Gun.on;
at.root = at.root || at.gun;
at.graph = at.graph || {};
at.on = at.on || Gun.on;
at.ask = at.ask || Gun.ask;
at.dup = at.dup || Gun.dup();
at.ask = Gun.on.ask;
at.ack = Gun.on.ack;
var gun = at.gun.opt(at.opt);
if(!at.once){
at.on('in', root, at);
@ -663,42 +688,42 @@
at.once = 1;
return gun;
}
function root(at){
function root(msg){
//console.log("add to.next(at)"); // TODO: BUG!!!
var ev = this, cat = ev.as, coat, tmp;
if(!at.gun){ at.gun = cat.gun }
if(!(tmp = at['#'])){ tmp = at['#'] = text_rand(9) }
if(cat.dup.check(tmp)){ return }
cat.dup.track(tmp);
coat = obj_to(at, {gun: cat.gun});
if(!cat.ack(at['@'], at)){
if(at.get){
Gun.on.get(coat);
//cat.on('get', get(coat));
var ev = this, at = ev.as, gun = at.gun, tmp;
//if(!msg.gun){ msg.gun = at.gun }
if(!(tmp = msg['#'])){ tmp = msg['#'] = text_rand(9) }
if(at.dup.check(tmp)){ return }
at.dup.track(tmp);
msg = obj_to(msg);//, {gun: at.gun});
if(!at.ask(msg['@'], msg)){
if(msg.get){
Gun.on.get(msg, gun);
//at.on('get', get(msg));
}
if(at.put){
Gun.on.put(coat);
//cat.on('put', put(coat));
if(msg.put){
Gun.on.put(msg, gun);
//at.on('put', put(msg));
}
}
cat.on('out', coat);
at.on('out', msg);
}
}());
;(function(){
Gun.on.put = function(at){
var cat = at.gun._, ctx = {gun: at.gun, graph: at.gun._.graph, put: {}, map: {}, machine: Gun.state()};
if(!Gun.graph.is(at.put, null, verify, ctx)){ ctx.err = "Error: Invalid graph!" }
if(ctx.err){ return cat.on('in', {'@': at['#'], err: Gun.log(ctx.err) }) }
Gun.on.put = function(msg, gun){
var at = gun._, ctx = {gun: gun, graph: at.graph, put: {}, map: {}, machine: Gun.state(), ack: msg['@']};
if(!Gun.graph.is(msg.put, null, verify, ctx)){ ctx.err = "Error: Invalid graph!" }
if(ctx.err){ return at.on('in', {'@': msg['#'], err: Gun.log(ctx.err) }) }
obj_map(ctx.put, merge, ctx);
obj_map(ctx.map, map, ctx);
if(u !== ctx.defer){
setTimeout(function(){
Gun.on.put(at);
}, ctx.defer - cat.machine);
Gun.on.put(msg, gun);
}, ctx.defer - at.machine);
}
if(!ctx.diff){ return }
cat.on('put', obj_to(at, {put: ctx.diff}));
at.on('put', obj_to(msg, {put: ctx.diff}));
};
function verify(val, key, node, soul){ var ctx = this;
var state = Gun.state.is(node, key), tmp;
@ -715,71 +740,49 @@
(ctx.diff || (ctx.diff = {}))[soul] = Gun.state.to(node, key, ctx.diff[soul]);
}
function merge(node, soul){
var cat = this.gun._, ref = (cat.next || empty)[soul];
if(!ref){ return }
var at = this.map[soul] = {
var cat = this.gun._, at = (cat.next || empty)[soul];
if(!at){ return }
var msg = this.map[soul] = {
put: this.node = node,
get: this.soul = soul,
gun: this.ref = ref
gun: this.at = at
};
if(this.ack){ msg['@'] = this.ack }
obj_map(node, each, this);
cat.on('node', at);
cat.on('node', msg);
}
function each(val, key){
var graph = this.graph, soul = this.soul, cat = (this.ref._), tmp;
var graph = this.graph, soul = this.soul, at = (this.at._), tmp;
graph[soul] = Gun.state.to(this.node, key, graph[soul]);
(cat.put || (cat.put = {}))[key] = val;
at.put = Gun.state.to(this.node, key, at.put);
}
function map(at, soul){
if(!at.gun){ return }
(at.gun._).on('in', at);
function map(msg, soul){
if(!msg.gun){ return }
(msg.gun._).on('in', msg);
}
Gun.on.get = function(at){
var cat = at.gun._, soul = at.get[_soul], node = cat.graph[soul], field = at.get[_field], tmp;
var next = cat.next || (cat.next = {}), as = ((next[soul] || empty)._);
if(!node || !as){ return cat.on('get', at) }
Gun.on.get = function(msg, gun){
var root = gun._, soul = msg.get[_soul], node = root.graph[soul], field = msg.get[_field], tmp;
var next = root.next || (root.next = {}), at = ((next[soul] || empty)._);
if(!node || !at){ return root.on('get', msg) }
if(field){
if(!obj_has(node, field)){ return cat.on('get', at) }
if(!obj_has(node, field)){ return root.on('get', msg) }
node = Gun.state.to(node, field);
} else {
node = Gun.obj.copy(node);
}
node = Gun.graph.node(node);
tmp = as.ack;
cat.on('in', {
'@': at['#'],
how: 'mem',
//tmp = at.ack;
root.on('in', {
'@': msg['#'],
//how: 'mem',
put: node,
gun: as.gun
gun: gun
});
if(0 < tmp){
return;
}
cat.on('get', at);
}
}());
;(function(){
Gun.on.ask = function(cb, as){
if(!this.on){ return }
var id = text_rand(9);
if(cb){
var to = this.on(id, cb, as);
to.err = setTimeout(function(){
to.next({err: "Error: No ACK received yet."});
to.off();
}, 1000 * 9); // TODO: Make configurable!!!
}
return id;
}
Gun.on.ack = function(at, reply){
if(!at || !reply || !this.on){ return }
var id = at['#'] || at, tmp = (this.tag||empty)[id];
if(!tmp){ return }
this.on(id, reply);
clearTimeout(tmp.err);
return true;
//if(0 < tmp){
// return;
//}
root.on('get', msg);
}
}());
@ -810,7 +813,7 @@
var list_is = Gun.list.is;
var text = Gun.text, text_is = text.is, text_rand = text.random;
var obj = Gun.obj, obj_is = obj.is, obj_has = obj.has, obj_to = obj.to, obj_map = obj.map, obj_copy = obj.copy;
var state = Gun.state, _soul = Gun._.soul, _field = Gun._.field, rel_is = Gun.val.rel.is;
var state = Gun.state, _soul = Gun._.soul, _field = Gun._.field, node_ = Gun._.node, rel_is = Gun.val.rel.is;
var empty = {}, u;
console.debug = function(i, s){ return (console.debug.i && i === console.debug.i && console.debug.i++) && (console.log.apply(console, arguments) || s) };
@ -881,132 +884,74 @@
cat.on('out', output, cat); // However for output, there isn't really the global option. I must listen by adding my own listener individually BEFORE this one is ever called.
return chain;
}
function output(at){
var cat = this.as, gun = cat.gun, root = gun.back(-1), put, get, now, tmp;
if(!at.gun){
at.gun = gun;
}
if(get = at.get){
if(tmp = get[_soul]){
tmp = (root.get(tmp)._);
if(obj_has(get, _field)){
if(obj_has(put = tmp.put, get = get[_field])){
tmp.on('in', {get: tmp.get, put: Gun.state.to(put, get), gun: tmp.gun}); // TODO: Ugly, clean up? Simplify all these if conditions (without ruining the whole chaining API)?
function output(msg){
var put, get, at = this.as, back = at.back._, root = at.root._;
if(!msg.gun){ msg.gun = at.gun }
this.to.next(msg);
if(get = msg.get){
/*if(u !== at.put){
at.on('in', at);
return;
}*/
if(get['#'] || at.soul){
get['#'] = get['#'] || at.soul;
msg['#'] || (msg['#'] = root.opt.uuid());
back = (root.gun.get(get['#'])._);
if(!(get = get['.'])){
if(obj_has(back, 'put')){
//if(u !== back.put){
back.on('in', back);
}
if(back.ack){
return;
}
msg.gun = back.gun;
back.ack = -1;
} else
if(obj_has(tmp, 'put')){
//if(u !== tmp.put){
tmp.on('in', tmp);
if(obj_has(back.put, get)){
back.on('in', {
gun: back.gun,
put: Gun.state.to(back.put, get),
get: back.get
});
return;
}
} else {
if(obj_has(get, _field)){
get = get[_field];
var next = get? (gun.get(get)._) : cat;
// TODO: BUG! Handle plural chains by iterating over them.
//if(obj_has(next, 'put')){ // potentially incorrect? Maybe?
if(u !== next.put){ // potentially incorrect? Maybe?
//next.tag['in'].last.next(next);
next.on('in', next);
return;
}
if(obj_has(cat, 'put')){
//if(u !== cat.put){
var val = cat.put, rel;
if(rel = Gun.node.soul(val)){
val = Gun.val.rel.ify(rel);
}
if(rel = Gun.val.rel.is(val)){
if(!at.gun._){ return }
(at.gun._).on('out', {
get: tmp = {'#': rel, '.': get, gun: at.gun},
'#': root._.ask(ack, tmp),
gun: at.gun
});
return;
}
if(u === val || Gun.val.is(val)){
if(!at.gun._){ return }
(at.gun._).on('in', {
get: get,
gun: at.gun
});
return;
}
} else
if(cat.map){
obj_map(cat.map, function(proxy){
proxy.at.on('in', proxy.at);
});
};
if(cat.soul){
if(!at.gun._){ return }
(at.gun._).on('out', {
get: tmp = {'#': cat.soul, '.': get, gun: at.gun},
'#': root._.ask(ack, tmp),
gun: at.gun
});
return;
}
if(cat.get){
if(!cat.back._){ return }
(cat.back._).on('out', {
get: obj_put({}, _field, cat.get),
gun: gun
});
return;
}
at = obj_to(at, {get: {}});
} else {
if(obj_has(cat, 'put')){
//if(u !== cat.put){
cat.on('in', cat);
} else
if(cat.map){
obj_map(cat.map, function(proxy){
proxy.at.on('in', proxy.at);
});
}
if(cat.ack){
if(!obj_has(cat, 'put')){ // u !== cat.put instead?
//if(u !== cat.put){
return;
}
}
cat.ack = -1;
if(cat.soul){
cat.on('out', {
get: tmp = {'#': cat.soul, gun: cat.gun},
'#': root._.ask(ack, tmp),
gun: cat.gun
});
return;
}
if(cat.get){
if(!cat.back._){ return }
(cat.back._).on('out', {
get: obj_put({}, _field, cat.get),
gun: cat.gun
});
return;
}
root.ask(ack, msg);
return root.on('in', msg);
}
if(root.now){
root.now[at.id] = root.now[at.id] || true;
}
if(get['.']){
if(at.get){
msg = {get: {'.': at.get}, gun: at.gun};
return back.on('out', msg);
}
msg = {get: {}, gun: at.gun};
return back.on('out', msg);
}
at.ack = at.ack || -1;
if(at.get){
msg.gun = at.gun;
get['.'] = at.get;
return back.on('out', msg);
}
}
(cat.back._).on('out', at);
return back.on('out', msg);
}
function input(at){
at = at._ || at;
var ev = this, cat = this.as, gun = at.gun, coat = gun._, change = at.put, back = cat.back._ || empty, rel, tmp;
if(0 > cat.ack && !at.ack && !Gun.val.rel.is(change)){ // for better behavior?
cat.ack = 1;
}
if(cat.get && at.get !== cat.get){
at = obj_to(at, {get: cat.get});
}
if(cat.field && coat !== cat){
at = obj_to(at, {gun: cat.gun});
if(coat.ack){
cat.ack = cat.ack || coat.ack;
cat.ack = coat.ack;
//cat.ack = cat.ack || coat.ack;
}
}
if(u === change){
@ -1021,12 +966,19 @@
return;
}
if(cat.soul){
if(cat.root._.now){ at = obj_to(at, {put: change = coat.put}) } // TODO: Ugly hack for uncached synchronous maps.
//if(cat.root._.now){ at = obj_to(at, {put: change = coat.put}) } // TODO: Ugly hack for uncached synchronous maps.
ev.to.next(at);
echo(cat, at, ev);
obj_map(change, map, {at: at, cat: cat});
return;
}
/*if(rel = Gun.val.rel.is(change)){
if(tmp = (gun.back(-1).get(rel)._).put){
change = tmp; // this will cause performance to turn to mush, maybe use `.now` check?
}
//if(tmp.put){ change = tmp.put; }
}
if(!rel || tmp){*/
if(!(rel = Gun.val.rel.is(change))){
if(Gun.val.is(change)){
if(cat.field || cat.soul){
@ -1058,31 +1010,36 @@
echo(cat, at, ev);
}
Gun.chain.chain.input = input;
function relate(cat, at, coat, rel){
if(!rel || node_ === cat.get){ return }
var tmp = (cat.root.get(rel)._);
if(cat.field){
coat = tmp;
function relate(at, msg, from, rel){
if(!rel || node_ === at.get){ return }
var tmp = (at.root.get(rel)._);
if(at.has){
from = tmp;
} else
if(coat.field){
relate(coat, at, coat, rel);
if(from.has){
relate(from, msg, from, rel);
}
if(coat === cat){ return }
(coat.echo || (coat.echo = {}))[cat.id] = cat;
if(cat.field && !(cat.map||empty)[coat.id]){
not(cat, at);
if(from === at){ return }
(from.echo || (from.echo = {}))[at.id] = at;
if(at.has && !(at.map||empty)[from.id]){ // if we haven't seen this before.
not(at, msg);
}
tmp = (cat.map || (cat.map = {}))[coat.id] = cat.map[coat.id] || {at: coat};
if(rel === tmp.rel){ return }
ask(cat, tmp.rel = rel);
tmp = (at.map || (at.map = {}))[from.id] = at.map[from.id] || {at: from};
var now = at.root._.now;
if(rel === tmp.rel){
if(!now){ return }
if(u === now[at.id]){ return }
if((now._ || (now._ = {}))[at.id]){ return } now._[at.id] = true;
}
ask(at, tmp.rel = rel);
}
function echo(cat, at, ev){
if(!cat.echo){ return } // || node_ === at.get ????
if(cat.field){ at = obj_to(at, {event: ev}) }
obj_map(cat.echo, reverb, at);
function echo(at, msg, ev){
if(!at.echo){ return } // || node_ === at.get ?
if(at.has || at.field){ msg = obj_to(msg, {event: ev}) }
obj_map(at.echo, reverb, msg);
}
function reverb(cat){
cat.on('in', this);
function reverb(to){
to.on('in', this);
}
function map(data, key){ // Map over only the changes on every update.
var cat = this.cat, next = cat.next || empty, via = this.at, gun, chain, at, tmp;
@ -1109,17 +1066,19 @@
via: via
});
}
function not(cat, at){
if(!(cat.field || cat.soul)){ return }
var tmp = cat.map;
cat.map = null;
if(null === tmp){ return }
if(u === tmp && cat.put !== u){ return } // TODO: Bug? Threw second condition in for a particular test, not sure if a counter example is tested though.
function not(at, msg){
if(!(at.has || at.soul)){ return }
var tmp = at.map, root = at.root._;
at.map = null;
if(!root.now || !root.now[at.id]){
if((u === msg.put && !msg['@']) && null === tmp){ return }
}
if(u === tmp && at.put !== u){ return } // TODO: Bug? Threw second condition in for a particular test, not sure if a counter example is tested though.
obj_map(tmp, function(proxy){
if(!(proxy = proxy.at)){ return }
obj_del(proxy.echo, cat.id);
obj_del(proxy.echo, at.id);
});
obj_map(cat.next, function(gun, key){
obj_map(at.next, function(gun, key){
var coat = (gun._);
coat.put = u;
if(coat.ack){
@ -1132,37 +1091,39 @@
});
});
}
function ask(cat, soul){
var tmp = (cat.root.get(soul)._);
if(cat.ack){
tmp.ack = tmp.ack || -1;
tmp.on('out', {
get: tmp = {'#': soul, gun: tmp.gun},
'#': cat.root._.ask(ack, tmp)
});
function ask(at, soul){
var tmp = (at.root.get(soul)._);
if(at.ack){
//tmp.ack = tmp.ack || -1;
tmp.on('out', {get: {'#': soul}});
return;
}
obj_map(cat.next, function(gun, key){
(gun._).on('out', {
get: gun = {'#': soul, '.': key, gun: gun},
'#': cat.root._.ask(ack, gun)
});
obj_map(at.next, function(gun, key){
//(tmp.gun.get(key)._).on('out', {get: {'#': soul, '.': key}});
//tmp.on('out', {get: {'#': soul, '.': key}});
(gun._).on('out', {get: {'#': soul, '.': key}});
//at.on('out', {get: {'#': soul, '.': key}});
});
}
function ack(at, ev){
var as = this.as, cat = as.gun._;
if(!at.put || (as['.'] && !obj_has(at.put[as['#']], cat.get))){
if(cat.put !== u){ return }
cat.on('in', {
get: cat.get,
put: cat.put = u,
gun: cat.gun,
function ack(msg, ev){
var as = this.as, get = as.get || empty, at = as.gun._;
if(at.ack){ at.ack = (at.ack + 1) || 1 }
if(!msg.put || node_ == get['.'] || (get['.'] && !obj_has(msg.put[get['#']], at.get))){
if(at.put !== u){ return }
//at.ack = 0;
at.on('in', {
get: at.get,
put: at.put = u,
gun: at.gun,
'@': msg['@']
})
return;
}
at.gun = cat.root;
//if(/*!msg.gun &&*/ !get['.'] && get['#']){ at.ack = (at.ack + 1) || 1 }
//msg = obj_to(msg);
msg.gun = at.root;
//Gun.on('put', at);
Gun.on.put(at);
Gun.on.put(msg, at.root);
}
var empty = {}, u;
var obj = Gun.obj, obj_has = obj.has, obj_put = obj.put, obj_del = obj.del, obj_to = obj.to, obj_map = obj.map;
@ -1180,15 +1141,16 @@
}
} else
if(key instanceof Function){
var gun = this, at = gun._;
var gun = this, at = gun._, root = at.root._;
as = cb || {};
as.use = key;
as.out = as.out || {cap: 1};
as.out = as.out || {};
as.out.get = as.out.get || {};
'_' != at.get && ((at.root._).now = true); // ugly hack for now.
at.on('in', use, as);
var tmp = at.on('in', use, as);
if(root.now){ ++root.now.$ }
(root.now || (root.now = {$:1}))[as.now = at.id] = tmp;
at.on('out', as.out);
(at.root._).now = false;
if(!(--root.now.$)){ obj.del(root, 'now'); }
return gun;
} else
if(num_is(key)){
@ -1210,23 +1172,33 @@
var cat = back._, next = cat.next, gun = back.chain(), at = gun._;
if(!next){ next = cat.next = {} }
next[at.get = key] = gun;
if(cat.root === back){ at.soul = key }
else if(cat.soul || cat.field){ at.field = key }
return gun;
}
function use(at){
var ev = this, as = ev.as, gun = at.gun, cat = gun._, data = at.put, tmp;
if(u === data){
data = cat.put;
}
if((tmp = data) && tmp[rel._] && (tmp = rel.is(tmp))){
tmp = (cat.root.get(tmp)._);
if(u !== tmp.put){
at = obj_to(at, {put: tmp.put});
if(cat.root === back){
at.soul = key;
} else
if(cat.soul || cat.field){ // TODO: Convert field to has!
at.field = at.has = key;
if(obj_has(cat.put, key)){
//at.put = cat.put[key];
}
}
as.use(at, at.event || ev);
ev.to.next(at);
return gun;
}
function use(msg){
var ev = this, as = ev.as, gun = msg.gun, at = gun._, root = at.root._, data = msg.put, tmp;
if((tmp = root.now) && ev !== tmp[as.now]){
return ev.to.next(msg);
}
if(u === data){
data = at.put;
}
if((tmp = data) && tmp[rel._] && (tmp = rel.is(tmp))){
tmp = (at.root.get(tmp)._);
if(u !== tmp.put){
msg = obj_to(msg, {put: tmp.put});
}
}
as.use(msg, msg.event || ev);
ev.to.next(msg);
}
var obj = Gun.obj, obj_has = obj.has, obj_to = Gun.obj.to;
var num_is = Gun.num.is;
@ -1264,10 +1236,11 @@
return gun;
}
if(Gun.is(data)){
data.get(function(at,ev){ev.off();
var s = Gun.node.soul(at.put);
if(!s){Gun.log("The reference you are saving is a", typeof at.put, '"'+ as.put +'", not a node (object)!');return}
gun.put(Gun.val.rel.ify(s), cb, as);
data.get('_').get(function(at, ev, tmp){ ev.off();
if(!(tmp = at.gun) || !(tmp = tmp._.back) || !tmp._.soul){
return Gun.log("The reference you are saving is a", typeof at.put, '"'+ as.put +'", not a node (object)!');
}
gun.put(Gun.val.rel.ify(tmp._.soul), cb, as);
});
return gun;
}
@ -1280,7 +1253,7 @@
as.ref.get('_').get(any, {as: as});
if(!as.out){
// TODO: Perf idea! Make a global lock, that blocks everything while it is on, but if it is on the lock it does the expensive lookup to see if it is a dependent write or not and if not then it proceeds full speed. Meh? For write heavy async apps that would be terrible.
as.res = as.res || noop; // Gun.on.stun(as.ref); // TODO: BUG! Deal with locking?
as.res = as.res || stun; // Gun.on.stun(as.ref); // TODO: BUG! Deal with locking?
as.gun._.stun = as.ref._.stun;
}
return gun;
@ -1299,17 +1272,37 @@
as.batch();
}
function stun(cb){
if(cb){ cb() }
return;
var as = this;
if(!as.ref){ return }
if(cb){
as.after = as.ref._.tag;
as.now = as.ref._.tag = {};
cb();
return;
}
if(as.after){
as.ref._.tag = as.after;
}
}
function batch(){ var as = this;
if(!as.graph || obj_map(as.stun, no)){ return }
(as.res||iife)(function(){
as.res = as.res || function(cb){ if(cb){ cb() } };
as.res(function(){
var cat = (as.gun.back(-1)._), ask = cat.ask(function(ack){
this.off(); // One response is good enough for us currently. Later we may want to adjust this.
if(!as.ack){ return }
as.ack(ack, this);
}, as.opt);
var tmp = cat.root._.now; obj.del(cat.root._, 'now');
(as.ref._).now = true;
(as.ref._).on('out', {
gun: as.ref, put: as.out = as.env.graph, opt: as.opt, '#': ask
});
obj.del((as.ref._), 'now');
cat.root._.now = tmp;
}, as);
if(as.res){ as.res() }
} function no(v,f){ if(v){ return true } }
@ -1353,6 +1346,7 @@
return;
}
var cat = (at.gun._.back._), data = cat.put, opt = as.opt||{}, root, tmp;
if((tmp = as.ref) && tmp._.now){ return }
ev.off();
if(as.ref !== as.gun){
tmp = (as.gun._).get || cat.get;
@ -1422,7 +1416,7 @@
opt = (true === opt)? {change: true} : opt || {};
opt.ok = tag;
opt.last = {};
gun.get(ok, opt); // TODO: PERF! Event listener leak!!!????
gun.get(ok, opt); // TODO: PERF! Event listener leak!!!?
return gun;
}
@ -1463,6 +1457,7 @@
if(cb){
(opt = opt || {}).ok = cb;
opt.cat = at;
opt.out = {'#': Gun.text.random(9)};
gun.get(val, {as: opt});
opt.async = true; //opt.async = at.stun? 1 : true;
} else {
@ -1476,23 +1471,25 @@
return gun;
}
function val(at, ev, to){
var opt = this.as, cat = opt.cat, gun = at.gun, coat = gun._, data = coat.put || at.put, tmp;
function val(msg, ev, to){
var opt = this.as, cat = opt.cat, gun = msg.gun, coat = gun._, data = coat.put || msg.put, tmp;
if(u === data){
//return;
}
if(data && data[rel._] && (tmp = rel.is(data))){
//if(coat.soul && !(0 < coat.ack)){ return }
if(tmp = Gun.node.soul(data) || rel.is(data)){
//if(data && data[rel._] && (tmp = rel.is(data))){
tmp = (cat.root.get(tmp)._);
if(u === tmp.put){
if(u === tmp.put){//} || !(0 < tmp.ack)){
return;
}
data = tmp.put;
}
if(ev.wait){ clearTimeout(ev.wait) }
//if(!to && (!(0 < coat.ack) || ((true === opt.async) && 0 !== opt.wait))){
if(!opt.async){
if(!to){
ev.wait = setTimeout(function(){
val.call({as:opt}, at, ev, ev.wait || 1)
val.call({as:opt}, msg, ev, ev.wait || 1);
}, opt.wait || 99);
return;
}
@ -1502,7 +1499,7 @@
if((opt.seen = opt.seen || {})[coat.id]){ return }
opt.seen[coat.id] = true;
}
opt.ok.call(at.gun || opt.gun, data, at.get);
opt.ok.call(msg.gun || opt.gun, data, msg.get);
}
Gun.chain.off = function(){
@ -1558,6 +1555,7 @@
if(chain = cat.fields){ return chain }
chain = cat.fields = gun.chain();
chain._.val = gun.back('val');
chain._.MAPOF = cat.soul;
gun.on('in', map, chain._);
return chain;
}
@ -1574,16 +1572,16 @@
});
return chain;
}
function map(at){
if(!at.put || Gun.val.is(at.put)){ return }
function map(msg){
if(!msg.put || Gun.val.is(msg.put)){ return }
if(this.as.val){ this.off() } // TODO: Ugly hack!
obj_map(at.put, each, {cat: this.as, gun: at.gun});
this.to.next(at);
obj_map(msg.put, each, {at: this.as, msg: msg});
this.to.next(msg);
}
function each(v,f){
if(n_ === f){ return }
var cat = this.cat, gun = this.gun.get(f), at = (gun._);
(at.echo || (at.echo = {}))[cat.id] = cat;
var msg = this.msg, gun = msg.gun, at = this.at, tmp = (gun.get(f)._);
(tmp.echo || (tmp.echo = {}))[at.id] = at;
}
var obj_map = Gun.obj.map, noop = function(){}, event = {stun: noop, off: noop}, n_ = Gun.node._, u;
})(require, './map');
@ -1593,6 +1591,7 @@
Gun.chain.set = function(item, cb, opt){
var gun = this, soul;
cb = cb || function(){};
opt = opt || {}; opt.item = opt.item || item;
if(soul = Gun.node.soul(item)){ return gun.set(gun.back(-1).get(soul), cb, opt) }
if(!Gun.is(item)){
if(Gun.obj.is(item)){ return gun.set(gun._.root.put(item), cb, opt) }
@ -1622,16 +1621,16 @@
If you update anything here, consider updating the other adapters as well.
*/
Gun.on('opt', function(ctx){
this.to.next(ctx);
var opt = ctx.opt;
if(ctx.once){ return }
Gun.on('opt', function(root){
this.to.next(root);
var opt = root.opt;
if(root.once){ return }
if(false === opt.localStorage){ return }
opt.file = opt.file || opt.prefix || 'gun/'; // support old option name.
var graph = ctx.graph, acks = {}, count = 0, to;
var graph = root.graph, acks = {}, count = 0, to;
var disk = Gun.obj.ify(store.getItem(opt.file)) || {};
ctx.on('put', function(at){
root.on('put', function(at){
this.to.next(at);
Gun.graph.is(at.put, null, map);
if(!at['@']){ acks[at['#']] = true; } // only ack non-acks.
@ -1643,9 +1642,9 @@
to = setTimeout(flush, opt.wait || 1);
});
ctx.on('get', function(at){
root.on('get', function(at){
this.to.next(at);
var gun = at.gun, lex = at.get, soul, data, opt, u;
var lex = at.get, soul, data, u;
//setTimeout(function(){
if(!lex || !(soul = lex[Gun._.soul])){ return }
//if(0 >= at.cap){ return }
@ -1654,11 +1653,11 @@
if(data && field){
data = Gun.state.to(data, field);
}
if(!data && !Gun.obj.empty(gun.back('opt.peers'))){ // if data not found, don't ack if there are peers.
if(!data && !Gun.obj.empty(opt.peers)){ // if data not found, don't ack if there are peers.
return; // Hmm, what if we have peers but we are disconnected?
}
gun.on('in', {'@': at['#'], put: Gun.graph.node(data), how: 'lS'});
//},11);
root.on('in', {'@': at['#'], put: Gun.graph.node(data), how: 'lS'});
//},1);
});
var map = function(val, key, node, soul){
@ -1676,7 +1675,7 @@
}catch(e){ Gun.log(err = e || "localStorage failure") }
if(!err && !Gun.obj.empty(opt.peers)){ return } // only ack if there are no peers.
Gun.obj.map(ack, function(yes, id){
ctx.on('in', {
root.on('in', {
'@': id,
err: err,
ok: 0 // localStorage isn't reliable, so make its `ok` code be a low number.
@ -1694,17 +1693,17 @@
} else {
return;
}
Gun.on('opt', function(ctx){
this.to.next(ctx);
var opt = ctx.opt;
if(ctx.once){ return }
Gun.on('opt', function(root){
this.to.next(root);
var opt = root.opt;
if(root.once){ return }
if(false === opt.WebSocket){ return }
var ws = opt.ws || (opt.ws = {}); ws.who = 0;
Gun.obj.map(opt.peers, function(){ ++ws.who });
if(ctx.once){ return }
if(root.once){ return }
var batch;
ctx.on('out', function(at){
root.on('out', function(at){
this.to.next(at);
if(at.ws && 1 == ws.who){ return } // performance hack for reducing echoes.
batch = JSON.stringify(at);
@ -1719,13 +1718,13 @@
ws.drain = null;
if(!tmp.length){ return }
batch = JSON.stringify(tmp);
Gun.obj.map(opt.peers, send, ctx);
Gun.obj.map(opt.peers, send, root);
}, opt.wait || 1);
Gun.obj.map(opt.peers, send, ctx);
Gun.obj.map(opt.peers, send, root);
});
function send(peer){
var ctx = this, msg = batch;
var wire = peer.wire || open(peer, ctx);
var root = this, msg = batch;
var wire = peer.wire || open(peer, root);
if(!wire){ return }
if(wire.readyState === wire.OPEN){
wire.send(msg);
@ -1733,19 +1732,19 @@
}
(peer.queue = peer.queue || []).push(msg);
}
function receive(msg, peer, ctx){
if(!ctx || !msg){ return }
function receive(msg, peer, root){
if(!root || !msg){ return }
try{msg = JSON.parse(msg.data || msg);
}catch(e){}
if(msg instanceof Array){
var i = 0, m;
while(m = msg[i++]){
receive(m, peer, ctx);
receive(m, peer, root);
}
return;
}
if(1 == ws.who){ msg.ws = noop } // If there is only 1 client, just use noop since it doesn't matter.
ctx.on('in', msg);
root.on('in', msg);
}
function open(peer, as){
if(!peer || !peer.url){ return }

2
gun.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -36,7 +36,7 @@ Gun.on('opt', function(ctx){
ctx.on('get', function(at){
this.to.next(at);
var gun = at.gun, lex = at.get, soul, data, opt, u;
var lex = at.get, soul, data, opt, u;
//setTimeout(function(){
if(!lex || !(soul = lex[Gun._.soul])){ return }
//if(0 >= at.cap){ return }
@ -45,7 +45,7 @@ Gun.on('opt', function(ctx){
if(data && field){
data = Gun.state.to(data, field);
}
gun.on('in', {'@': at['#'], put: Gun.graph.node(data)});
ctx.on('in', {'@': at['#'], put: Gun.graph.node(data)});
//},11);
});
@ -53,7 +53,7 @@ Gun.on('opt', function(ctx){
disk[soul] = Gun.state.to(node, key, disk[soul]);
}
var wait;
var wait, u;
var flush = function(){
if(wait){ return }
clearTimeout(to);
@ -68,7 +68,7 @@ Gun.on('opt', function(ctx){
ctx.on('in', {
'@': id,
err: err,
ok: 1
ok: err? u : 1
});
});
if(1 < tmp){ flush() }

View File

@ -30,7 +30,7 @@ Gun.on('opt', function(ctx){
ctx.on('get', function(at){
this.to.next(at);
var gun = at.gun, lex = at.get, soul, data, opt, u;
var lex = at.get, soul, data, opt, u;
//setTimeout(function(){
if(!lex || !(soul = lex[Gun._.soul])){ return }
//if(0 >= at.cap){ return }
@ -39,7 +39,7 @@ Gun.on('opt', function(ctx){
if(data && field){
data = Gun.state.to(data, field);
}
gun.on('in', {'@': at['#'], put: Gun.graph.node(data)});
ctx.on('in', {'@': at['#'], put: Gun.graph.node(data)});
//},11);
});

View File

@ -3,71 +3,80 @@ var Gun = require('../gun');
var Radix = require('./radix');
function Radisk(opt){
/*
Any and all storage adapters should...
1. If not busy, write to disk immediately.
2. If busy, batch to disk. (Improves performance, reduces potential disk corruption)
3. If a batch exceeds a certain number of writes, force atomic batch to disk. (This caps total performance, but reduces potential loss)
*/
var radisk = function(key, val, cb){
if(0 <= key.indexOf('_') || 0 <= key.indexOf('$')){ // TODO: BUG! Fix!
var err = "ERROR: Radix and Radisk not tested against _ or $ keys!";
console.log(err);
cb = cb || val;
if(cb instanceof Function){ cb(err) }
return;
}
if(val instanceof Function){
cb = val;
val = radisk.batch(key);
if(u !== val){
return cb(null, val);
}
if(radisk.was){
val = radisk.was(key);
if(u !== val){
return cb(null, val);
}
}
console.log("READ FROM DISK");
return cb(null, val);
}
radisk.batch(key, val);
if(cb){ radisk.batch.acks.push(cb) }
if(!count++){ return thrash() } // (1)
if(opt.batch <= count){ return thrash() } // (3)
clearTimeout(to); // (2)
to = setTimeout(thrash, opt.wait);
};
radisk.batch = Radix();
radisk.batch.acks = [];
var count = 0, wait, to, u;
opt = opt || {};
opt.file = String(opt.file || 'radata');
opt.size = opt.size || (1024 * 1024 * 10); // 10MB
opt.batch = opt.batch || 10 * 1000;
opt.wait = opt.wait || 1;
opt.nest = opt.nest || ' ';
opt.size = opt.size || (1024 * 1024 * 10); // 10MB
opt.code = opt.code || {};
opt.code.from = opt.code.from || '!';
console.log("Warning: Radix storage engine has not been tested with all types of values and keys yet.");
if(!fs.existsSync(opt.file)){ fs.mkdirSync(opt.file) }
if(!opt.store){
return Gun.log("ERROR: Radisk needs `opt.store` interface with `{get: fn, put: fn, list: fn}`!");
}
if(!opt.store.put){
return Gun.log("ERROR: Radisk needs `store.put` interface with `(file, data, cb)`!");
}
if(!opt.store.get){
return Gun.log("ERROR: Radisk needs `store.get` interface with `(file, cb)`!");
}
if(!opt.store.list){
return Gun.log("ERROR: Radisk needs a streaming `store.list` interface with `(cb)`!");
}
var thrash = function(){
if(wait){ return }
clearTimeout(to);
wait = true;
var was = radisk.was = radisk.batch;
radisk.batch = null;
radisk.batch = Radix();
radisk.batch.acks = [];
chunk(radisk.was, function(err, ok){
radisk.was = null;
wait = false;
var tmp = count;
count = 0;
Gun.obj.map(was.acks, function(cb){cb(err, ok)});
if(1 < tmp){ thrash() }
/*
Any and all storage adapters should...
1. Because writing to disk takes time, we should batch data to disk. This improves performance, and reduces potential disk corruption.
2. If a batch exceeds a certain number of writes, we should immediately write to disk when physically possible. This caps total performance, but reduces potential loss.
*/
var r = function(key, val, cb){
key = ''+key;
if(val instanceof Function){
cb = val;
val = r.batch(key);
console.debug(3, 'batch', val);
if(u !== val){
return cb(u, val);
}
if(r.thrash.at){
val = r.thrash.at(key);
if(u !== val){
return cb(u, val);
}
}
//console.log("READ FROM DISK");
return r.read(key, cb);
}
r.batch(key, val);
if(cb){ r.batch.acks.push(cb) }
if(++r.batch.ed >= opt.batch){ return r.thrash() } // (2)
clearTimeout(r.batch.to); // (1)
r.batch.to = setTimeout(r.thrash, opt.wait);
}
r.batch = Radix();
r.batch.acks = [];
r.batch.ed = 0;
r.thrash = function(){
var thrash = r.thrash;
if(thrash.ing){ return thrash.more = true }
thrash.more = false;
thrash.ing = true;
var batch = thrash.at = r.batch, i = 0;
clearTimeout(r.batch.to);
r.batch = null;
r.batch = Radix();
r.batch.acks = [];
r.batch.ed = 0;
r.save(batch, function(err, ok){
if(++i > 1){ return }
if(err){ Gun.log(err) }
Gun.obj.map(batch.acks, function(cb){ cb(err, ok) });
thrash.at = null;
thrash.ing = false;
if(thrash.more){ thrash() }
});
}
@ -80,125 +89,255 @@ function Radisk(opt){
6. Merge and write all of those to the in-memory file and back to disk.
7. If file to large, split. More details needed here.
*/
function chunk(radix, cb){
var step = {
check: function(tree, key){
if(key < step.start){ return }
step.start = key;
fs.readdir(opt.file, step.match);
r.save = function(rad, cb){
var s = function Span(){};
s.find = function(tree, key){
if(key < s.start){ return }
s.start = key;
opt.store.list(s.lex);
return true;
}
s.lex = function(file){
if(!file || file > s.start){
s.mix(s.file || opt.code.from, s.start, s.end = file);
return true;
},
match: function(err, dir){
step.dir = dir;
if(!dir.length){
step.file = '0';
return step.merge(null, Radix());
}
Gun.obj.map(dir, step.lex);
read(step.file, step.merge);
},
lex: function(file){
if(file > step.start){
return step.end = file;
}
step.file = file;
},
merge: function(err, disk){
if(err){ return console.log("ERROR!!!", err) }
step.disk = disk;
Radix.map(radix, step.add);
write(step.file, step.disk, step.done);
},
add: function(val, key){
if(key < step.start){ return }
if(step.end && step.end < key){ return step.next = key; }
step.disk(key, val);
},
done: function(err){
if(err){ console.log("ERROR!!!", err) }
if(!step.next){
return cb(err);
}
step.start = step.next;
step.end = step.next = step.file = u;
Radix.map(radix, step.check);
}
s.file = file;
}
Radix.map(radix, step.check);
s.mix = function(file, start, end){
s.start = s.end = s.file = u;
r.parse(file, function(err, disk){
if(err){ return cb(err) }
Radix.map(rad, function(val, key){
if(key < start){ return }
if(end && end < key){ return s.start = key }
disk(key, val);
});
r.write(file, disk, s.next);
});
}
s.next = function(err, ok){
if(s.err = err){ return cb(err) }
if(s.start){ return Radix.map(rad, s.find) }
cb(err, ok);
}
Radix.map(rad, s.find);
}
var write = function(file, radix, cb){
var step = {
rest: "",
count: 0,
file: file,
each: function(val, key, k, pre){
step.count++;
if(opt.size < step.rest.length){
step.rest = "";
step.limit = Math.ceil(step.count/2);
step.count = 0;
step.sub = Radix();
Radix.map(radix, step.slice);
return true;
}
var i = pre.length;
while(i--){ step.rest += opt.nest };
step.rest += k + (u === val? '' : '=' + val) + '\n';
},
dump: function(){
var rest = step.rest;
step.rest = "";
fs.writeFile(opt.file +'/'+ file, rest, cb);
if(opt.disk){ opt.disk(opt.file+'/'+file, rest, cb) }
},
slice: function(val, key){
if(key < step.file){ return }
if(step.limit < (++step.count)){
var name = step.file;
step.file = key;
step.count = 0;
write(name, step.sub, step.next);
return true;
}
step.sub(key, val);
},
next: function(err){
if(err){ console.log("ERR!!!!") }
step.sub = Radix();
if(!Radix.map(radix, step.slice)){
write(step.file, step.sub, cb);
}
/*
Any storage engine at some point will have to do a read in order to write.
This is true of even systems that use an append only log, if they support updates.
Therefore it is unavoidable that a read will have to happen,
the question is just how long you delay it.
*/
r.write = function(file, rad, cb){
var f = function Fractal(){};
f.text = '';
f.count = 0;
f.file = file;
f.each = function(val, key, k, pre){
f.count++;
var enc = Radisk.encode(pre.length) +'#'+ Radisk.encode(k) + (u === val? '' : '='+ Radisk.encode(val)) +'\n';
if(opt.size < f.text.length + enc.length){
f.text = '';
f.limit = Math.ceil(f.count/2);
f.count = 0;
f.sub = Radix();
Radix.map(rad, f.slice);
return true;
}
f.text += enc;
}
f.write = function(){ opt.store.put(file, f.text, cb) }
f.slice = function(val, key){
if(key < f.file){ return }
if(f.limit < (++f.count)){
var name = f.file;
f.file = key;
f.count = 0;
r.write(name, f.sub, f.next);
return true;
}
f.sub(key, val);
}
f.next = function(err){
if(err){ return cb(err) }
f.sub = Radix();
if(!Radix.map(rad, f.slice)){
r.write(f.file, f.sub, cb);
}
}
if(!Radix.map(rad, f.each, true)){ f.write() }
}
r.read = function(key, cb){
// TODO: BUG!!! If a node spans multiple file chunks, it won't return all!
// TODO: BUG!!! If a node spans multiple file chunks, it won't return all!
// TODO: BUG!!! If a node spans multiple file chunks, it won't return all!
// TODO: BUG!!! If a node spans multiple file chunks, it won't return all!
// TODO: BUG!!! If a node spans multiple file chunks, it won't return all!
// TODO: BUG!!! If a node spans multiple file chunks, it won't return all!
// TODO: BUG!!! If a node spans multiple file chunks, it won't return all!
var id = Gun.text.random(3);
console.debug(4, 'read', key, !!RAD, id);
if(RAD){ // cache
var val = RAD(key);
if(u !== val){
return cb(u, val);
}
}
var g = function Get(){}, tmp;
g.lex = function(file){
if(!file || file > key){
if(tmp = q[g.file]){
tmp.push(cb);
return true;
}
q[g.file] = [cb];
r.parse(g.file, g.it);
return true;
}
g.file = file;
}
g.it = function(err, disk){
console.debug(5, 'readit', id);
if(g.err = err){ Gun.log(err) }
if(disk){
RAD = disk;
g.val = disk(key);
}
Gun.obj.map(q[g.file], g.ack);
Gun.obj.del(q[g.file]);
}
g.ack = function(ack){
ack(g.err, g.val);
}
opt.store.list(g.lex);
}
/*
Let us start by assuming we are the only process that is
changing the directory or bucket. Not because we do not want
to be multi-process/machine, but because we want to experiment
with how much performance and scale we can get out of only one.
Then we can work on the harder problem of being multi-process.
*/
r.parse = function(file, cb){
var p = function Parse(){}, s = String.fromCharCode(31);
p.disk = Radix();
p.read = function(err, data){ var tmp;
if(err){ return cb(err) }
if(!data){ return cb(u, p.disk) }
var tmp = p.split(data), pre = [], i, k, v;
while(tmp){
k = v = u;
i = tmp[1];
tmp = p.split(tmp[2])||'';
if('#' == tmp[0]){
k = tmp[1];
pre = pre.slice(0,i);
if(i <= pre.length){
pre.push(k);
}
}
tmp = p.split(tmp[2])||'';
if('\n' == tmp[0]){ continue }
if('=' == tmp[0]){ v = tmp[1] }
if(u !== k && u !== v){ p.disk(pre.join(''), v) }
tmp = p.split(tmp[2]);
}
cb(u, p.disk);
};
if(!Radix.map(radix, step.each, true)){ step.dump() }
}
var read = function(file, cb){
var step = {
nest: 0,
rad: Radix(),
data: function(err, data){
if(err){ return console.log("ERROR READING FILE!", err) }
step.pre = [];
Gun.obj.map(data.toString().split('\n'), step.split); // TODO: Escape!
cb(null, step.rad);
},
split: function(line){ var LINE = line;
var nest = -1; while(opt.nest === line[++nest]){};
if(nest){ line = line.slice(nest) }
if(nest <= step.nest){ step.pre = step.pre.slice(0, nest - step.nest - 1) }
line = line.split('='); step.pre.push(line[0]);
if(1 < line.length){ step.rad(step.pre.join(''), line[1]) }
step.nest = nest;
p.split = function(t){
if(!t){ return }
var l = [], o = {}, i = -1, a = '', b, c;
while(c = t[++i]){
if(s === c){ break }
a += c;
}
if(!c){ return }
l[0] = a;
l[1] = b = Radisk.decode(t.slice(i), o);
l[2] = t.slice(i + o.i);
return l;
}
fs.readFile(opt.file +'/'+ file, step.data);
opt.store.get(file, p.read);
}
radisk.read = read;
return radisk;
var q = {}, RAD, u;
return r;
}
;(function(){
s = String.fromCharCode(31);
Radisk.encode = function(d, o){
var t = s, tmp;
if(typeof d == 'string'){
var i = -1, c;
while(c = d[++i]){
if(s === c){
t += s;
}
}
return t + '"' + d + s;
} else
if(d && d['#'] && (tmp = Gun.val.rel.is(d))){
return t + '#' + tmp + t;
} else
if(Gun.num.is(d)){
return t + '+' + (d||0) + t;
} else
if(null === d){
return t + ' ' + t;
} else
if(true === d){
return t + '+' + t;
} else
if(false === d){
return t + '-' + t;
}// else
//if(binary){}
}
Radisk.decode = function(t, o){
var d = '', i = -1, n = 0, c, p;
if(s !== t[0]){ return }
while(c = t[++i]){
if(p){
if(s === c){
if(--n <= 0){
break;
}
}
d += c;
} else
if(s === c){
++n;
} else {
p = c || true;
}
}
if(o){ o.i = i+1 }
if('"' === p){
return d;
} else
if('#' === p){
return Gun.val.rel.ify(d);
} else
if('+' === p){
if(0 === d.length){
return true;
}
return parseFloat(d);
} else
if(' ' === p){
return null;
} else
if('-' === p){
return false;
}
}
}());
Radisk.Radix = Radix;
module.exports = Radisk;

242
lib/radisk_.js Normal file
View File

@ -0,0 +1,242 @@
var fs = require('fs');
var Gun = require('../gun');
var Radix = require('./radix');
function Radisk(opt){
/*
Any and all storage adapters should...
1. If not busy, write to disk immediately.
2. If busy, batch to disk. (Improves performance, reduces potential disk corruption)
3. If a batch exceeds a certain number of writes, force atomic batch to disk. (This caps total performance, but reduces potential loss)
*/
var radisk = function(key, val, cb){
key = ''+key;
if(0 <= key.indexOf('_') || 0 <= key.indexOf('$')){ // TODO: BUG! Fix!
var err = "ERROR: Radix and Radisk not tested against _ or $ keys!";
console.log(err);
cb = cb || val;
if(cb instanceof Function){ cb(err) }
return;
}
if(val instanceof Function){
cb = val;
val = radisk.batch(key);
if(u !== val){
return cb(null, val);
}
if(radisk.was){
val = radisk.was(key);
if(u !== val){
return cb(null, val);
}
}
console.log("READ FROM DISK");
return cb(null, val);
}
radisk.batch(key, val);
if(cb){ radisk.batch.acks.push(cb) }
if(!count++){ return thrash() } // (1)
if(opt.batch <= count){ return thrash() } // (3)
clearTimeout(to); // (2)
to = setTimeout(thrash, opt.wait);
};
radisk.batch = Radix();
radisk.batch.acks = [];
var count = 0, wait, to, u;
opt = opt || {};
opt.file = String(opt.file || 'radata');
opt.size = opt.size || (1024 * 1024 * 10); // 10MB
opt.batch = opt.batch || 10 * 1000;
opt.wait = opt.wait || 1;
opt.nest = opt.nest || ' ';
console.log("Warning: Radix storage engine has not been tested with all types of values and keys yet.");
if(!fs.existsSync(opt.file)){ fs.mkdirSync(opt.file) }
var thrash = function(){
if(wait){ return }
clearTimeout(to);
wait = true;
var was = radisk.was = radisk.batch;
radisk.batch = null;
radisk.batch = Radix();
radisk.batch.acks = [];
chunk(radisk.was, function(err, ok){
radisk.was = null;
wait = false;
var tmp = count;
count = 0;
Gun.obj.map(was.acks, function(cb){cb(err, ok)});
if(1 < tmp){ thrash() }
});
}
/*
1. Find the first radix item in memory.
2. Use that as the starting index in the directory of files.
3. Find the first file that is lexically larger than it,
4. Read the previous file to that into memory
5. Scan through the in memory radix for all values lexically less than the limit.
6. Merge and write all of those to the in-memory file and back to disk.
7. If file to large, split. More details needed here.
*/
function chunk(radix, cb){
var step = {
check: function(tree, key){
if(key < step.start){ return }
step.start = key;
fs.readdir(opt.file, step.match);
return true;
},
match: function(err, dir){
step.dir = dir;
if(!dir.length){
step.file = '0';
return step.merge(null, Radix());
}
Gun.obj.map(dir, step.lex);
read(step.file, step.merge);
},
lex: function(file){
if(file > step.start){
return step.end = file;
}
step.file = file;
},
merge: function(err, disk){
if(err){ return console.log("ERROR!!!", err) }
step.disk = disk;
Radix.map(radix, step.add);
write(step.file, step.disk, step.done);
},
add: function(val, key){
if(key < step.start){ return }
if(step.end && step.end < key){ return step.next = key; }
step.disk(key, val);
},
done: function(err){
if(err){ console.log("ERROR!!!", err) }
if(!step.next){
return cb(err);
}
step.start = step.next;
step.end = step.next = step.file = u;
Radix.map(radix, step.check);
}
}
Radix.map(radix, step.check);
}
/*
Any storage engine at some point will have to do a read in order to write.
This is true of even systems that use an append only log, if they support updates.
Therefore it is unavoidable that a read will have to happen,
the question is just how long you delay it.
*/
var write = function(file, radix, cb){
var step = {
rest: "",
count: 0,
file: file,
each: function(val, key, k, pre){
step.count++;
if(opt.size < step.rest.length){
step.rest = "";
step.limit = Math.ceil(step.count/2);
step.count = 0;
step.sub = Radix();
Radix.map(radix, step.slice);
return true;
}
var i = pre.length;
while(i--){ step.rest += opt.nest };
step.rest += encode(k) + (u === val? '' : '=' + encode(val)) + '\n';
},
dump: function(){
var rest = step.rest;
step.rest = "";
fs.writeFile(opt.file +'/'+ file, rest, cb);
console.log("DUMP");
if(opt.disk){ opt.disk(opt.file+'/'+file, rest, cb) }
},
slice: function(val, key){
if(key < step.file){ return }
if(step.limit < (++step.count)){
var name = step.file;
step.file = key;
step.count = 0;
write(name, step.sub, step.next);
return true;
}
step.sub(key, val);
},
next: function(err){
if(err){ console.log("ERR!!!!") }
step.sub = Radix();
if(!Radix.map(radix, step.slice)){
write(step.file, step.sub, cb);
}
}
};
if(!Radix.map(radix, step.each, true)){ step.dump() }
}
var split = String.fromCharCode(31), esc = JSON.stringify, fix = JSON.parse;
function encode(data){
return split + esc(data) + split;
}
function decode(data){
if(!data){ return '' }
var i = -1, c, f, t = '';
while(c = data[++i]){
if(f){
if(split === c){
try{ t = fix(t);
}catch(e){ t = '' }
return t;
}
t += c;
} else
if(split === c){
f = true;
}
}
return '';
}
/*
Let us start by assuming we are the only process that is
changing the directory or bucket. Not because we do not want
to be multi-process/machine, but because we want to experiment
with how much performance and scale we can get out of only one.
Then we can work on the harder problem of being multi-process.
*/
var read = function(file, cb){
var step = {
nest: 0,
rad: Radix(),
data: function(err, data){
if(err){ return console.log("ERROR READING FILE!", err) }
step.pre = [];
Gun.obj.map(data.toString().split('\n'), step.split); // TODO: Escape!
cb(null, step.rad);
},
split: function(line){ var LINE = line;
var nest = -1; while(opt.nest === line[++nest]){};
if(nest){ line = line.slice(nest) }
if(nest <= step.nest){ step.pre = step.pre.slice(0, nest - step.nest - 1) }
line = line.split('='); step.pre.push(line[0]);
if(1 < line.length){ step.rad(step.pre.join(''), line[1]) }
step.nest = nest;
}
}
fs.readFile(opt.file +'/'+ file, step.data);
}
radisk.read = read;
return radisk;
}
module.exports = Radisk;

View File

@ -1,57 +1,60 @@
var Gun = require('../gun');
var gbm = Gun.obj.map, no = {}, u;
function Radix(){
var radix = function(key, val, t){
t = t || radix._ || (radix._ = {});
var i = 0, l = key.length-1, k = key[i], at, tmp;
while(!(at = t[k]) && i < l){
k += key[++i];
}
if(!at){
if(!gbm(t, function(r, s){
var ii = 0, kk = '';
while(s[ii] == key[ii]){
kk += s[ii++];
}
if(kk){
if(u === val){ return (tmp || (tmp = {}))[s.slice(ii)] = r; }
var _ = {};
_[s.slice(ii)] = r;
_[key.slice(ii)] = {$: val};
t[kk] = {_: _};
delete t[s];
return true;
}
})){
if(u === val){ return; }
(t[k] || (t[k] = {})).$ = val;
} else
if(u === val){
return tmp;
}
} else
if(i == l){
if(u === val){ return (u === (tmp = at.$))? at._ : tmp }
at.$ = val;
} else {
return radix(key.slice(++i), val, at._ || (at._ = {}));
}
}
return radix;
};
;(function(){
var map = Gun.obj.map, no = {}, u;
var $ = String.fromCharCode(30), _ = String.fromCharCode(29);
function Radix(){
var radix = function(key, val, t){
t = t || radix[_] || (radix[_] = {});
var i = 0, l = key.length-1, k = key[i], at, tmp;
while(!(at = t[k]) && i < l){
k += key[++i];
}
if(!at){
if(u === val && i == l){ return }
if(!map(t, function(r, s){
var ii = 0, kk = '';
while(s[ii] == key[ii]){
kk += s[ii++];
}
if(kk){
if(u === val){ return (tmp || (tmp = {}))[s.slice(ii)] = r; }
var __ = {};
__[s.slice(ii)] = r;
(__[key.slice(ii)] = {})[$] = val;
(t[kk] = {})[_] = __;
delete t[s];
return true;
}
})){
if(u === val){ return; }
(t[k] || (t[k] = {}))[$] = val;
} else
if(u === val){
return tmp;
}
} else
if(i == l){
if(u === val){ return (u === (tmp = at[$]))? at[_] : tmp }
at[$] = val;
} else {
return radix(key.slice(++i), val, at[_] || (at[_] = {}));
}
}
return radix;
};
Radix.map = function map(radix, cb, opt, pre){ pre = pre || [];
var _ = radix._ || radix, keys = Object.keys(_).sort(), i = 0, l = keys.length;
for(;i < l; i++){ var key = keys[i], tree = _[key], tmp;
if(u !== (tmp = tree.$)){
var t = radix[_] || radix, keys = Object.keys(t).sort(), i = 0, l = keys.length;
for(;i < l; i++){ var key = keys[i], tree = t[key], tmp;
if(u !== (tmp = tree[$])){
tmp = cb(tmp, pre.join('') + key, key, pre);
if(u !== tmp){ return tmp }
} else
if(opt){
cb(u, pre.join(''), key, pre);
}
if(tmp = tree._){
if(tmp = tree[_]){
pre.push(key);
tmp = map(tmp, cb, opt, pre);
if(u !== tmp){ return tmp }
@ -59,7 +62,7 @@ function Radix(){
}
}
};
Object.keys = Object.keys || function(o){ return gbm(o, function(v,k,t){t(k)}) }
}());
module.exports = Radix;
Object.keys = Object.keys || function(o){ return map(o, function(v,k,t){t(k)}) }
module.exports = Radix;
}());

72
lib/rs3.js Normal file
View File

@ -0,0 +1,72 @@
var Gun = require('gun/gun');
var Radisk = require('./radisk');
var fs = require('fs');
var Radix = Radisk.Radix;
var u;
var AWS = require('aws-sdk');
Gun.on('opt', function(ctx){
this.to.next(ctx);
var opt = ctx.opt;
if(ctx.once){ return }
if(!process.env.AWS_S3_BUCKET){ return }
opt.batch = opt.batch || 10 * 1000;
opt.wait = opt.wait || 1000 * 15;
opt.size = opt.size || (1024 * 1024 * 10); // 10MB
var opts = opt.s3 || (opt.s3 = {});
opts.bucket = opts.bucket || process.env.AWS_S3_BUCKET;
opts.region = opts.region || process.AWS_REGION || "us-east-1";
opts.accessKeyId = opts.key = opts.key || opts.accessKeyId || process.env.AWS_ACCESS_KEY_ID;
opts.secretAccessKey = opts.secret = opts.secret || opts.secretAccessKey || process.env.AWS_SECRET_ACCESS_KEY;
if(opt.fakes3 = opt.fakes3 || process.env.fakes3){
opts.endpoint = opt.fakes3;
opts.sslEnabled = false;
opts.bucket = opts.bucket.replace('.','p');
}
opts.config = new AWS.Config(opts);
opts.s3 = opts.s3 || new AWS.S3(opts.config);
opt.store = opt.store || Store(opt);
});
function Store(opt){
opt = opt || {};
opt.file = String(opt.file || 'radata');
var opts = opt.s3, s3 = opts.s3;
var store = function Store(){};
store.put = function(file, data, cb){
var params = {Bucket: opts.bucket, Key: file, Body: data};
s3.putObject(params, cb);
};
store.get = function(file, cb){
var params = {Bucket: opts.bucket, Key: file};
s3.getObject(params, function(err, ack){
if(!ack){ return cb(err) }
var data = ack.Body;
if(data){ data = data.toString() }
console.log("HERE WE GO!", data);
cb(err, data);
});
};
store.list = function(cb, match, params){
params = params || {Bucket: opts.bucket};
s3.listObjectsV2(params, function(err, data){
if(err){ return Gun.log(err, err.stack) }
if(Gun.obj.map(data.Contents, function(content){
return cb(content.Key);
})){ return }
if(!data.IsTruncated){ return cb() } // Stream interface requires a final call to know when to be done.
params.ContinuationToken = data.NextContinuationToken;
console.log("get further list...");
store.list(cb, match, params);
});
};
//store.list(function(){ return true });
return store;
}
module.exports = Store;

104
lib/store.js Normal file
View File

@ -0,0 +1,104 @@
var Gun = require('gun/gun');
var Radisk = require('./radisk');
var fs = require('fs');
var Radix = Radisk.Radix;
var u;
Gun.on('opt', function(ctx){
this.to.next(ctx);
var opt = ctx.opt;
if(ctx.once){ return }
opt.store = opt.store || Store(opt);
var rad = Radisk(opt);
ctx.on('put', function(at){
this.to.next(at);
var id = at['#'], acks = at['@']? u : 0; // only ack non-acks.
Gun.graph.is(at.put, null, function(val, key, node, soul){
++acks;
val = Radisk.encode(val)+'>'+Radisk.encode(Gun.state.is(node, key));
rad(soul+'.'+key, val, (u === acks? u : ack));
});
function ack(err, ok){
acks--;
if(ack.err){ return }
if(ack.err = err){
ctx.on('in', {'@': id, err: Gun.log(err)});
return;
}
if(acks){ return }
ctx.on('in', {'@': id, ok: 1});
}
});
ctx.on('get', function(at){
this.to.next(at);
var id = at['#'], soul = at.get['#'], key = at.get['.']||'', tmp = soul+'.'+key, node;
console.debug(2,"store GET", tmp);
console.debug(1,"store GET", tmp);
rad(tmp, function(err, val){
console.debug(7,"store GOT", val);
if(val){
Radix.map(val, each);
if(!node){ each(val, key) }
}
console.debug(8,"store GOT", node);
console.debug(6,"store GOT", node);
ctx.on('in', {'@': id, put: Gun.graph.node(node), err: err? err : u});
});
function each(val, key){
tmp = val.lastIndexOf('>');
var state = Radisk.decode(val.slice(tmp+1));
val = Radisk.decode(val.slice(0,tmp));
node = Gun.state.ify(node, key, state, val, soul);
}
});
});
function Store(opt){
opt = opt || {};
opt.file = String(opt.file || 'radata');
var store = function Store(){};
store.put = function(file, data, cb){
fs.writeFile(opt.file+'.tmp', data, function(err, ok){
if(err){ return cb(err) }
fs.rename(opt.file+'.tmp', opt.file+'/'+file, cb);
});
};
store.get = function(file, cb){
fs.readFile(opt.file+'/'+file, function(err, data){
if(err){
if('ENOENT' === (err.code||'').toUpperCase()){
return cb(null);
}
Gun.log("ERROR:", err)
}
if(data){ data = data.toString() }
cb(err, data);
});
};
store.list = function(cb, match){
fs.readdir(opt.file, function(err, dir){
Gun.obj.map(dir, cb) || cb(); // Stream interface requires a final call to know when to be done.
});
};
if(!fs.existsSync(opt.file)){ fs.mkdirSync(opt.file) }
//store.list(function(){ return true });
return store;
}
module.exports = Store;
;(function(){//return;
global.Gun = require('../gun');
/*process.env.AWS_S3_BUCKET = 'test-s3';
process.env.AWS_ACCESS_KEY_ID = 'asdf';
process.env.AWS_SECRET_ACCESS_KEY = 'fdsa';
process.env.fakes3 = 'http://localhost:4567';
process.env.AWS_S3_THROTTLE = 0;*/
require('../test/abc');
}());

View File

@ -1,6 +1,6 @@
{
"name": "gun",
"version": "0.8.1",
"version": "0.8.8",
"description": "Graph engine",
"main": "index.js",
"browser": "gun.min.js",
@ -43,11 +43,8 @@
"url": "https://github.com/amark/gun/issues"
},
"homepage": "https://github.com/amark/gun#readme",
"engines": {
"node": ">=0.6.6"
},
"dependencies": {
"aws-sdk": ">=2.41.0",
"aws-sdk": ">=2.86.0",
"formidable": ">=1.1.1",
"ws": "~>2.2.3"
},

View File

@ -5,6 +5,11 @@ var root, noop = function(){}, u;
if(typeof window !== 'undefined'){ root = window }
var store = root.localStorage || {setItem: noop, removeItem: noop, getItem: noop};
/*
NOTE: Both `lib/file.js` and `lib/memdisk.js` are based on this design!
If you update anything here, consider updating the other adapters as well.
*/
Gun.on('opt', function(ctx){
this.to.next(ctx);
var opt = ctx.opt;
@ -56,7 +61,7 @@ Gun.on('opt', function(ctx){
var ack = acks;
acks = {};
try{store.setItem(opt.file, JSON.stringify(disk));
}catch(e){ err = e || "localStorage failure" }
}catch(e){ Gun.log(err = e || "localStorage failure") }
if(!err && !Gun.obj.empty(opt.peers)){ return } // only ack if there are no peers.
Gun.obj.map(ack, function(yes, id){
ctx.on('in', {

View File

@ -2,7 +2,7 @@
var Type = require('./type');
function Dup(opt){
var dup = {s:{}};
opt = opt || {max: 1000, age: 1000 * 60 * 2};
opt = opt || {max: 1000, age: 1000 * 9};//1000 * 60 * 2};
dup.check = function(id){
return dup.s[id]? dup.track(id) : false;
}

View File

@ -127,6 +127,22 @@ Gun.chain.off = function(){
}
return gun;
}
Gun.chain.off = function(){
var gun = this, at = gun._, tmp;
var back = at.back || {}, cat = back._;
if(!cat){ return }
if(tmp = cat.next){
if(tmp[at.get]){
obj_del(tmp, at.get);
} else {
}
}
if(tmp = at.soul){
obj_del(cat.root._.graph, tmp);
}
return gun;
}
var obj = Gun.obj, obj_has = obj.has, obj_del = obj.del, obj_to = obj.to;
var rel = Gun.val.rel;
var empty = {}, noop = function(){}, u;

View File

@ -17,6 +17,9 @@ module.exports = function onto(tag, arg, as){
this.to.back = this.back;
this.next = onto._.next;
this.back.to = this.to;
if(this.the.last === this.the){
delete this.on.tag[this.the.tag];
}
}),
to: onto._,
next: arg,

View File

@ -66,13 +66,13 @@ function ify(as){
function batch(){ var as = this;
if(!as.graph || obj_map(as.stun, no)){ return }
(as.res||iife)(function(){
var cat = (as.gun.back(-1)._), ask = cat.ask(function(ack){
this.off(); // One response is good enough for us currently. Later we may want to adjust this.
if(!as.ack){ return }
as.ack(ack, this);
}, as.opt);
(as.ref._).on('out', {
cap: 3,
gun: as.ref, put: as.out = as.env.graph, opt: as.opt,
'#': as.gun.back(-1)._.ask(function(ack){ this.off(); // One response is good enough for us currently. Later we may want to adjust this.
if(!as.ack){ return }
as.ack(ack, this);
}, as.opt)
gun: as.ref, put: as.out = as.env.graph, opt: as.opt, '#': ask
});
}, as);
if(as.res){ as.res() }

View File

@ -148,14 +148,21 @@ Gun._ = { // some reserved key words, these are not the only ones.
Gun.on.ask = function(cb, as){
if(!this.on){ return }
var id = text_rand(9);
if(cb){ this.on(id, cb, as) }
if(cb){
var to = this.on(id, cb, as);
to.err = setTimeout(function(){
to.next({err: "Error: No ACK received yet."});
to.off();
}, 1000 * 9); // TODO: Make configurable!!!
}
return id;
}
Gun.on.ack = function(at, reply){
if(!at || !reply || !this.on){ return }
var id = at['#'] || at;
if(!this.tag || !this.tag[id]){ return }
var id = at['#'] || at, tmp = (this.tag||empty)[id];
if(!tmp){ return }
this.on(id, reply);
clearTimeout(tmp.err);
return true;
}
}());

View File

@ -1642,6 +1642,7 @@ describe('Gun', function(){
done.to = setTimeout(function(){
expect(check.Fluffy).to.be('name');
expect(check.Frisky).to.be('name');
//console.log("????", gun._.graph);
Gun.obj.map(gun._.graph, function(n,s){
if('u/m/p/n/p' === s){ return }
var a = Gun.obj.map(n, function(v,f,t){t(v)});
@ -1724,7 +1725,6 @@ describe('Gun', function(){
}
});
setTimeout(function(){
//console.debug.i=1;console.log("-----------------------");
gun.get('u/m/mutate/n').get('alice').put({
_:{'#':'u/m/m/n/soul'},
name: 'Alice Zzxyz'
@ -3011,6 +3011,7 @@ describe('Gun', function(){
gun.get('val/follow').val(function(data){
//console.log("val", data);
}).get(function(at){
//console.log("?????", at);
if(done.c){ return } done.c = 1;
done();
});
@ -3018,29 +3019,28 @@ describe('Gun', function(){
});
it('map val get put', function(done){
var gun = Gun().get('chat/asdf');
var check = {}, count = {};
gun.map().val(function(v,f){
check[f] = v;
count[f] = (count[f] || 0) + 1;
if(check['1_1'] && check['2_2']){
// console.log("**************", f, v);
if(check['1-1'] && check['2-2']){
clearTimeout(done.to);
done.to = setTimeout(function(){
expect(check['1_1'].what).to.be('hi');
expect(check['2_2'].what).to.be('you.');
expect(count['1_1']).to.be(1);
expect(count['2_2']).to.be(1);
expect(check['1-1'].what).to.be('hi');
expect(check['2-2'].what).to.be('you.');
expect(count['1-1']).to.be(1);
expect(count['2-2']).to.be(1);
done();
},50);
}
});
setTimeout(function(){
gun.get('1_1').put({what: "hi"});
gun.get('1-1').put({what: "hi"});
setTimeout(function(){
gun.get('2_2').put({what: "you."});
gun.get('2-2').put({what: "you."});
},40);
},40);
});
@ -3132,6 +3132,7 @@ describe('Gun', function(){
salt: 'random'
})
);
//return;
setTimeout(function(){
gun.get(function(at){
//console.log('*', at.put);
@ -3144,8 +3145,9 @@ describe('Gun', function(){
gun.get('alias').get(function(at){
//console.log("***", at.put);
done.alias = done.alias || at.put.mark;
//!console.debug.i&&(console.debug.i=1)&&console.log("---------------------");
}).get('mark').get(function(at){
//console.log("************", at.put);return;
//console.log("************", at.put);//return;
setTimeout(function(){
done.mark = done.mark || at.put.pub;
expect(Gun.val.rel.is(done.mark)).to.be('pub');
@ -3160,6 +3162,7 @@ describe('Gun', function(){
});
it('get put get get put reload get get then get', function(done){
this.timeout(6000);
var gun = Gun();
gun.get('stef').put({name:'Stef'});
@ -3172,7 +3175,10 @@ describe('Gun', function(){
// reload
setTimeout(function(){
var gun2 = Gun();
//console.log(require('fs').readFileSync('./radata/!').toString());
//console.debug.i=1;console.log("-----------------");
gun2.get('stef').get('address').val(function(data){ // Object {_: Object, country: "Netherlands", zip: "1766KP"} "adress"
//console.log("******", data);
done.a = true;
expect(data.country).to.be('Netherlands');
expect(data.zip).to.be('999999');
@ -3181,6 +3187,8 @@ describe('Gun', function(){
done();
});
gun2.get('stef').val(function(data){ //Object {_: Object, address: Object} "stef"
//console.log("**************", data);
//return;
done.s = true;
expect(data.name).to.be('Stef');
expect(data.address).to.be.ok();
@ -3188,7 +3196,7 @@ describe('Gun', function(){
if(done.c){ return } done.c = 1;
done();
});
},300);
},5000);
});
it('get get get any parallel', function(done){
@ -3201,7 +3209,7 @@ describe('Gun', function(){
}, s)});
gun.get('parallel').get('bob').get('age').get(function(at, ev){
var err = at.err, data = at.put, field = at.get;
//console.log("***** age", data, at.gun._.ack);return;
//console.log("***** age", data, at.gun._.ack);//return;
expect(data).to.be(29);
expect(field).to.be('age');
done.age = true;
@ -3276,6 +3284,7 @@ describe('Gun', function(){
done.age = true;
});
setTimeout(function(){
//console.debug.i=1;console.log('---------------');
gun.get('parallel/not/later').get('bob').get('name').get(function(at, ev){
var err = at.err, data = at.put, field = at.get;
//console.log("*********** name", field, data);
@ -3382,11 +3391,13 @@ describe('Gun', function(){
var app = gun.get(s.soul);
app.get('alias').get('mark').map().val(function(alias){
//console.log("***", alias);
done.alias = alias;
});
setTimeout(function(){
app.get('alias').map().map().get('born').on(function(data){
//console.log("*******", data);
expect(data).to.be(1);
expect(done.alias.pub).to.be("asdf");
expect(done.alias.alias).to.be("mark");
@ -3394,11 +3405,32 @@ describe('Gun', function(){
if(done.c){ return } done.c = 1;
done();
});
},300);
},400);
});
it('put on a put', function(done){
var gun = Gun();
var foo = gun.get('put/on/put').get('a').get('b');
var bar = gun.get('put/on/put/ok').get('a').get('b');
bar.put({a:1})
bar.on(function(data){
if(1 === data.a && 3 === data.c){
if(done.c){ return } done.c = 1;
done();
}
});
foo.on(function(ack){
bar.put({c:3});
});
foo.put({b:2});
});
it('map with map function', function(done){
console.debug.i=0;
var gun = Gun(), s = 'map/mapfunc', u;
var app = gun.get(s);
var list = app.get('list');
@ -3474,13 +3506,13 @@ describe('Gun', function(){
Gun.on('opt', function(ctx){
ctx.on('out', function(msg){
this.to.next(msg);
var onGun = msg.gun.back(-1);
if(onGun === b) {
var onGun = ctx;
if(onGun.gun === b) {
if(d){
//console.log("b can send to d....", Gun.obj.copy(msg));
d.on("in", msg);
}
} else if(onGun === d){
} else if(onGun.gun === d){
//console.log("d sends to b....", Gun.obj.copy(msg));
b.on("in", msg);
}

View File

@ -1,71 +1,115 @@
var Radix = require('../../lib/radix');
var Radisk = require('../../lib/radisk');
var Store = require('../../lib/store');
//var Store = require('../../lib/rs3');
var Gun = require('../../gun');
var fs = require('fs');
var TOTAL = 1000000;
var TOTAL = 25000;
var c = 0;
var acked = 0;
var start;
var diff;
(function(){//return;
var radix = Radisk();
var gtr = Gun()._.opt.uuid;
var l = 500000;
//var opt = {size: 1024 * 1024};
//opt.store = Store(opt);
//var radix = Radisk();
var gun = Gun();
var gtr = gun._.opt.uuid;
var l = 2000;
var last = start;
var t = Gun.time.is;
var at = c;
;(function(){
start = Gun.time.is();
gun.get('j59an5jj2LUW8IJXl0u3').get('foo').on(function(data){
/*Radix.map(data, function(val, key){
console.log('>>>', key, val);
})*/
console.log("************", data, 'in', (Gun.time.is() - start)/1000);
setTimeout(function(){
console.debug.i=1;console.log("----------------------");
start = Gun.time.is();
gun.get('j59an5jj2LUW8IJXl0u3').on(function(data){
console.log("*****", data, 'in', (Gun.time.is() - start)/1000);
})
},2000);
});
}());
return;
var toc, alldone = function(){
acked++;
if(acked < TOTAL){ return }
diff = (Gun.time.is() - start) / 1000;
clearTimeout(toc);
toc = setTimeout(CHECK,1000);
toc = setTimeout(CHECK,5000);
}
function bench(){
if(c >= (TOTAL)){ return clearInterval(it); }
for(var i = 0; i < l; i++){
radix(++c, gtr(), alldone);
if(c % 50000 === 0){
var now = t();
console.log(c);//, (now - last)/1000);
at = c;
last = now;
}
act(i);
}
}
start = Gun.time.is();
var it = setInterval(bench, 1);
function act(i){
if(c >= (TOTAL)){ clearInterval(it); return; }
++c;
var ref = gun.get(gtr());
ref.put({
val: c,
foo: 'hello ' + c,
why: c + 'not?'
}, alldone);
setTimeout(function(){ ref.off(); },1);
//radix(gtr(), Gun.text.random(3), alldone);
//radix(c, Math.random()/* + '\n' + Gun.text.random(3)*/, alldone);
//radix(Gun.text.random(5), Math.random(), alldone);
if(c % 50000 === 0){
var now = t();
console.log(c);//, (now - last)/1000);
at = c;
last = now;
}
}
}());
function CHECK(){
console.log(Math.floor(c / diff), 'disk writes per second');
var disk = Radisk();
console.log(Math.floor(c / diff), 'disk writes per second', acked, 'of', c);
var opt = {batch: 5, wait: 500};
opt.store = Store(opt);
var radix = Radisk(opt);
var all = {};
var to;
var i = TOTAL;
var i = c || TOTAL;
/*while(--i){
all[i] = true;
}*/
var dir = fs.readdirSync('radata');
dir.forEach(function(file){
disk.read(file, function(err, rad){
var dir = fs.readdirSync('radata'), i = 0;
function readcheck(i){
var file = dir[i];
if(!file){
var len = Object.keys(all).length;
console.log("how many?", len);
if(len < TOTAL){ return }
var missing = [];
var fail = Gun.obj.map(all, function(val, key){
if(val){ missing.push(key); return true }
});
//console.log(all);
console.log("DONE!", 'Verify ALL writes:', fail? '!!!FAIL!!!!' : 'YES');// '. Missing:', missing);
return;
}
radix.parse(file, function(err, rad){
Radix.map(rad, function(val, key){
all[key] = false;
clearTimeout(to);
to = setTimeout(function(){
var len = Object.keys(all).length;
console.log("how many?", len);
if(len < TOTAL){ return }
var missing = [];
var fail = Gun.obj.map(all, function(val, key){
if(val){ missing.push(key); return true }
});
//console.log(all);
console.log("DONE!", 'Verify ALL writes:', fail? '!!!FAIL!!!!' : 'YES');// '. Missing:', missing);
},1000);
})
});
readcheck(++i);
})
})
}
readcheck(i);
}