|
@@ -0,0 +1,525 @@
|
|
|
+;(function(){
|
|
|
+ console.log("RADISK 2!!!!");
|
|
|
+
|
|
|
+ function Radisk(opt){
|
|
|
+
|
|
|
+ opt = opt || {};
|
|
|
+ opt.log = opt.log || console.log;
|
|
|
+ opt.file = String(opt.file || 'radata');
|
|
|
+ var has = (Radisk.has || (Radisk.has = {}))[opt.file];
|
|
|
+ if(has){ return has }
|
|
|
+
|
|
|
+ opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB.
|
|
|
+ opt.until = opt.until || opt.wait || 250;
|
|
|
+ opt.batch = opt.batch || (10 * 1000);
|
|
|
+ opt.chunk = opt.chunk || (1024 * 1024 * 10); // 10MB
|
|
|
+ opt.code = opt.code || {};
|
|
|
+ opt.code.from = opt.code.from || '!';
|
|
|
+ //opt.jsonify = true; // TODO: REMOVE!!!!
|
|
|
+
|
|
|
+ function ename(t){ return encodeURIComponent(t).replace(/\*/g, '%2A') }
|
|
|
+ function atomic(v){ return u !== v && (!v || 'object' != typeof v) }
|
|
|
+ var map = Gun.obj.map;
|
|
|
+ var LOG = false;
|
|
|
+
|
|
|
+ if(!opt.store){
|
|
|
+ return opt.log("ERROR: Radisk needs `opt.store` interface with `{get: fn, put: fn (, list: fn)}`!");
|
|
|
+ }
|
|
|
+ if(!opt.store.put){
|
|
|
+ return opt.log("ERROR: Radisk needs `store.put` interface with `(file, data, cb)`!");
|
|
|
+ }
|
|
|
+ if(!opt.store.get){
|
|
|
+ return opt.log("ERROR: Radisk needs `store.get` interface with `(file, cb)`!");
|
|
|
+ }
|
|
|
+ if(!opt.store.list){
|
|
|
+ //opt.log("WARNING: `store.list` interface might be needed!");
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ 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){
|
|
|
+ var o = cb || {};
|
|
|
+ cb = val;
|
|
|
+ val = r.batch(key);
|
|
|
+ if(u !== val){
|
|
|
+ cb(u, r.range(val, o), o);
|
|
|
+ if(atomic(val)){ return }
|
|
|
+ // if a node is requested and some of it is cached... the other parts might not be.
|
|
|
+ }
|
|
|
+ if(r.thrash.at){
|
|
|
+ val = r.thrash.at(key);
|
|
|
+ if(u !== val){
|
|
|
+ cb(u, r.range(val, o), o);
|
|
|
+ if(atomic(val)){ cb(u, val, o); return }
|
|
|
+ // if a node is requested and some of it is cached... the other parts might not be.
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return r.read(key, cb, o);
|
|
|
+ }
|
|
|
+ r.batch(key, val);
|
|
|
+ if(cb){ r.batch.acks.push(cb) }
|
|
|
+ if(++r.batch.ed >= opt.batch){ return r.thrash() } // (2)
|
|
|
+ if(r.batch.to){ return }
|
|
|
+ //clearTimeout(r.batch.to); // (1) // THIS LINE IS EVIL! NEVER USE IT! ALSO NEVER DELETE THIS SO WE NEVER MAKE THE SAME MISTAKE AGAIN!
|
|
|
+ r.batch.to = setTimeout(r.thrash, opt.until || 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ 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;
|
|
|
+ //var id = Gun.text.random(2), S = (+new Date); console.log("<<<<<<<<<<<<", id);
|
|
|
+ r.save(batch, function(err, ok){
|
|
|
+ if(++i > 1){ opt.log('RAD ERR: Radisk has callbacked multiple times, please report this as a BUG at github.com/amark/gun/issues ! ' + i); return }
|
|
|
+ if(err){ opt.log('err', err) }
|
|
|
+ //console.log(">>>>>>>>>>>>", id, ((+new Date) - S), batch.acks.length);
|
|
|
+ map(batch.acks, function(cb){ cb(err, ok) });
|
|
|
+ thrash.at = null;
|
|
|
+ thrash.ing = false;
|
|
|
+ if(thrash.more){ 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 too large, split. More details needed here.
|
|
|
+ */
|
|
|
+ r.save = function(rad, cb){
|
|
|
+ var s = function Span(){};
|
|
|
+ s.find = function(tree, key){
|
|
|
+ if(key < s.start){ return }
|
|
|
+ s.start = key;
|
|
|
+ r.list(s.lex);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ s.lex = function(file){
|
|
|
+ file = (u === file)? u : decodeURIComponent(file);
|
|
|
+ if(!file || file > s.start){
|
|
|
+ s.mix(s.file || opt.code.from, s.start, s.end = file);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ s.file = file;
|
|
|
+ }
|
|
|
+ s.mix = function(file, start, end){
|
|
|
+ s.start = s.end = s.file = u;
|
|
|
+ r.parse(file, function(err, disk){
|
|
|
+ if(err){ return cb(err) }
|
|
|
+ disk = disk || Radix();
|
|
|
+ Radix.map(rad, function(val, key){
|
|
|
+ if(key < start){ return }
|
|
|
+ if(end && end < key){ return s.start = key }
|
|
|
+ // PLUGIN: consider adding HAM as an extra layer of protection
|
|
|
+ disk(key, val); // merge batch[key] -> disk[key]
|
|
|
+ });
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ 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, o){
|
|
|
+ o = ('object' == typeof o)? o : {force: o};
|
|
|
+ var f = function Fractal(){};
|
|
|
+ f.text = '';
|
|
|
+ f.count = 0;
|
|
|
+ f.file = file;
|
|
|
+ f.each = function(val, key, k, pre){
|
|
|
+ //console.log("RAD:::", JSON.stringify([val, key, k, pre]));
|
|
|
+ if(u !== val){ f.count++ }
|
|
|
+ if(opt.pack <= (val||'').length){ return cb("Record too big!"), true }
|
|
|
+ var enc = Radisk.encode(pre.length) +'#'+ Radisk.encode(k) + (u === val? '' : ':'+ Radisk.encode(val)) +'\n';
|
|
|
+ if((opt.chunk < f.text.length + enc.length) && (1 < f.count) && !o.force){
|
|
|
+ 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(){
|
|
|
+ var tmp = ename(file);
|
|
|
+ var start; LOG && (start = (+new Date)); // comment this out!
|
|
|
+ opt.store.put(tmp, f.text, function(err){
|
|
|
+ LOG && console.log("wrote JSON in", (+new Date) - start); // comment this out!
|
|
|
+ if(err){ return cb(err) }
|
|
|
+ r.list.add(tmp, 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, o);
|
|
|
+ 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, o);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(opt.jsonify){ return r.write.jsonify(f, file, rad, cb, o) } // temporary testing idea
|
|
|
+ if(!Radix.map(rad, f.each, true)){ f.write() }
|
|
|
+ }
|
|
|
+
|
|
|
+ r.write.jsonify = function(f, file, rad, cb, o){
|
|
|
+ var raw;
|
|
|
+ var start; LOG && (start = (+new Date)); // comment this out!
|
|
|
+ try{raw = JSON.stringify(rad.$);
|
|
|
+ }catch(e){ return cb("Record too big!") }
|
|
|
+ LOG && console.log("stringified JSON in", (+new Date) - start); // comment this out!
|
|
|
+ if(opt.chunk < raw.length && !o.force){
|
|
|
+ if(Radix.map(rad, f.each, true)){ return }
|
|
|
+ }
|
|
|
+ f.text = raw;
|
|
|
+ f.write();
|
|
|
+ }
|
|
|
+
|
|
|
+ r.range = function(tree, o){
|
|
|
+ if(!tree || !o){ return }
|
|
|
+ if(u === o.start && u === o.end){ return tree }
|
|
|
+ if(atomic(tree)){ return tree }
|
|
|
+ var sub = Radix();
|
|
|
+ Radix.map(tree, function(v,k){
|
|
|
+ sub(k,v);
|
|
|
+ }, o)
|
|
|
+ return sub('');
|
|
|
+ }
|
|
|
+
|
|
|
+ ;(function(){
|
|
|
+ var Q = {};
|
|
|
+ r.read = function(key, cb, o){
|
|
|
+ o = o || {};
|
|
|
+ if(RAD && !o.next){ // cache
|
|
|
+ var val = RAD(key);
|
|
|
+ //if(u !== val){
|
|
|
+ //cb(u, val, o);
|
|
|
+ if(atomic(val)){ cb(u, val, o); return }
|
|
|
+ // if a node is requested and some of it is cached... the other parts might not be.
|
|
|
+ //}
|
|
|
+ }
|
|
|
+ o.span = (u !== o.start) || (u !== o.end);
|
|
|
+ var g = function Get(){};
|
|
|
+ g.lex = function(file){ var tmp;
|
|
|
+ file = (u === file)? u : decodeURIComponent(file);
|
|
|
+ tmp = o.next || key || (o.reverse? o.end || '\uffff' : o.start || '');
|
|
|
+ if(!file || (o.reverse? file < tmp : file > tmp)){
|
|
|
+ if(o.next || o.reverse){ g.file = file }
|
|
|
+ if(tmp = Q[g.file]){
|
|
|
+ tmp.push({key: key, ack: cb, file: g.file, opt: o});
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ Q[g.file] = [{key: key, ack: cb, file: g.file, opt: o}];
|
|
|
+ if(!g.file){
|
|
|
+ g.it(null, u, {});
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ r.parse(g.file, g.it);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ g.file = file;
|
|
|
+ }
|
|
|
+ g.it = function(err, disk, info){
|
|
|
+ if(g.err = err){ opt.log('err', err) }
|
|
|
+ g.info = info;
|
|
|
+ if(disk){ RAD = g.disk = disk }
|
|
|
+ disk = Q[g.file]; delete Q[g.file];
|
|
|
+ map(disk, g.ack);
|
|
|
+ }
|
|
|
+ g.ack = function(as){
|
|
|
+ if(!as.ack){ return }
|
|
|
+ var tmp = as.key, o = as.opt, info = g.info, rad = g.disk || noop, data = r.range(rad(tmp), o), last = rad.last;
|
|
|
+ o.parsed = (o.parsed || 0) + (info.parsed||0);
|
|
|
+ o.chunks = (o.chunks || 0) + 1;
|
|
|
+ if(!o.some){ o.some = (u !== data) }
|
|
|
+ if(u !== data){ as.ack(g.err, data, o) }
|
|
|
+ else if(!as.file){ !o.some && as.ack(g.err, u, o); return }
|
|
|
+ if(!o.span){
|
|
|
+ if(/*!last || */last === tmp){ !o.some && as.ack(g.err, u, o); return }
|
|
|
+ if(last && last > tmp && 0 != last.indexOf(tmp)){ !o.some && as.ack(g.err, u, o); return }
|
|
|
+ }
|
|
|
+ if(o.some && o.parsed >= o.limit){ return }
|
|
|
+ o.next = as.file;
|
|
|
+ r.read(tmp, as.ack, o);
|
|
|
+ }
|
|
|
+ if(o.reverse){ g.lex.reverse = true }
|
|
|
+ r.list(g.lex);
|
|
|
+ }
|
|
|
+ }());
|
|
|
+
|
|
|
+ ;(function(){
|
|
|
+ /*
|
|
|
+ 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 Q = {}, s = String.fromCharCode(31);
|
|
|
+ r.parse = function(file, cb, raw){ var q;
|
|
|
+ if(q = Q[file]){ return q.push(cb) } q = Q[file] = [cb];
|
|
|
+ var p = function Parse(){}, info = {};
|
|
|
+ p.disk = Radix();
|
|
|
+ p.read = function(err, data){ var tmp;
|
|
|
+ delete Q[file];
|
|
|
+ if((p.err = err) || (p.not = !data)){
|
|
|
+ return map(q, p.ack);
|
|
|
+ }
|
|
|
+ if(typeof data !== 'string'){
|
|
|
+ try{
|
|
|
+ if(opt.pack <= data.length){
|
|
|
+ p.err = "Chunk too big!";
|
|
|
+ } else {
|
|
|
+ data = data.toString(); // If it crashes, it crashes here. How!?? We check size first!
|
|
|
+ }
|
|
|
+ }catch(e){ p.err = e }
|
|
|
+ if(p.err){ return map(q, p.ack) }
|
|
|
+ }
|
|
|
+ info.parsed = data.length;
|
|
|
+
|
|
|
+ var start; LOG && (start = (+new Date)); // keep this commented out in production!
|
|
|
+ if(opt.jsonify){ // temporary testing idea
|
|
|
+ try{
|
|
|
+ var json = JSON.parse(data);
|
|
|
+ p.disk.$ = json;
|
|
|
+ LOG && console.log('parsed JSON in', (+new Date) - start); // keep this commented out in production!
|
|
|
+ map(q, p.ack);
|
|
|
+ return;
|
|
|
+ }catch(e){ tmp = e }
|
|
|
+ if('{' === data[0]){
|
|
|
+ p.err = tmp || "JSON error!";
|
|
|
+ return map(q, p.ack);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ var start; LOG && (start = (+new Date)); // keep this commented out in production!
|
|
|
+ var tmp = p.split(data), pre = [], i, k, v, at, ats=[];
|
|
|
+ if(!tmp || 0 !== tmp[1]){
|
|
|
+ p.err = "File '"+file+"' does not have root radix! ";
|
|
|
+ return map(q, p.ack);
|
|
|
+ }
|
|
|
+ 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]){
|
|
|
+ at = ats[i] || p.disk.at;
|
|
|
+ p.disk(k, u, at);
|
|
|
+ ats[i] = p.disk.at;
|
|
|
+ ats[i+1] = p.disk.at[k] || (p.disk.at[k]={});
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if('=' == tmp[0] || ':' == tmp[0]){ v = tmp[1] }
|
|
|
+ if(u !== k && u !== v){
|
|
|
+// p.disk(pre.join(''), v)// mark's code
|
|
|
+ at = ats[i];// || p.disk.at;
|
|
|
+ p.disk(k, v, at);
|
|
|
+ ats[i] = p.disk.at;
|
|
|
+ ats[i+1] = p.disk.at[k];
|
|
|
+ }
|
|
|
+ tmp = p.split(tmp[2]);
|
|
|
+ }
|
|
|
+ LOG && console.log('parsed JSON in', (+new Date) - start); // keep this commented out in production!
|
|
|
+ //cb(err, p.disk);
|
|
|
+ map(q, p.ack);
|
|
|
+ };
|
|
|
+ p.split = function(t){
|
|
|
+ if(!t){ return }
|
|
|
+ var l = [], o = {}, i = -1, a = '', b, c;
|
|
|
+ i = t.indexOf(s);
|
|
|
+ if(!t[i]){ return }
|
|
|
+ a = t.slice(0, i);
|
|
|
+ l[0] = a;
|
|
|
+ l[1] = b = Radisk.decode(t.slice(i), o);
|
|
|
+ l[2] = t.slice(i + o.i);
|
|
|
+ return l;
|
|
|
+ }
|
|
|
+ p.ack = function(cb){
|
|
|
+ if(!cb){ return }
|
|
|
+ if(p.err || p.not){ return cb(p.err, u, info) }
|
|
|
+ cb(u, p.disk, info);
|
|
|
+ }
|
|
|
+ if(raw){ return p.read(null, raw) }
|
|
|
+ opt.store.get(ename(file), p.read);
|
|
|
+ }
|
|
|
+ }());
|
|
|
+
|
|
|
+ ;(function(){
|
|
|
+ var dir, q, f = String.fromCharCode(28), ef = ename(f);
|
|
|
+ r.list = function(cb){
|
|
|
+ if(dir){
|
|
|
+ var tmp = {reverse: (cb.reverse)? 1 : 0};
|
|
|
+ Radix.map(dir, function(val, key){
|
|
|
+ return cb(key);
|
|
|
+ }, tmp) || cb();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if(q){ return q.push(cb) } q = [cb];
|
|
|
+ r.parse(f, r.list.init);
|
|
|
+ }
|
|
|
+ r.list.add = function(file, cb){
|
|
|
+ var has = dir(file);
|
|
|
+ if(has || file === ef){
|
|
|
+ return cb(u, 1);
|
|
|
+ }
|
|
|
+ dir(file, true);
|
|
|
+ cb.listed = (cb.listed || 0) + 1;
|
|
|
+ r.write(f, dir, function(err, ok){
|
|
|
+ if(err){ return cb(err) }
|
|
|
+ cb.listed = (cb.listed || 0) - 1;
|
|
|
+ if(cb.listed !== 0){ return }
|
|
|
+ cb(u, 1);
|
|
|
+ }, true);
|
|
|
+ }
|
|
|
+ r.list.init = function(err, disk){
|
|
|
+ if(err){
|
|
|
+ opt.log('list', err);
|
|
|
+ setTimeout(function(){ r.parse(f, r.list.init) }, 1000);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if(disk){
|
|
|
+ r.list.drain(disk);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if(!opt.store.list){
|
|
|
+ r.list.drain(Radix());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // import directory.
|
|
|
+ opt.store.list(function(file){
|
|
|
+ dir = dir || Radix();
|
|
|
+ if(!file){ return r.list.drain(dir) }
|
|
|
+ r.list.add(file, noop);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ r.list.drain = function(rad, tmp){
|
|
|
+ r.list.dir = dir = rad;
|
|
|
+ tmp = q; q = null;
|
|
|
+ Gun.list.map(tmp, function(cb){
|
|
|
+ r.list(cb);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }());
|
|
|
+
|
|
|
+ var noop = function(){}, RAD, u;
|
|
|
+ Radisk.has[opt.file] = r;
|
|
|
+ return r;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ ;(function(){
|
|
|
+ var _ = String.fromCharCode(31), u;
|
|
|
+ Radisk.encode = function(d, o, s){ s = s || _;
|
|
|
+ var t = s, tmp;
|
|
|
+ if(typeof d == 'string'){
|
|
|
+ var i = d.indexOf(s);
|
|
|
+ while(i != -1){ t += s; i = d.indexOf(s, i+1) }
|
|
|
+ return t + '"' + d + s;
|
|
|
+ } else
|
|
|
+ if(d && d['#'] && (tmp = Gun.val.link.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, s){ s = s || _;
|
|
|
+ var d = '', i = -1, n = 0, c, p;
|
|
|
+ if(s !== t[0]){ return }
|
|
|
+ while(s === t[++i]){ ++n }
|
|
|
+ p = t[c = n] || true;
|
|
|
+ while(--n >= 0){ i = t.indexOf(s, i+1) }
|
|
|
+ if(i == -1){ i = t.length }
|
|
|
+ d = t.slice(c+1, i);
|
|
|
+ if(o){ o.i = i+1 }
|
|
|
+ if('"' === p){
|
|
|
+ return d;
|
|
|
+ } else
|
|
|
+ if('#' === p){
|
|
|
+ return Gun.val.link.ify(d);
|
|
|
+ } else
|
|
|
+ if('+' === p){
|
|
|
+ if(0 === d.length){
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return parseFloat(d);
|
|
|
+ } else
|
|
|
+ if(' ' === p){
|
|
|
+ return null;
|
|
|
+ } else
|
|
|
+ if('-' === p){
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }());
|
|
|
+
|
|
|
+ if(typeof window !== "undefined"){
|
|
|
+ var Gun = window.Gun;
|
|
|
+ var Radix = window.Radix;
|
|
|
+ window.Radisk = Radisk;
|
|
|
+ } else {
|
|
|
+ var Gun = require('../gun');
|
|
|
+ var Radix = require('./radix2');
|
|
|
+ try{ module.exports = Radisk }catch(e){}
|
|
|
+ }
|
|
|
+
|
|
|
+ Radisk.Radix = Radix;
|
|
|
+
|
|
|
+}());
|