1 |
|
2 |
|
3 | var
|
4 | assert = require('assert'),
|
5 | Skiplist = require('skiplist'),
|
6 | Xxhash = require('xxhashjs').h64
|
7 | ;
|
8 |
|
9 | var Lightcycle = module.exports = function Lightcycle(settings)
|
10 | {
|
11 | settings = settings || {};
|
12 |
|
13 | this.seed = settings.seed || 0xcafed00d;
|
14 | this.size = Math.round(settings.size) || 128;
|
15 | assert(this.size > 0, 'you must pass in a positive integer for size');
|
16 |
|
17 | this.replicas = settings.replicas || this.size;
|
18 | this.resources = new Skiplist(this.size * this.replicas + 16);
|
19 | this.cache = {};
|
20 | this.entries = {};
|
21 | };
|
22 |
|
23 | Lightcycle.prototype.seed = 0xcafed00d;
|
24 | Lightcycle.prototype.size = 128;
|
25 | Lightcycle.prototype.replicas = 128;
|
26 | Lightcycle.prototype.resources = null;
|
27 | Lightcycle.prototype.cache = null;
|
28 | Lightcycle.prototype.entries = null;
|
29 |
|
30 |
|
31 | Lightcycle.SIZE_PAD = 16;
|
32 | Lightcycle.REPLICAS_PAD = 8;
|
33 |
|
34 | Lightcycle.prototype.add = function add(resource, id)
|
35 | {
|
36 | assert(resource);
|
37 | assert(id && typeof id === 'string');
|
38 | if (!this.cache[id])
|
39 | this.cache[id] = [];
|
40 | var key;
|
41 |
|
42 | for (var i = 0; i < this.replicas; i++)
|
43 | {
|
44 | if (this.cache[id][i])
|
45 | key = this.cache[id][i];
|
46 | else
|
47 | {
|
48 | key = this.hashit(id + String(i));
|
49 | this.cache[id][i] = key;
|
50 | }
|
51 | this.resources.insert(key, resource);
|
52 | }
|
53 |
|
54 | this.entries[id] = resource;
|
55 | if (Object.keys(this.entries).length > this.size)
|
56 | this.rebalance();
|
57 | };
|
58 |
|
59 | Lightcycle.prototype.remove = function remove(id)
|
60 | {
|
61 | assert(id && typeof id === 'string');
|
62 | if (!Array.isArray(this.cache[id]))
|
63 | return;
|
64 | var key;
|
65 |
|
66 | for (var i = 0; i < this.replicas; i++)
|
67 | {
|
68 | key = this.cache[id][i];
|
69 | this.resources.remove(key);
|
70 | }
|
71 |
|
72 | delete this.entries[id];
|
73 | };
|
74 |
|
75 | Lightcycle.prototype.locate = function locate(id)
|
76 | {
|
77 | var key = this.hashit(id);
|
78 | var results = this.resources.findWithCount(key, 1);
|
79 |
|
80 | if (results.length === 0)
|
81 | results = this.resources.findWithCount(null, 1);
|
82 |
|
83 | if (results.length > 0)
|
84 | return results[0][1];
|
85 |
|
86 | return null;
|
87 | };
|
88 |
|
89 | Lightcycle.prototype.hashit = function hashit(input)
|
90 | {
|
91 | if (!Buffer.isBuffer(input))
|
92 | input = new Buffer(input);
|
93 |
|
94 | var hash = Xxhash(this.seed);
|
95 | hash.update(input);
|
96 | var result = hash.digest().toString(16);
|
97 | while (result.length < 8) result = '0' + result;
|
98 |
|
99 | return result;
|
100 | };
|
101 |
|
102 | Lightcycle.prototype.all = function all()
|
103 | {
|
104 | return this.entries;
|
105 | };
|
106 |
|
107 | Lightcycle.prototype.rebalance = function rebalance()
|
108 | {
|
109 | var ids = Object.keys(this.entries);
|
110 |
|
111 | this.size = ids.length + Lightcycle.SIZE_PAD;
|
112 | this.replicas = ids.length + Lightcycle.REPLICAS_PAD;
|
113 | this.resources = new Skiplist(this.size * this.replicas);
|
114 |
|
115 | for (var i = 0; i < ids.length; i++)
|
116 | this.add(this.entries[ids[i]], ids[i]);
|
117 | };
|