diff --git a/lib/book.js b/lib/book.js index 12936305..8b67cc6f 100644 --- a/lib/book.js +++ b/lib/book.js @@ -28,6 +28,7 @@ var sT = setTimeout, B = sT.Book || (sT.Book = function(text){ function page(word){ var b = this, l = b.list, i = spot(B.encode(word), l), p = l[i]; if('string' == typeof p){ l[i] = p = {size: -1, first: p, substring: sub, toString: to, book: b, get: b} } // TODO: test, how do we arrive at this condition again? + p.i = i; return p; // TODO: BUG! What if we get the page, it turns out to be too big & split, we must then RE get the page! } @@ -81,7 +82,7 @@ function set(word, is){ page.sort = 1; b(word, is); page.size += size(word) + size(is); - if(PAGE < page.size){ split(page, b) } + if((b.PAGE || PAGE) < page.size){ split(page, b) } return b; } @@ -112,16 +113,16 @@ function size(t){ return (t||'').length||1 } // bits/numbers less size? Bug or f function subt(i,j){ return this.word } //function tot(){ return this.text = this.text || "'"+(this.word)+"'"+(this.is)+"'" } function tot(){ var tmp; - if((tmp = this.page) && tmp.saving){ delete tmp.book.all[this.word]; } + //if((tmp = this.page) && tmp.saving){ delete tmp.book.all[this.word]; } // TODO: BUG! Book can't know about RAD, this was from RAD, so this MIGHT be correct but we need to refactor. Make sure to add tests that will re-trigger this. return this.text = this.text || "'"+(this.word)+"'"+(this.is)+"'"; } function sub(i,j){ return (this.first||this.word||(from(this)||'')[0]||'').substring(i,j) } function to(){ return this.text = this.text || text(this) } function join(){ return this.join('|') } function text(p){ - if(!p.list){ return (p.from||'')+'' } - if(!p.from){ return '|'+((p.list && (p.list = p.list.sort()).join('|'))||'')+'|' } - return '|'+/*from(p).concat(p.list)*/p.list.sort().join('|')+'|'; // commenting out this sub-portion of code fixed a more basic test, but will probably cause a bug with a FROM + MEMORY. + if(!p.list){ return (typeof p.from == 'string')? (p.from||'')+'' : '|'+p.from+'|' } + if(!p.from){ return '|'+((p.list && (p.list = p.list.sort()).join('|'))||'')+'|' } + return '|'+from(p).concat(p.list).sort().join('|')+'|'; // commenting out this sub-portion of code fixed a more basic test, but will probably cause a bug with a FROM + MEMORY. } B.encode = function(d, _){ _ = _ || "'"; @@ -144,4 +145,4 @@ B.hash = function(s, c){ // via SO } try{module.exports=B}catch(e){} -}()); +}()); \ No newline at end of file diff --git a/lib/radisk3.js b/lib/radisk3.js index c0045fa3..111983f1 100644 --- a/lib/radisk3.js +++ b/lib/radisk3.js @@ -20,6 +20,7 @@ //console.log("OFF");return; // ON WRITE: // batch until read from disk is done (and if a write was going, do that first) + if(!valid(word, is, reply)){ return } b(word, is); write(word, reply); return r; @@ -27,12 +28,34 @@ async function read(word, reply, page){ - var p = page;//b.page(word); + var p = page || b.page(word); reply = reply.call ? reply : () => { }; - log(`read() ${word.slice(0, 40)}`); get(p, function(err, disk){ if(err){ log("ERR! in read() get() cb", err); reply(err); return } - p.from = disk || p.from; + if(!disk){ + // do something to show whatever we have in-memory is going to be the disk. + } + if(disk){ + disk = Book(p.from = disk); + disk.PAGE = Infinity; // THIS BOOK IS ONLY TEMPORARY! + (p.list||[]).forEach(function(has){ + disk(has.word, has.is); + }); + // TODO: BUG! What happens if the merge causes too large of a page and it splits... WRITE TESTS TO HANDLE. + disk = disk.list[0]; + disk.first = p.first = (disk.first < p.first? disk.first : p.first); + /*disk.book = disk.get = b; + disk.saving = p.saving; + disk.i = p.i; + p = b.list[p.i] = disk;*/ + p.from = disk.from; + p.list = disk.list; + p.size = disk.size; + p.text = disk.text; + } else { + //p.from = disk || p.from; // TODO: NEED TO MERGE! AND HANDLE ERR! + } + //p.from = disk || p.from; reply(null, p, b); }) } @@ -42,13 +65,13 @@ var p = b.page(word), tmp; if(tmp = p.saving){ reply && tmp.push(reply); return } p.saving = [reply]; var S = +new Date; log(" writing", p.substring(), 'since last', S - p.saved, RAD.c, 'records', env.count++, 'mid-swap.'); - get(p, function(err, disk){ + read(p, function(err, disk){ if(err){ log("ERR! in write() get() cb ", err); return } //log(' get() - p.saving ', (p.saving || []).length); if(p.from && disk){ //log(" get() merge: p.from ", p.toString().slice(0, 40), " disk.length", disk?.length || 0); } - p.from = disk || p.from; // TODO: NEED TO MERGE! AND HANDLE ERR! + // CODE CUT OUT AND MOVED TO READ // var save = '' + p; if(!p.from){ p.from = save } // p.list = p.text = p.from = 0; // p.first = p.first.word || p.first; @@ -59,7 +82,7 @@ // log({ tmp }); write(word, reply); }); - }) + }, p); } function put(file, data, cb){ file.first && (file = Book.slot(file.first)[0]); @@ -117,6 +140,11 @@ function ename(t) { return encodeURIComponent(t).replace(/\*/g, '%2A').slice(0, 250) } function fname(p) { return opt.file + '/' + ename(p.substring()) } + function valid(word, is, reply){ + if(is !== is){ reply(word +" cannot be NaN!"); return } + return true; + } + return r; }), MAX = 1000/* 300000000 */; diff --git a/test/rad/book.js b/test/rad/book.js index d65290a7..57971691 100644 --- a/test/rad/book.js +++ b/test/rad/book.js @@ -32,10 +32,10 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam var opt = {}; opt.file = 'radatatest'; - var Radisk = (setTimeout.RAD) || require('../../lib/radisk'); + var RAD = (setTimeout.RAD) || require('../../lib/radisk'); //opt.store = ((Gun.window && Gun.window.RindexedDB) || require('../../lib/rfs'))(opt); opt.chunk = 1000; - var rad = Radisk(opt), esc = String.fromCharCode(27); + var rad = RAD(opt), esc = String.fromCharCode(27); describe('Book', function(){ this.timeout(1000 * 9); @@ -89,10 +89,10 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam false, -Infinity, Infinity, - NaN, -0 ]; - var prim = ['alice', 'bob']; + //var prim = ['alice', 'bob']; + //var prim = [null]; root.rad = rad; describe('can in-memory write & read all primitives', done => { prim.forEach(function(type){ @@ -106,11 +106,10 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam });}); describe('can disk write & read all primitives', done => { prim.forEach(function(type){ - var r = rad; it('save '+type, done => { setTimeout(function(){ - r('type-'+type, type, function(err, ok){ + rad('type-'+type, type, function(err, ok){ expect(err).to.not.be.ok(); - r('type-'+type, function(err, page){ + rad('type-'+type, function(err, page){ var val = page.get('type-'+type); expect(val).to.be(type); done(); @@ -119,6 +118,81 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam },1); }); });}); + describe('error on invalid primitives', function(){ + it('test invalid', done => { + rad('type-NaN', NaN, function(err, ok){ + expect(err).to.be.ok(); + done(); + }); + }); + }); + + describe('Async Race Conditions', function(){ + + it('make sure word does not get duplicated when data is re-saved after read', done => { + var opt = {file: 'zadata'} + var prev = RAD(opt); + + prev('helloz', 'world', function(err, ok){ + + prev('helloz', function(err, page){ + prev('zalice', 'yay', function(err){ + expect(page.text.split('helloz').length).to.be(2); + done(); + }); + }); + }); + /* + (A) READ ONLY: we receive a message, we READ only - parseless is important. + (B) READ & WRITE: we write a page, and it already exists on disk. + (C) WRITE ONLY: we write a page, and it is new to disk. + */ + }); + + it('test if adding an in-memory word merges with previously written disk data', done => { + var prev = RAD(opt); + + prev('pa-alice', 'hello', function(err, ok){ + expect(err).to.not.be.ok(); + + setTimeout(function(){ + var rad = RAD(opt); + + rad('pa-bob', 'banana', function(err, ok){ + expect(err).to.not.be.ok(); + //console.log("COMPARE:", rad.book.list[0].text, 'vs', prev.book.list[0].text); + var text = rad.book.list[0].text; + var i = text.indexOf('pa-alice'); + expect(i).to.not.be(-1); + var ii = text.indexOf('hello'); + expect((ii - i) < 10).to.be.ok(); + done(); + }) + },99); + }); + }); + + it('test if updating an in-memory word merges with previously written disk data', done => { + var prev = RAD(opt); + prev('pu-alice', 'hello', function(err, ok){ + expect(err).to.not.be.ok(); + + var rad = RAD(opt); + + rad('pu-alice', 'cool', function(err, ok){ + expect(err).to.not.be.ok(); + + var next = RAD(opt); + next('pu-alice', function(err, page){ + expect('cool').to.be(page.get('pu-alice')); + done(); + }) + }); + }); + }); + + }); + }); var ntmp = names;