1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | var ERROR = function ( message )
|
7 | {
|
8 | this.getError = function () { return message; };
|
9 | this.toString = function () { return "<ERROR<" + message + ">>"; };
|
10 | };
|
11 |
|
12 | var BAD_TYPE = new ERROR ( 'Operation against a key holding the wrong kind of value' );
|
13 | var BAD_KEY = new ERROR ( 'no such key' );
|
14 | var BAD_INT = new ERROR ( 'value is not an integer or out of range' );
|
15 | var BAD_FLOAT = new ERROR ( 'value is not a valid float' );
|
16 | var BAD_ARGS = new ERROR ( 'wrong number of arguments' );
|
17 | var BAD_SYNTAX = new ERROR ( 'syntax error' );
|
18 | var BAD_INDEX = new ERROR ( 'index out of range' );
|
19 | var BAD_SORT = new ERROR ( 'One or more scores can\'t be converted into double' );
|
20 |
|
21 | var BAD_BIT1 = new ERROR ( 'bit offset is not an integer or out of range' );
|
22 | var BAD_BIT2 = new ERROR ( 'bit is not an integer or out of range' );
|
23 | var BAD_SETEX = new ERROR ( 'invalid expire time in SETEX' );
|
24 | var BAD_ZUIS = new ERROR ( 'at least 1 input key is needed for ZUNIONSTORE/ZINTERSTORE' );
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 | var STATUS = function ( message )
|
31 | {
|
32 | this.getStatus = function () { return message; };
|
33 | this.toString = function () { return "<STATUS<" + message + ">>"; };
|
34 | };
|
35 |
|
36 | var OK = new STATUS ( 'OK' );
|
37 | var PONG = new STATUS ( 'PONG' );
|
38 | var NONE = new STATUS ( 'none' );
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 | var VALID_TYPE = function () {};
|
45 | var TYPE = function ( type, makePrimitive )
|
46 | {
|
47 | var Constr = function ( value )
|
48 | {
|
49 | if ( !( this instanceof VALID_TYPE ) )
|
50 | return new Constr ( value );
|
51 | if ( !value )
|
52 | value = makePrimitive ();
|
53 |
|
54 | this.value = value;
|
55 | };
|
56 |
|
57 | Constr.getStatus = function () { return type; };
|
58 | Constr.prototype = new VALID_TYPE;
|
59 | Constr.prototype.toString = function () { return "<TYPE<" + type + ">>"; };
|
60 | Constr.prototype.TYPE = Constr;
|
61 | return Constr;
|
62 | };
|
63 |
|
64 | var EMPTY_STR = { toString : function () { return ""; }, length : 0, copy : function () {} };
|
65 | var STRING = TYPE ( "string", function () { return EMPTY_STR; } );
|
66 | var LIST = TYPE ( "list", function () { return []; } );
|
67 | var HASH = TYPE ( "hash", function () { return {}; } );
|
68 | var SET = TYPE ( "set", function () { return {}; } );
|
69 | var ZSET = TYPE ( "zset", function () { return {}; } );
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 | var arr = function ( obj )
|
76 | {
|
77 | var i, n = obj.length, out = [];
|
78 | for ( i = 0; i < n; i ++ )
|
79 | out [ i ] = obj [ i ];
|
80 |
|
81 | return out;
|
82 | };
|
83 |
|
84 | var range = function ( min, max )
|
85 | {
|
86 | var xlo, xhi;
|
87 |
|
88 | if (( xlo = min.substr ( 0, 1 ) === '(' ))
|
89 | min = str2float ( min.substr ( 1 ) );
|
90 | else
|
91 | min = str2float ( min );
|
92 |
|
93 | if ( min instanceof ERROR )
|
94 | return min;
|
95 |
|
96 | if (( xhi = max.substr ( 0, 1 ) === '(' ))
|
97 | max = str2float ( max.substr ( 1 ) );
|
98 | else
|
99 | max = str2float ( max );
|
100 |
|
101 | if ( max instanceof ERROR )
|
102 | return max;
|
103 |
|
104 | return function ( num )
|
105 | {
|
106 | return !( ( xlo && num <= min ) || ( num < min ) || ( xhi && num >= max ) || ( num > max ) );
|
107 | };
|
108 | };
|
109 |
|
110 | var slice = function ( arr, start, stop, asCount )
|
111 | {
|
112 | start = str2int ( start );
|
113 | stop = str2int ( stop );
|
114 | if ( start instanceof ERROR ) return start;
|
115 | if ( stop instanceof ERROR ) return stop;
|
116 |
|
117 | if ( arr.slice )
|
118 | {
|
119 | var n = arr.length;
|
120 | if ( asCount )
|
121 | {
|
122 | if ( start < 0 )
|
123 | {
|
124 | start = 0;
|
125 | stop = 0;
|
126 | }
|
127 | else if ( stop < 0 ) stop = n;
|
128 | else stop += start;
|
129 | }
|
130 | else
|
131 | {
|
132 | if ( start < 0 ) start = n + start;
|
133 | if ( stop < 0 ) stop = n + stop;
|
134 | stop ++;
|
135 | }
|
136 |
|
137 | if ( start >= stop )
|
138 | return [];
|
139 | else
|
140 | return arr.slice ( start < 0 ? 0 : start, stop > n ? n : stop );
|
141 | }
|
142 |
|
143 | else
|
144 | return arr;
|
145 | };
|
146 |
|
147 | var str2float = function ( string )
|
148 | {
|
149 | var value = Number ( string );
|
150 | if ( typeof string !== 'string' ) throw new Error ( "WOOT! str2float: '" + string + "' not a string." );
|
151 | if ( string === '+inf' ) value = Number.POSITIVE_INFINITY;
|
152 | else if ( string === '-inf' ) value = Number.NEGATIVE_INFINITY;
|
153 | else if ( !string || ( !value && value !== 0 ) ) return BAD_FLOAT;
|
154 | return value;
|
155 | };
|
156 |
|
157 | var str2int = function ( string )
|
158 | {
|
159 | var value = str2float ( string );
|
160 | if ( value instanceof ERROR || value % 1 !== 0 ) return BAD_INT;
|
161 | return value;
|
162 | };
|
163 |
|
164 | var pattern = function ( string )
|
165 | {
|
166 | string = string.replace ( /[+{($^|.\\]/g, '\\' + '$0' );
|
167 | string = string.replace ( /(^|[^\\])([*?])/g, '$1.$2' );
|
168 | string = '^' + string + '$';
|
169 |
|
170 | var pattern = new RegExp ( string );
|
171 | return pattern.test.bind ( pattern );
|
172 | };
|
173 |
|
174 |
|
175 |
|
176 |
|
177 |
|
178 | exports.Backend = function ()
|
179 | {
|
180 | var state,
|
181 | dbs = {},
|
182 | delrev = {},
|
183 | rev = 0,
|
184 |
|
185 | subs = [],
|
186 | call = [],
|
187 | tick = false;
|
188 |
|
189 |
|
190 |
|
191 |
|
192 | this.selectDB = function (id) {
|
193 | if (typeof id !== "number" || id % 1 !== 0)
|
194 | throw new Error("Invalid database id: " + id);
|
195 |
|
196 |
|
197 | var db = dbs[id] || (dbs[id] = {});
|
198 | state = db;
|
199 | };
|
200 |
|
201 |
|
202 | this.selectDB(0);
|
203 |
|
204 |
|
205 |
|
206 |
|
207 | this.getKey = function ( type, key, make )
|
208 | {
|
209 | var entry = state [ key ];
|
210 |
|
211 | if ( type && !type.getStatus )
|
212 | throw new Error ( "WOOT! type param for getKey is not a valid type." );
|
213 | if ( key === undefined )
|
214 | throw new Error ( "WOOT! key param for getKey is undefined." );
|
215 |
|
216 | if ( !entry || entry.expire < Date.now () )
|
217 | {
|
218 | delete state [ key ];
|
219 | delrev [ key ] = ++ rev;
|
220 | entry = null;
|
221 | }
|
222 | else if ( !( entry.value instanceof VALID_TYPE ) )
|
223 | throw new Error ( "WOOT! keyspace entry value is not a valid type." );
|
224 |
|
225 | if ( type )
|
226 | {
|
227 | if ( entry && !( entry.value instanceof type ) )
|
228 | return BAD_TYPE;
|
229 | if ( !entry && make )
|
230 | return new type;
|
231 | }
|
232 |
|
233 | return ( entry && entry.value ) || null;
|
234 | };
|
235 |
|
236 | this.setKey = function ( key, value )
|
237 | {
|
238 | if ( value )
|
239 | {
|
240 | if ( !( value instanceof VALID_TYPE ) )
|
241 | throw new Error ( "WOOT! Value doesn't have a valid type." );
|
242 |
|
243 | rev ++;
|
244 | state [ key ] = { value : value };
|
245 | state [ key ].rev = rev;
|
246 | delete delrev [ key ];
|
247 |
|
248 | this.pub ( this.UPDATE, key );
|
249 |
|
250 | return 1;
|
251 | }
|
252 |
|
253 | else if ( state [ key ] )
|
254 | {
|
255 | rev ++;
|
256 | delrev [ key ] = rev;
|
257 | delete state [ key ];
|
258 |
|
259 | return 1;
|
260 | }
|
261 |
|
262 | return 0;
|
263 | };
|
264 |
|
265 | this.upsetKey = function ( key, value )
|
266 | {
|
267 | if ( !value )
|
268 | throw new Error ( "WOOT! Update key with a falsy value." );
|
269 | if ( !( value instanceof VALID_TYPE ) )
|
270 | throw new Error ( "WOOT! Value doesn't have a valid type." );
|
271 |
|
272 | if ( state [ key ] && state [ key ].expire >= Date.now () )
|
273 | {
|
274 | if ( state [ key ].value !== value )
|
275 | throw new Error ( "WOOT! Chaning value containers during upsetKey." );
|
276 |
|
277 | rev ++;
|
278 | state [ key ].value = value;
|
279 | state [ key ].rev = rev;
|
280 |
|
281 | this.pub ( this.UPDATE, key );
|
282 | }
|
283 |
|
284 | else
|
285 | this.setKey ( key, value );
|
286 | };
|
287 |
|
288 | this.getExpire = function ( key )
|
289 | {
|
290 | var entry = state [ key ];
|
291 |
|
292 | if ( !entry || entry.expire < Date.now () )
|
293 | {
|
294 | delete state [ key ];
|
295 | return null;
|
296 | }
|
297 |
|
298 | return entry.expire;
|
299 | };
|
300 |
|
301 | this.setExpire = function ( key, expire )
|
302 | {
|
303 | var entry = state [ key ];
|
304 |
|
305 | if ( !entry || entry.expire < Date.now () )
|
306 | {
|
307 | delete state [ key ];
|
308 | return 0;
|
309 | }
|
310 |
|
311 | else if ( expire )
|
312 | {
|
313 | entry.expire = expire;
|
314 | return 1;
|
315 | }
|
316 |
|
317 | else if ( entry.expire )
|
318 | {
|
319 | delete entry.expire;
|
320 | return 1;
|
321 | }
|
322 |
|
323 | return 0;
|
324 | };
|
325 |
|
326 | this.getKeys = function ()
|
327 | {
|
328 | var keys = [],
|
329 | key;
|
330 |
|
331 | for ( key in state )
|
332 | if ( this.getKey ( null, key ) )
|
333 | keys.push ( key );
|
334 |
|
335 | return keys;
|
336 | };
|
337 |
|
338 | this.renameKey = function ( keyA, keyB )
|
339 | {
|
340 | if ( !this.getKey ( null, keyA ) )
|
341 | return false;
|
342 |
|
343 | rev ++;
|
344 | state [ keyB ] = state [ keyA ];
|
345 | state [ keyB ].rev = rev ++;
|
346 | delete state [ keyA ];
|
347 |
|
348 | this.pub ( this.UPDATE, keyB );
|
349 |
|
350 | return true;
|
351 | };
|
352 |
|
353 |
|
354 |
|
355 |
|
356 | this.UPDATE = new STATUS ( "Key value updated." );
|
357 |
|
358 |
|
359 |
|
360 |
|
361 | this.getRevision = function ( key )
|
362 | {
|
363 | this.getKey ( null, key );
|
364 | return ( state [ key ] && state [ key ].rev ) || delrev [ key ] || 0;
|
365 | };
|
366 |
|
367 |
|
368 |
|
369 |
|
370 | this.pub = function ( channel, message )
|
371 | {
|
372 | if ( !channel && channel !== '' ) throw new Error ( "WOOT! Publishing to a falsy, non-string channel : [" + channel + '] ' + message );
|
373 | if ( !message && message !== '' ) throw new Error ( "WOOT! Publishing a falsy, non-string message : [" + channel + '] ' + message );
|
374 |
|
375 | var i, n = subs.length, sub, x = 0;
|
376 | for ( i = 0; i < n; i ++ )
|
377 | {
|
378 | sub = subs [ i ];
|
379 |
|
380 | if ( sub.channel === channel || ( sub.pattern !== null && sub.channel ( channel ) ) )
|
381 | {
|
382 | if ( sub.pattern !== null )
|
383 | call.push ( sub.client.pushMessage.bind ( sub.client, 'pmessage', sub.pattern, channel, message ) );
|
384 | else
|
385 | call.push ( sub.client.pushMessage.bind ( sub.client, 'message', channel, message ) );
|
386 |
|
387 | x ++;
|
388 | if ( !tick )
|
389 | {
|
390 | tick = true;
|
391 | process.nextTick ( function ()
|
392 | {
|
393 | var c, func;
|
394 | tick = false;
|
395 | c = call.splice ( 0, call.length );
|
396 | while (( func = c.shift () )) func ();
|
397 | });
|
398 | }
|
399 | }
|
400 | }
|
401 |
|
402 | return x;
|
403 | };
|
404 |
|
405 |
|
406 |
|
407 |
|
408 |
|
409 | this.sub = function ( p, channel, client )
|
410 | {
|
411 | if ( !channel && channel !== '' ) throw new Error ( "WOOT! Subscribing to a falsy, non-string channel : [" + channel + ']' );
|
412 | if ( !client || !client.pushMessage ) throw new Error ( "WOOT! Subscribing an invalid client : " + client );
|
413 | if ( typeof channel === 'function' ) throw new Error ( "WOOT! Subscribing to a function : " + channel );
|
414 |
|
415 | var i, n = subs.length, sub, found = false;
|
416 | for ( i = 0; i < n; i ++ )
|
417 | {
|
418 | sub = subs [ i ];
|
419 | if ( sub.client === client && ( ( p && sub.pattern === channel ) || ( !p && sub.channel === channel ) ) )
|
420 | {
|
421 | found = true;
|
422 | break;
|
423 | }
|
424 | }
|
425 |
|
426 | var x = this.numSubs ( client );
|
427 |
|
428 | if ( !found )
|
429 | {
|
430 | x ++;
|
431 |
|
432 | subs.push ({ pattern : p ? channel : null, channel : p ? pattern ( channel ) : channel, client : client });
|
433 | process.nextTick ( client.pushMessage.bind ( client, p ? 'psubscribe' : 'subscribe', channel, x ) );
|
434 | }
|
435 |
|
436 | return x;
|
437 | };
|
438 |
|
439 | this.unsub = function ( p, channel, client )
|
440 | {
|
441 | if ( !channel && channel !== '' && channel !== null ) throw new Error ( "WOOT! Unsubscribing from a falsy, non-string, non-null channel : [" + channel + ']' );
|
442 | if ( !client || !client.pushMessage ) throw new Error ( "WOOT! Unsubscribing an invalid client : " + client );
|
443 |
|
444 | var x = this.numSubs ( client );
|
445 |
|
446 | var i, n = subs.length, sub;
|
447 | for ( i = 0; i < n; i ++ )
|
448 | {
|
449 | sub = subs [ i ];
|
450 | if ( sub.client !== client )
|
451 | continue;
|
452 |
|
453 | if ( ( p && sub.pattern !== null && ( channel === null || sub.pattern === channel ) ) || ( !p && sub.pattern === null && ( channel === null || sub.channel === channel ) ) )
|
454 | {
|
455 | x --;
|
456 | subs.splice ( i, 1 );
|
457 | process.nextTick ( client.pushMessage.bind ( client, p ? 'punsubscribe' : 'unsubscribe', p ? sub.pattern : sub.channel, x ) );
|
458 | i --; n --;
|
459 | }
|
460 | }
|
461 |
|
462 | return x;
|
463 | };
|
464 |
|
465 | this.numSubs = function ( client )
|
466 | {
|
467 | var i, n = subs.length, x = 0;
|
468 | for ( i = 0; i < n; i ++ )
|
469 | if ( subs [ i ].client === client )
|
470 | x ++;
|
471 |
|
472 | return x;
|
473 | }
|
474 |
|
475 | };
|
476 |
|
477 |
|
478 |
|
479 |
|
480 |
|
481 | exports.Backend.prototype =
|
482 | {
|
483 |
|
484 |
|
485 |
|
486 |
|
487 | DEL : function ()
|
488 | {
|
489 | var i, n = arguments.length, x = 0;
|
490 | if ( !n ) return BAD_ARGS;
|
491 | for ( i = 0; i < n; i ++ )
|
492 | if ( this.setKey ( arguments [ i ], null ) ) x ++;
|
493 |
|
494 | return x;
|
495 | },
|
496 |
|
497 | EXISTS : function ( key )
|
498 | {
|
499 | return this.getKey ( null, key ) ? 1 : 0;
|
500 | },
|
501 |
|
502 | PEXPIREAT : function ( key, time )
|
503 | {
|
504 | time = str2int ( time );
|
505 | if ( time instanceof ERROR ) return time;
|
506 | return this.setExpire ( key, time );
|
507 | },
|
508 |
|
509 | EXPIREAT : function ( key, time )
|
510 | {
|
511 | time = str2int ( time );
|
512 | if ( time instanceof ERROR ) return time;
|
513 | return this.setExpire ( key, time * 1000 );
|
514 | },
|
515 |
|
516 | PEXPIRE : function ( key, time )
|
517 | {
|
518 | time = str2int ( time );
|
519 | if ( time instanceof ERROR ) return time;
|
520 | return this.setExpire ( key, time + Date.now () );
|
521 | },
|
522 |
|
523 | EXPIRE : function ( key, time )
|
524 | {
|
525 | time = str2int ( time );
|
526 | if ( time instanceof ERROR ) return time;
|
527 | return this.setExpire ( key, time * 1000 + Date.now () );
|
528 | },
|
529 |
|
530 | PERSIST : function ( key )
|
531 | {
|
532 | return this.PEXPIREAT ( key, "0" );
|
533 | },
|
534 |
|
535 | PTTL : function ( key )
|
536 | {
|
537 | var ttl = this.getExpire ( key );
|
538 | if ( ttl ) return ttl - Date.now ();
|
539 | else return -1;
|
540 | },
|
541 |
|
542 | RANDOMKEY : function ( key )
|
543 | {
|
544 | var keys = this.getKeys (), n = keys && keys.length;
|
545 | if ( n ) return keys [ Math.floor ( Math.random () * n ) ];
|
546 | else return null;
|
547 | },
|
548 |
|
549 | RENAME : function ( key, newkey )
|
550 | {
|
551 | return this.renameKey ( key, newkey ) ? OK : BAD_KEY;
|
552 | },
|
553 |
|
554 | RENAMENX : function ( key, newkey )
|
555 | {
|
556 | if ( !this.EXISTS ( key ) ) return BAD_KEY;
|
557 | if ( this.EXISTS ( newkey ) ) return 0;
|
558 | if ( !this.renameKey ( key, newkey ) ) throw new Error ( "WOOT! Couldn't rename." );
|
559 | return 1;
|
560 | },
|
561 |
|
562 | TTL : function ( key )
|
563 | {
|
564 | var ttl = this.getExpire ( key );
|
565 | if ( ttl ) return Math.ceil ( ( ttl - Date.now () ) / 1000 );
|
566 | else return -1;
|
567 | },
|
568 |
|
569 | TYPE : function ( key )
|
570 | {
|
571 | var K = this.getKey ( null, key );
|
572 | return K ? K.TYPE : NONE;
|
573 | },
|
574 |
|
575 | KEYS : function ( pat )
|
576 | {
|
577 | var keys = this.getKeys ().filter ( pattern ( pat ) );
|
578 | keys.sort ();
|
579 | return keys;
|
580 | },
|
581 |
|
582 |
|
583 |
|
584 |
|
585 |
|
586 | SET : function ( key, value )
|
587 | {
|
588 | var buf = new Buffer ( Buffer.byteLength ( value ) );
|
589 | buf.write ( value );
|
590 |
|
591 | this.setKey ( key, new STRING ( buf ) );
|
592 | return OK;
|
593 | },
|
594 |
|
595 | sIncrBy : function ( parse, key, incr )
|
596 | {
|
597 | var K = this.getKey ( STRING, key, true );
|
598 | if ( K instanceof ERROR ) return K;
|
599 |
|
600 | incr = parse ( incr );
|
601 | if ( incr instanceof ERROR ) return incr;
|
602 | var value = parse ( K.value.toString () || "0" );
|
603 | if ( value instanceof ERROR ) return value;
|
604 |
|
605 | value = ( value + incr ).toString ();
|
606 | var buf = new Buffer ( Buffer.byteLength ( value ) );
|
607 | buf.write ( value );
|
608 |
|
609 | K.value = value;
|
610 | this.upsetKey ( key, K );
|
611 | return value;
|
612 | },
|
613 |
|
614 | sFit : function ( key, length )
|
615 | {
|
616 | var K = this.getKey ( STRING, key, true );
|
617 | if ( K instanceof ERROR ) return ERROR;
|
618 |
|
619 | if ( K.value.length < length )
|
620 | {
|
621 | var buf = new Buffer ( length );
|
622 | buf.fill ( 0 );
|
623 |
|
624 | K.value.copy ( buf );
|
625 | K.value = buf;
|
626 | }
|
627 |
|
628 | return K;
|
629 | },
|
630 |
|
631 | SETBIT : function ( key, offset, state )
|
632 | {
|
633 | var offset = str2int ( offset );
|
634 | if ( !( offset > -1 ) ) return BAD_BIT1;
|
635 | var state = str2int ( state );
|
636 | if ( !( state === 0 || state === 1 ) ) return BAD_BIT2;
|
637 |
|
638 | var x = Math.floor ( offset / 8 );
|
639 | var K = this.sFit ( key, x + 1 );
|
640 | if ( K instanceof ERROR ) return K;
|
641 |
|
642 | var mask = 1 << ( 7 - ( offset % 8 ) );
|
643 | var current = K.value [ x ];
|
644 | var old = current & mask ? 1 : 0;
|
645 |
|
646 | if ( state && !old )
|
647 | K.value [ x ] = current | mask;
|
648 | else if ( !state && old )
|
649 | K.value [ x ] = current & ~mask;
|
650 |
|
651 | this.upsetKey ( key, K );
|
652 | return old;
|
653 | },
|
654 |
|
655 | SETRANGE : function ( key, offset, value )
|
656 | {
|
657 | var offset = str2int ( offset );
|
658 | if ( !( offset > -1 ) ) return BAD_BIT1;
|
659 |
|
660 | var K = this.sFit ( key, offset + Buffer.byteLength ( value ) );
|
661 | K.value.write ( value, offset );
|
662 |
|
663 | this.upsetKey ( key, K );
|
664 | return this.STRLEN ( key );
|
665 | },
|
666 |
|
667 |
|
668 |
|
669 | GET : function ( key )
|
670 | {
|
671 | var K = this.getKey ( STRING, key );
|
672 | if ( K instanceof ERROR ) return K;
|
673 | return K ? K.value.toString () : null;
|
674 | },
|
675 |
|
676 | STRLEN : function ( key )
|
677 | {
|
678 | var K = this.getKey ( STRING, key );
|
679 | if ( K instanceof ERROR ) return ERROR;
|
680 | return K ? K.value.length : 0;
|
681 | },
|
682 |
|
683 | GETBIT : function ( key, offset )
|
684 | {
|
685 | var K = this.getKey ( STRING, key );
|
686 | if ( K instanceof ERROR ) return ERROR;
|
687 |
|
688 | var offset = str2int ( offset );
|
689 | if ( !( offset > -1 ) ) return BAD_BIT1;
|
690 | var x = Math.floor ( offset / 8 );
|
691 | if ( !K || K.length < x + 1 ) return 0;
|
692 |
|
693 | var mask = 1 << ( 7 - ( offset % 8 ) );
|
694 | return ( K.value [ x ] & mask ) ? 1 : 0;
|
695 | },
|
696 |
|
697 | GETRANGE : function ( key, start, stop )
|
698 | {
|
699 | var K = this.getKey ( STRING, key );
|
700 | if ( K instanceof ERROR ) return ERROR;
|
701 | if ( !K ) return "";
|
702 |
|
703 | var out = slice ( K.value, start, stop );
|
704 | if ( out instanceof ERROR ) return out;
|
705 | return out.toString ();
|
706 | },
|
707 |
|
708 |
|
709 |
|
710 | APPEND : function ( key, value )
|
711 | {
|
712 | var strlen = this.STRLEN ( key );
|
713 | if ( strlen instanceof ERROR ) return strlen;
|
714 | return this.SETRANGE ( key, strlen.toString (), value );
|
715 | },
|
716 |
|
717 | DECR : function ( key )
|
718 | {
|
719 | return this.DECRBY ( key, "1" );
|
720 | },
|
721 |
|
722 | DECRBY : function ( key, decr )
|
723 | {
|
724 | var value = str2int ( decr );
|
725 | if ( value instanceof ERROR ) return value;
|
726 | return this.INCRBY ( key, ( - value ).toString () );
|
727 | },
|
728 |
|
729 | GETSET : function ( key, value )
|
730 | {
|
731 | var old = this.GET ( key );
|
732 | if ( old instanceof ERROR ) return old;
|
733 | this.SET ( key, value );
|
734 | return old;
|
735 | },
|
736 |
|
737 | INCR : function ( key )
|
738 | {
|
739 | return this.INCRBY ( key, "1" );
|
740 | },
|
741 |
|
742 | INCRBY : function ( key, incr )
|
743 | {
|
744 | return this.sIncrBy ( str2int, key, incr );
|
745 | },
|
746 |
|
747 | INCRBYFLOAT : function ( key, incr )
|
748 | {
|
749 | return this.sIncrBy ( str2float, key, incr );
|
750 | },
|
751 |
|
752 | MGET : function ()
|
753 | {
|
754 | var out = [], i, n = arguments.length;
|
755 | if ( !n ) return BAD_ARGS;
|
756 |
|
757 | for ( i = 0; i < n; i ++ )
|
758 | {
|
759 | var value = this.GET ( arguments [ i ] );
|
760 | out [ i ] = value instanceof ERROR ? null : value;
|
761 | }
|
762 |
|
763 | return out;
|
764 | },
|
765 |
|
766 | MSET : function ()
|
767 | {
|
768 | var key, value, i, n = arguments.length;
|
769 | if ( !n || n % 2 ) return BAD_ARGS;
|
770 |
|
771 | for ( i = 0; i < n; i += 2 )
|
772 | {
|
773 | key = arguments [ i ];
|
774 | value = arguments [ i + 1 ];
|
775 | this.SET ( key, value );
|
776 | }
|
777 |
|
778 | return OK;
|
779 | },
|
780 |
|
781 | MSETNX : function ()
|
782 | {
|
783 | var i, n = arguments.length;
|
784 | for ( i = 0; i < n; i += 2 )
|
785 | if ( this.EXISTS ( arguments [ i ] ) ) return 0;
|
786 |
|
787 | this.MSET.apply ( this, arguments );
|
788 | return 1;
|
789 | },
|
790 |
|
791 | PSETEX : function ( key, timediff, value )
|
792 | {
|
793 | if ( !( str2int ( timediff ) > 0 ) )
|
794 | return BAD_SETEX;
|
795 |
|
796 | this.SET ( key, value );
|
797 | this.PEXPIRE ( key, timediff );
|
798 | return OK;
|
799 | },
|
800 |
|
801 | SETEX : function ( key, timediff, value )
|
802 | {
|
803 | if ( !( str2int ( timediff ) > 0 ) )
|
804 | return BAD_SETEX;
|
805 |
|
806 | this.SET ( key, value );
|
807 | this.EXPIRE ( key, timediff );
|
808 | return OK;
|
809 | },
|
810 |
|
811 | SETNX : function ( key, value )
|
812 | {
|
813 | if ( this.EXISTS ( key ) ) return 0;
|
814 | this.SET ( key, value );
|
815 | return 1;
|
816 | },
|
817 |
|
818 |
|
819 |
|
820 |
|
821 |
|
822 | lStore : function ( key, values )
|
823 | {
|
824 |
|
825 |
|
826 | if ( values.length )
|
827 | return this.setKey ( key, new LIST ( values ) );
|
828 | else
|
829 | return this.setKey ( key, null );
|
830 | },
|
831 |
|
832 | LINDEX : function ( key, index )
|
833 | {
|
834 | var K = this.getKey ( LIST, key );
|
835 | if ( K instanceof ERROR ) return K;
|
836 |
|
837 | index = str2int ( index );
|
838 | if ( index instanceof ERROR )
|
839 | return index;
|
840 |
|
841 | return ( K && K.value [ index < 0 ? K.value.length + index : index ] ) || null;
|
842 | },
|
843 |
|
844 | upsetList : function ( key, K )
|
845 | {
|
846 | if ( K.value.length ) this.upsetKey ( key, K );
|
847 | else this.setKey ( key, null );
|
848 | },
|
849 |
|
850 | LINSERT : function ( key, relpos, pivot, value )
|
851 | {
|
852 | var K = this.getKey ( LIST, key ), x;
|
853 | if ( K instanceof ERROR ) return K;
|
854 |
|
855 | relpos = relpos.toUpperCase ();
|
856 | if ( relpos !== 'BEFORE' && relpos !== 'AFTER' ) return BAD_SYNTAX;
|
857 | if ( !K ) return 0;
|
858 | if ( ( x = K.value.indexOf ( pivot ) ) < 0 ) return 0;
|
859 |
|
860 | K.value.splice ( relpos === 'AFTER' ? x + 1 : x, 0, value );
|
861 | this.upsetList ( key, K );
|
862 | return 1;
|
863 | },
|
864 |
|
865 | LLEN : function ( key )
|
866 | {
|
867 | var K = this.getKey ( LIST, key );
|
868 | if ( K instanceof ERROR ) return K;
|
869 |
|
870 | return ( K && K.value && K.value.length ) || 0;
|
871 | },
|
872 |
|
873 | lPopMany : function ( left, keys )
|
874 | {
|
875 | var K = [], value, i, n = keys.length;
|
876 | if ( !n ) return BAD_ARGS;
|
877 | for ( i = 0; i < n; i ++ )
|
878 | {
|
879 | K [ i ] = this.getKey ( LIST, keys [ i ] );
|
880 | if ( K [ i ] instanceof ERROR ) return K [ i ];
|
881 | }
|
882 | for ( i = 0; i < n; i ++ )
|
883 | if ( K [ i ] && K [ i ].value && K [ i ].value.length )
|
884 | {
|
885 | value = left ? K [ i ].value.shift () : K [ i ].value.pop ();
|
886 | this.upsetList ( keys [ i ], K [ i ] );
|
887 | return [ keys [ i ], value ];
|
888 | }
|
889 |
|
890 | return null;
|
891 | },
|
892 |
|
893 | lPop : function ( left, key )
|
894 | {
|
895 | var out = this.lPopMany ( left, [ key ] );
|
896 | return out && out.length ? out [ 1 ] : out;
|
897 | },
|
898 |
|
899 | LPOP : function ( key )
|
900 | {
|
901 | return this.lPop ( true, key );
|
902 | },
|
903 |
|
904 | RPOP : function ( key )
|
905 | {
|
906 | return this.lPop ( false, key );
|
907 | },
|
908 |
|
909 | lPush : function ( left, make, args )
|
910 | {
|
911 | var i, n = args.length, key = args [ 0 ];
|
912 | var K = this.getKey ( LIST, key, make );
|
913 | if ( K instanceof ERROR ) return K;
|
914 | if ( n < 2 ) return BAD_ARGS;
|
915 | if ( !K ) return 0;
|
916 |
|
917 | if ( left ) for ( i = 1; i < n; i ++ )
|
918 | K.value.unshift ( args [ i ] );
|
919 | else
|
920 | K.value.push.apply ( K.value, args.slice ( 1 ) );
|
921 |
|
922 | this.upsetList ( key, K );
|
923 | return K.value.length;
|
924 | },
|
925 |
|
926 | LPUSH : function ()
|
927 | {
|
928 | return this.lPush ( true, true, arr ( arguments ) );
|
929 | },
|
930 |
|
931 | LPUSHX : function ()
|
932 | {
|
933 | return this.lPush ( true, false, arr ( arguments ) );
|
934 | },
|
935 |
|
936 | RPUSH : function ()
|
937 | {
|
938 | return this.lPush ( false, true, arr ( arguments ) );
|
939 | },
|
940 |
|
941 | RPUSHX : function ()
|
942 | {
|
943 | return this.lPush ( false, false, arr ( arguments ) );
|
944 | },
|
945 |
|
946 | RPOPLPUSH : function ( source, destination )
|
947 | {
|
948 | var dest = this.getKey ( LIST, destination );
|
949 | if ( dest && dest instanceof ERROR ) return dest;
|
950 | var value = this.RPOP ( source );
|
951 | if ( value === null || value instanceof ERROR ) return value;
|
952 |
|
953 | var len = this.LPUSH ( destination, value );
|
954 | if ( !len || len instanceof ERROR ) throw new Error ( "WOOT! LPUSH failed in RPOPLPUSH." );
|
955 |
|
956 | return value;
|
957 | },
|
958 |
|
959 | LRANGE : function ( key, start, stop )
|
960 | {
|
961 | var K = this.getKey ( LIST, key );
|
962 | if ( K instanceof ERROR ) return K;
|
963 | if ( !K ) return [];
|
964 |
|
965 | return slice ( K.value, start, stop );
|
966 | },
|
967 |
|
968 | LREM : function ( key, count, value )
|
969 | {
|
970 | var K = this.getKey ( LIST, key );
|
971 | if ( K instanceof ERROR ) return K;
|
972 | var count = str2int ( count );
|
973 | if ( count instanceof ERROR ) return count;
|
974 | if( !K ) return 0;
|
975 |
|
976 | var i, n = K.value.length, x = 0;
|
977 | if ( count < 0 )
|
978 | {
|
979 | count *= -1;
|
980 | for ( i = n - 1; i >= 0; i -- )
|
981 | if ( K.value [ i ] === value && (!count || x < count) )
|
982 | {
|
983 | K.value.splice ( i, 1 );
|
984 | x ++;
|
985 | }
|
986 | }
|
987 | else for ( i = 0; i < n; i ++ )
|
988 | if ( K.value [ i ] === value && (!count || x < count) )
|
989 | {
|
990 | K.value.splice ( i, 1 );
|
991 | i --; n --; x ++;
|
992 | }
|
993 |
|
994 | if ( x > 0 ) this.upsetList ( key, K );
|
995 | return x;
|
996 | },
|
997 |
|
998 | LSET : function ( key, index, value )
|
999 | {
|
1000 | var K = this.getKey ( LIST, key );
|
1001 | if ( !K ) return BAD_KEY;
|
1002 | if ( K instanceof ERROR ) return K;
|
1003 | var index = str2int ( index );
|
1004 | if ( index instanceof ERROR ) return index;
|
1005 | if ( index < 0 || index > K.value.length ) return BAD_INDEX;
|
1006 |
|
1007 | K.value [ index ] = value;
|
1008 | this.upsetList ( key, K );
|
1009 | return OK;
|
1010 | },
|
1011 |
|
1012 | LTRIM : function ( key, start, stop )
|
1013 | {
|
1014 | var range = this.LRANGE ( key, start, stop );
|
1015 | if ( !range.join )
|
1016 | return range;
|
1017 |
|
1018 | var K = this.getKey ( LIST, key );
|
1019 | if ( K )
|
1020 | {
|
1021 | K.value = range;
|
1022 | this.upsetList ( key, K );
|
1023 | }
|
1024 |
|
1025 | return OK;
|
1026 | },
|
1027 |
|
1028 |
|
1029 |
|
1030 |
|
1031 |
|
1032 |
|
1033 |
|
1034 | bArgs : function ( args )
|
1035 | {
|
1036 | args = arr ( args );
|
1037 | var timeout = str2int ( args.pop () || "FAIL" );
|
1038 | if ( timeout instanceof ERROR ) return timeout;
|
1039 | if ( timeout < 0 ) return BAD_INT;
|
1040 | return args;
|
1041 | },
|
1042 |
|
1043 | BLPOP : function ()
|
1044 | {
|
1045 | var a = this.bArgs ( arguments );
|
1046 | if ( a instanceof ERROR ) return a;
|
1047 | return this.lPopMany ( true, a );
|
1048 | },
|
1049 |
|
1050 | BRPOP : function ()
|
1051 | {
|
1052 | var a = this.bArgs ( arguments );
|
1053 | if ( a instanceof ERROR ) return a;
|
1054 | return this.lPopMany ( false, a );
|
1055 | },
|
1056 |
|
1057 | BRPOPLPUSH : function ()
|
1058 | {
|
1059 | var a = this.bArgs ( arguments );
|
1060 | if ( a instanceof ERROR ) return a;
|
1061 | return this.RPOPLPUSH.apply ( this, a );
|
1062 | },
|
1063 |
|
1064 |
|
1065 |
|
1066 |
|
1067 |
|
1068 | structPut : function ( type, validate, revArgs, args )
|
1069 | {
|
1070 | var key = args [ 0 ], i, n = args.length, x = 0;
|
1071 |
|
1072 | if ( n < 3 || ( ( n - 1 ) % 2 ) ) return BAD_ARGS;
|
1073 | var K = this.getKey ( type, key, true );
|
1074 | if ( K instanceof ERROR ) return K;
|
1075 |
|
1076 | for ( i = 1; i < n; i += 2 )
|
1077 | {
|
1078 | var member = args [ revArgs ? i + 1 : i ],
|
1079 | value = validate ( args [ revArgs ? i : i + 1 ] );
|
1080 | if ( value instanceof ERROR ) return value;
|
1081 | if ( !( member in K.value ) ) x ++;
|
1082 | K.value [ member ] = value;
|
1083 | }
|
1084 |
|
1085 | if ( x ) this.upsetKey ( key, K );
|
1086 | return x;
|
1087 | },
|
1088 |
|
1089 | structDel : function ( type, args )
|
1090 | {
|
1091 | var key = args [ 0 ],
|
1092 | i, n = args.length,
|
1093 | x;
|
1094 |
|
1095 | if ( n < 2 ) return BAD_ARGS;
|
1096 | var K = this.getKey ( type, key );
|
1097 | if ( K instanceof ERROR ) return K;
|
1098 | if ( !K ) return 0;
|
1099 |
|
1100 | x = 0;
|
1101 | for ( i = 1; i < n; i ++ )
|
1102 | {
|
1103 | if ( args [ i ] in K.value ) x ++;
|
1104 | delete K.value [ args [ i ] ];
|
1105 | }
|
1106 |
|
1107 |
|
1108 |
|
1109 | var member;
|
1110 | for ( member in K.value )
|
1111 | {
|
1112 | if ( x ) this.upsetKey ( key, K );
|
1113 | return x;
|
1114 | }
|
1115 |
|
1116 | this.setKey ( key, null );
|
1117 | return x;
|
1118 | },
|
1119 |
|
1120 | structGet : function ( type, key, member )
|
1121 | {
|
1122 | var K = this.getKey ( type, key );
|
1123 | if ( K instanceof ERROR ) return K;
|
1124 | if ( !K || !( member in K.value ) ) return null;
|
1125 | return K.value [ member ];
|
1126 | },
|
1127 |
|
1128 | HDEL : function ()
|
1129 | {
|
1130 | return this.structDel ( HASH, arguments );
|
1131 | },
|
1132 |
|
1133 | HEXISTS : function ( key, field )
|
1134 | {
|
1135 | var fields = this.HKEYS ( key );
|
1136 | if ( fields.indexOf ) return fields.indexOf ( field ) >= 0 ? 1 : 0;
|
1137 | return fields;
|
1138 | },
|
1139 |
|
1140 | HGET : function ( key, field )
|
1141 | {
|
1142 | return this.structGet ( HASH, key, field );
|
1143 | },
|
1144 |
|
1145 | HGETALL : function ( key )
|
1146 | {
|
1147 | var fields = this.HKEYS ( key );
|
1148 | var i, n = fields.length, out = [];
|
1149 | for ( i = 0; i < n; i ++ )
|
1150 | out.push ( fields [ i ], this.HGET ( key, fields [ i ] ) );
|
1151 |
|
1152 | return out;
|
1153 | },
|
1154 |
|
1155 | hIncrBy : function ( parse, key, field, incr )
|
1156 | {
|
1157 | var K = this.getKey ( HASH, key, true );
|
1158 | if ( K instanceof ERROR ) return K;
|
1159 |
|
1160 | incr = parse ( incr );
|
1161 | if ( incr instanceof ERROR ) return incr;
|
1162 | var value = parse ( K.value [ field ] || "0" );
|
1163 | if ( value instanceof ERROR ) return value;
|
1164 |
|
1165 | K.value [ field ] = ( value + incr ).toString ();
|
1166 | this.upsetKey ( key, K );
|
1167 | return K.value [ field ];
|
1168 | },
|
1169 |
|
1170 | HINCRBY : function ( key, field, incr )
|
1171 | {
|
1172 | return this.hIncrBy ( str2int, key, field, incr );
|
1173 | },
|
1174 |
|
1175 | HINCRBYFLOAT : function ( key, field, incr )
|
1176 | {
|
1177 | return this.hIncrBy ( str2float, key, field, incr );
|
1178 | },
|
1179 |
|
1180 | HKEYS : function ( key )
|
1181 | {
|
1182 | var K = this.getKey ( HASH, key );
|
1183 | if ( K instanceof ERROR ) return K;
|
1184 |
|
1185 | var fields = [], field;
|
1186 | if ( K ) for ( field in K.value )
|
1187 | fields.push ( field );
|
1188 |
|
1189 | fields.sort ();
|
1190 | return fields;
|
1191 | },
|
1192 |
|
1193 | HLEN : function ( key )
|
1194 | {
|
1195 | var fields = this.HKEYS ( key );
|
1196 | if ( fields.indexOf ) return fields.length;
|
1197 | return fields;
|
1198 | },
|
1199 |
|
1200 | HMGET : function ()
|
1201 | {
|
1202 | var K = this.getKey ( HASH, arguments [ 0 ] );
|
1203 | if ( K instanceof ERROR ) return K;
|
1204 |
|
1205 | var i, n = arguments.length, values = [];
|
1206 | if ( n < 2 ) return BAD_ARGS;
|
1207 | for ( i = 1; i < n; i ++ )
|
1208 | values.push ( K && arguments [ i ] in K.value ? K.value [ arguments [ i ] ] : null );
|
1209 |
|
1210 | return values;
|
1211 | },
|
1212 |
|
1213 | HMSET : function ()
|
1214 | {
|
1215 | var x = this.structPut ( HASH, String, false, arguments );
|
1216 | return x instanceof ERROR ? x : OK;
|
1217 | },
|
1218 |
|
1219 | HSET : function ( key, field, value )
|
1220 | {
|
1221 | return this.structPut ( HASH, String, false, arguments );
|
1222 | },
|
1223 |
|
1224 | HSETNX : function ( key, field, value )
|
1225 | {
|
1226 | var exists = this.HEXISTS ( key, field );
|
1227 | if ( exists instanceof ERROR ) return exists;
|
1228 | if ( exists ) return 0;
|
1229 | return this.HSET ( key, field, value );
|
1230 | },
|
1231 |
|
1232 | HVALS : function ( key )
|
1233 | {
|
1234 | var out = this.HKEYS ( key ), self = this;
|
1235 | if ( out instanceof ERROR ) return out;
|
1236 |
|
1237 | if ( out.map )
|
1238 | out = out.map ( function ( field )
|
1239 | {
|
1240 | return self.HGET ( key, field );
|
1241 | });
|
1242 |
|
1243 | out.sort ();
|
1244 | return out;
|
1245 | },
|
1246 |
|
1247 |
|
1248 |
|
1249 |
|
1250 |
|
1251 | SADD : function ()
|
1252 | {
|
1253 | var key = arguments [ 0 ],
|
1254 | i, n = arguments.length,
|
1255 | x = 0;
|
1256 |
|
1257 | if ( n < 2 ) return BAD_ARGS;
|
1258 | var K = this.getKey ( SET, key, true );
|
1259 | if ( K instanceof ERROR ) return K;
|
1260 |
|
1261 | for ( i = 1; i < n; i ++ )
|
1262 | if ( !K.value [ arguments [ i ] ] )
|
1263 | {
|
1264 | K.value [ arguments [ i ] ] = true;
|
1265 | x ++;
|
1266 | }
|
1267 |
|
1268 | if ( x ) this.upsetKey ( key, K );
|
1269 | return x;
|
1270 | },
|
1271 |
|
1272 | SCARD : function ( key )
|
1273 | {
|
1274 | var members = this.SMEMBERS ( key );
|
1275 | return members.join ? members.length : members;
|
1276 | },
|
1277 |
|
1278 | SISMEMBER : function ( key, member )
|
1279 | {
|
1280 | var members = this.SMEMBERS ( key );
|
1281 | return members.indexOf ? members.indexOf ( member ) >= 0 ? 1 : 0 : members;
|
1282 | },
|
1283 |
|
1284 | SMEMBERS : function ( key )
|
1285 | {
|
1286 | return this.SUNION ( key );
|
1287 | },
|
1288 |
|
1289 | SPOP : function ( key )
|
1290 | {
|
1291 | var member = this.SRANDMEMBER ( key );
|
1292 | if ( typeof member === 'string' )
|
1293 | this.SREM ( key, member );
|
1294 |
|
1295 | return member;
|
1296 | },
|
1297 |
|
1298 | SRANDMEMBER : function ( key )
|
1299 | {
|
1300 | var members = this.SMEMBERS ( key ),
|
1301 | n = members.length, member;
|
1302 | if ( !n )
|
1303 | return n === 0 ? null : members;
|
1304 |
|
1305 | member = members [ Math.floor ( Math.random () * n ) ];
|
1306 | return member;
|
1307 | },
|
1308 |
|
1309 | SREM : function ()
|
1310 | {
|
1311 | return this.structDel ( SET, arguments );
|
1312 | },
|
1313 |
|
1314 |
|
1315 |
|
1316 |
|
1317 | SMOVE : function ( source, destination, member )
|
1318 | {
|
1319 | var removed = this.SREM ( source, member );
|
1320 | if ( removed === 1 )
|
1321 | return this.SADD ( destination, member );
|
1322 | else
|
1323 | return removed;
|
1324 | },
|
1325 |
|
1326 | SUNION : function ()
|
1327 | {
|
1328 | var i, n = arguments.length, out = [];
|
1329 | if ( !n ) return BAD_ARGS;
|
1330 |
|
1331 | for ( i = 0; i < n; i ++ )
|
1332 | {
|
1333 | var K = this.getKey ( SET, arguments [ i ] );
|
1334 | if ( K instanceof ERROR ) return K;
|
1335 |
|
1336 | var member;
|
1337 | if ( K ) for ( member in K.value )
|
1338 | if ( out.indexOf ( member ) < 0 )
|
1339 | out.push ( member );
|
1340 | }
|
1341 |
|
1342 | out.sort ();
|
1343 | return out;
|
1344 | },
|
1345 |
|
1346 | sCombine : function ( diff, args )
|
1347 | {
|
1348 | var i, n = args.length;
|
1349 | if ( !n )
|
1350 | return BAD_ARGS;
|
1351 |
|
1352 | var out = this.SUNION ( args [ 0 ] );
|
1353 | if ( out instanceof ERROR ) return out;
|
1354 | for ( i = 1; i < n; i ++ )
|
1355 | {
|
1356 | var K = this.getKey ( SET, args [ i ] );
|
1357 | if ( K instanceof ERROR ) return K;
|
1358 |
|
1359 | var j, m = out.length;
|
1360 | if ( K ) for ( j = 0; j < m; j ++ )
|
1361 | if ( ( diff && K.value [ out [ j ] ] ) || ( !diff && !K.value [ out [ j ] ] ) )
|
1362 | {
|
1363 | out.splice ( j, 1 );
|
1364 | j --;
|
1365 | m --;
|
1366 | }
|
1367 | }
|
1368 |
|
1369 | out.sort ();
|
1370 | return out;
|
1371 | },
|
1372 |
|
1373 | SDIFF : function ()
|
1374 | {
|
1375 | return this.sCombine ( true, arr ( arguments ) );
|
1376 | },
|
1377 |
|
1378 | SINTER : function ()
|
1379 | {
|
1380 | return this.sCombine ( false, arr ( arguments ) );
|
1381 | },
|
1382 |
|
1383 | sStore : function ( key, members )
|
1384 | {
|
1385 | var K, i, n = members.length;
|
1386 | if ( n ) K = new SET ({});
|
1387 | for ( i = 0; i < n; i ++ )
|
1388 | K.value [ members [ i ] ] = true;
|
1389 |
|
1390 | return this.setKey ( key, K || null );
|
1391 | },
|
1392 |
|
1393 | sStoreOp : function ( op, args )
|
1394 | {
|
1395 | if ( !args.length )
|
1396 | return BAD_ARGS;
|
1397 |
|
1398 | var key = args.shift (),
|
1399 | members = op.apply ( this, args );
|
1400 |
|
1401 | if ( members.join )
|
1402 | {
|
1403 | this.sStore ( key, members );
|
1404 | return members.length;
|
1405 | }
|
1406 |
|
1407 | return members;
|
1408 | },
|
1409 |
|
1410 | SDIFFSTORE : function ()
|
1411 | {
|
1412 | return this.sStoreOp ( this.SDIFF, arr ( arguments ) );
|
1413 | },
|
1414 |
|
1415 | SINTERSTORE : function ()
|
1416 | {
|
1417 | return this.sStoreOp ( this.SINTER, arr ( arguments ) );
|
1418 | },
|
1419 |
|
1420 | SUNIONSTORE : function ()
|
1421 | {
|
1422 | return this.sStoreOp ( this.SUNION, arr ( arguments ) );
|
1423 | },
|
1424 |
|
1425 |
|
1426 |
|
1427 |
|
1428 |
|
1429 | ZADD : function ()
|
1430 | {
|
1431 | return this.structPut ( ZSET, str2float, true, arguments );
|
1432 | },
|
1433 |
|
1434 | ZCARD : function ( key )
|
1435 | {
|
1436 | return this.ZCOUNT ( key, '-inf', '+inf' );
|
1437 | },
|
1438 |
|
1439 | ZCOUNT : function ( key, min, max )
|
1440 | {
|
1441 | var members = this.ZRANGEBYSCORE ( key, min, max );
|
1442 | return members.join ? members.length : members;
|
1443 | },
|
1444 |
|
1445 | ZINCRBY : function ( key, incr, member )
|
1446 | {
|
1447 | var K = this.getKey ( ZSET, key, true );
|
1448 | if ( K instanceof ERROR ) return K;
|
1449 |
|
1450 | var value = str2float ( incr );
|
1451 | if ( value instanceof ERROR ) return value;
|
1452 | value += Number ( K.value [ member ] || 0 );
|
1453 |
|
1454 | K.value [ member ] = value;
|
1455 | this.upsetKey ( key, K );
|
1456 | return value;
|
1457 | },
|
1458 |
|
1459 |
|
1460 |
|
1461 | zSort : function ( rev, key, min, max )
|
1462 | {
|
1463 | var K = this.getKey ( ZSET, key );
|
1464 | if ( K instanceof ERROR ) return K;
|
1465 | if ( !K ) return [];
|
1466 |
|
1467 | var R = range ( min, max ), member, out = [];
|
1468 | if ( R instanceof ERROR ) return R;
|
1469 |
|
1470 | for ( member in K.value )
|
1471 | if ( R ( K.value [ member ] ) )
|
1472 | out.push ({ member : member, score : K.value [ member ] });
|
1473 |
|
1474 |
|
1475 |
|
1476 |
|
1477 | if ( rev )
|
1478 | out.sort ( function ( b, a )
|
1479 | {
|
1480 | return ( a.score - b.score ) || ( a.member < b.member ? -1 : 1 );
|
1481 | });
|
1482 |
|
1483 | else
|
1484 | out.sort ( function ( a, b )
|
1485 | {
|
1486 | return ( a.score - b.score ) || ( a.member < b.member ? -1 : 1 );
|
1487 | });
|
1488 |
|
1489 | return out;
|
1490 | },
|
1491 |
|
1492 | zUnwrap : function ( range, scores )
|
1493 | {
|
1494 | var i, n = range.length, out = n ? [] : range;
|
1495 | if ( n )
|
1496 | for ( i = 0; i < n; i ++ )
|
1497 | {
|
1498 | out.push ( range [ i ].member );
|
1499 | if ( scores )
|
1500 | out.push ( range [ i ].score );
|
1501 | }
|
1502 |
|
1503 | return out;
|
1504 | },
|
1505 |
|
1506 | zGetRange : function ( rev, args )
|
1507 | {
|
1508 | var key = args [ 0 ], start = args [ 1 ], stop = args [ 2 ], scores = args [ 3 ];
|
1509 |
|
1510 | if ( args.length < 3 || args.length > 4 )
|
1511 | return BAD_ARGS;
|
1512 | if ( scores && scores.toUpperCase () !== 'WITHSCORES' )
|
1513 | return BAD_SYNTAX;
|
1514 |
|
1515 | var range = this.zSort ( rev, key, '-inf', '+inf' );
|
1516 |
|
1517 | return this.zUnwrap ( slice ( range, start, stop ), scores );
|
1518 | },
|
1519 |
|
1520 | zGetRangeByScore : function ( rev, args )
|
1521 | {
|
1522 | var key = args [ 0 ], min = args [ rev ? 2 : 1 ], max = args [ rev ? 1 : 2 ],
|
1523 | scores, limit, offset, count;
|
1524 |
|
1525 | if ( args.length < 3 )
|
1526 | return BAD_ARGS;
|
1527 |
|
1528 | else if ( args.length === 4 )
|
1529 | scores = args [ 3 ];
|
1530 |
|
1531 | else if ( args.length === 6 )
|
1532 | {
|
1533 | limit = args [ 3 ];
|
1534 | offset = args [ 4 ];
|
1535 | count = args [ 5 ];
|
1536 | }
|
1537 |
|
1538 | else if ( args.length === 7 )
|
1539 | {
|
1540 | scores = args [ 3 ];
|
1541 | limit = args [ 4 ];
|
1542 | offset = args [ 5 ];
|
1543 | count = args [ 6 ];
|
1544 | }
|
1545 |
|
1546 | if ( scores && scores.toUpperCase () !== 'WITHSCORES' )
|
1547 | return BAD_SYNTAX;
|
1548 | if ( limit && limit.toUpperCase () !== 'LIMIT' )
|
1549 | return BAD_SYNTAX;
|
1550 |
|
1551 | var range = this.zSort ( rev, key, min, max );
|
1552 | if ( limit )
|
1553 | range = slice ( range, offset, count, true );
|
1554 |
|
1555 | return this.zUnwrap ( range, scores );
|
1556 | },
|
1557 |
|
1558 | ZRANGE : function ()
|
1559 | {
|
1560 | return this.zGetRange ( false, arr ( arguments ) );
|
1561 | },
|
1562 |
|
1563 | ZREVRANGE : function ()
|
1564 | {
|
1565 | return this.zGetRange ( true, arr ( arguments ) );
|
1566 | },
|
1567 |
|
1568 | ZRANGEBYSCORE : function ()
|
1569 | {
|
1570 | return this.zGetRangeByScore ( false, arr ( arguments ) );
|
1571 | },
|
1572 |
|
1573 | ZREVRANGEBYSCORE : function ()
|
1574 | {
|
1575 | return this.zGetRangeByScore ( true, arr ( arguments ) );
|
1576 | },
|
1577 |
|
1578 | ZRANK : function ( key, member )
|
1579 | {
|
1580 | var out = this.zSort ( false, key, '-inf', '+inf' ),
|
1581 | i, n = out.length;
|
1582 |
|
1583 | for ( i = 0; i < n; i ++ )
|
1584 | if ( out [ i ].member === member )
|
1585 | return i;
|
1586 |
|
1587 | return n || n === 0 ? null : out;
|
1588 | },
|
1589 |
|
1590 | ZREVRANK : function ( key, member )
|
1591 | {
|
1592 | var out = this.zSort ( false, key, '-inf', '+inf' ),
|
1593 | i, n = out.length;
|
1594 |
|
1595 | for ( i = n - 1; i >= 0; i -- )
|
1596 | if ( out [ i ].member === member )
|
1597 | return n - i - 1;
|
1598 |
|
1599 | return n || n === 0 ? null : out;
|
1600 | },
|
1601 |
|
1602 | ZSCORE : function ( key, member )
|
1603 | {
|
1604 | return this.structGet ( ZSET, key, member );
|
1605 | },
|
1606 |
|
1607 | ZREM : function ()
|
1608 | {
|
1609 | return this.structDel ( ZSET, arguments );
|
1610 | },
|
1611 |
|
1612 | ZREMRANGEBYRANK : function ( key, start, stop )
|
1613 | {
|
1614 | var members = this.ZRANGE ( key, start, stop ), n = members.length;
|
1615 | if ( n )
|
1616 | n = this.ZREM.apply ( this, [ key ].concat ( members ) );
|
1617 |
|
1618 | return n || n === 0 ? n : members;
|
1619 | },
|
1620 |
|
1621 | ZREMRANGEBYSCORE : function ( key, min, max )
|
1622 | {
|
1623 | var members = this.ZRANGEBYSCORE ( key, min, max ), n = members.length;
|
1624 | if ( n )
|
1625 | n = this.ZREM.apply ( this, [ key ].concat ( members ) );
|
1626 |
|
1627 | return n || n === 0 ? n : members;
|
1628 | },
|
1629 |
|
1630 |
|
1631 |
|
1632 | zOpStore : function ( union, key, keys, weights, aggregate )
|
1633 | {
|
1634 | var K = this.getKey ( ZSET, keys [ 0 ] );
|
1635 | if ( K instanceof ERROR ) return K;
|
1636 |
|
1637 | var out = {}, member, x = 0, weight = ( weights === null ? 1 : weights [ 0 ] );
|
1638 | if ( K ) for ( member in K.value )
|
1639 | {
|
1640 | out [ member ] = K.value [ member ] * weight;
|
1641 | x ++;
|
1642 | }
|
1643 |
|
1644 | var i, n = keys.length;
|
1645 | for ( i = 1; i < n; i ++ )
|
1646 | {
|
1647 | K = this.getKey ( ZSET, keys [ i ] );
|
1648 | if ( K instanceof ERROR ) return K;
|
1649 |
|
1650 | weight = ( weights !== null ? weights [ i ] : 1 );
|
1651 | if ( !union )
|
1652 | {
|
1653 | if ( !K )
|
1654 | {
|
1655 | out = {};
|
1656 | x = 0;
|
1657 | }
|
1658 |
|
1659 | else for ( member in out ) if ( !( member in K.value ) )
|
1660 | {
|
1661 | delete out [ member ];
|
1662 | x --;
|
1663 | }
|
1664 | }
|
1665 |
|
1666 | if ( K ) for ( member in K.value )
|
1667 | if ( union || member in out )
|
1668 | {
|
1669 | if ( !( member in out ) )
|
1670 | {
|
1671 | x ++;
|
1672 | out [ member ] = K.value [ member ] * weight;
|
1673 | }
|
1674 |
|
1675 | else
|
1676 | out [ member ] = aggregate ( K.value [ member ] * weight, out [ member ] );
|
1677 | }
|
1678 | }
|
1679 |
|
1680 | if ( x ) this.setKey ( key, new ZSET ( out ) );
|
1681 | return x;
|
1682 | },
|
1683 |
|
1684 | zsum : function ( a, b ) { return a + b; },
|
1685 | zmin : function ( a, b ) { return a < b ? a : b; },
|
1686 | zmax : function ( a, b ) { return a > b ? a : b; },
|
1687 |
|
1688 | zParseOpStore : function ( union, args )
|
1689 | {
|
1690 | var key = args [ 0 ], N = str2int ( args [ 1 ] );
|
1691 | if ( N instanceof ERROR ) return N;
|
1692 | if ( N < 1 ) return BAD_ZUIS;
|
1693 | if ( args.length < N + 2 ) return BAD_ARGS;
|
1694 |
|
1695 | var keys = args.splice ( 2, N ), weigh = ( args [ 2 ] || '' ).toUpperCase () === 'WEIGHTS', weights;
|
1696 | if ( weigh )
|
1697 | {
|
1698 | if ( args.length < N + 3 ) return BAD_ARGS;
|
1699 | weights = args.splice ( 3, N );
|
1700 | if ( weights.map ( str2float ).some ( function ( w ) { return w instanceof ERROR; } ) ) return BAD_FLOAT;
|
1701 | args.splice ( 2, 1 );
|
1702 | }
|
1703 |
|
1704 | var aggregate = ( args [ 2 ] || '' ).toUpperCase () === 'AGGREGATE' ? ( args [ 3 ] || '' ).toLowerCase () : null;
|
1705 | if ( aggregate )
|
1706 | {
|
1707 | if ( aggregate !== 'sum' && aggregate !== 'min' && aggregate !== 'max' ) return BAD_SYNTAX;
|
1708 | aggregate = this [ 'z' + aggregate ];
|
1709 | if ( typeof aggregate !== 'function' )
|
1710 | throw new Error ( "WOOT! Can't find the aggregate function for " + args [ 3 ] );
|
1711 | args.splice ( 2, 2 );
|
1712 | }
|
1713 |
|
1714 | if ( args.length !== 2 )
|
1715 | return BAD_ARGS;
|
1716 |
|
1717 | return this.zOpStore ( union, key, keys, weights || null, aggregate || this.zsum );
|
1718 | },
|
1719 |
|
1720 | ZINTERSTORE : function ()
|
1721 | {
|
1722 | return this.zParseOpStore ( false, arr ( arguments ) );
|
1723 | },
|
1724 |
|
1725 | ZUNIONSTORE : function ()
|
1726 | {
|
1727 | return this.zParseOpStore ( true, arr ( arguments ) );
|
1728 | },
|
1729 |
|
1730 |
|
1731 |
|
1732 |
|
1733 |
|
1734 | sortSelect : function ( pat, key )
|
1735 | {
|
1736 | var select = /^((?:.)*?)(?:->(.*))?$/.exec ( pat ),
|
1737 | key = select [ 1 ].replace ( /\*/, key ),
|
1738 | field = select [ 2 ];
|
1739 |
|
1740 | if ( typeof field === 'string' )
|
1741 | return this.HGET ( key, field );
|
1742 | else
|
1743 | return this.GET ( key );
|
1744 | },
|
1745 |
|
1746 | SORT : function ()
|
1747 | {
|
1748 | var self = this, args = arr ( arguments ), n = args.length;
|
1749 | if ( !n ) return new BAD_ARGS;
|
1750 |
|
1751 |
|
1752 |
|
1753 |
|
1754 | var key = args.shift (),
|
1755 | by, limit, offset, count, get, pat, desc, alpha, store;
|
1756 |
|
1757 | if ( /^by$/i.test ( args [ 0 ] ) )
|
1758 | {
|
1759 | by = args [ 1 ];
|
1760 | if ( typeof by !== 'string' ) return BAD_SYNTAX;
|
1761 | args.splice ( 0, 2 );
|
1762 | }
|
1763 |
|
1764 | if ( /^limit$/i.test ( args [ 0 ] ) )
|
1765 | {
|
1766 | limit = true;
|
1767 | if ( args.length < 3 ) return BAD_ARGS;
|
1768 | offset = args [ 1 ];
|
1769 | count = args [ 2 ];
|
1770 | args.splice ( 0, 3 );
|
1771 | }
|
1772 |
|
1773 | while ( /^get$/i.test ( args [ 0 ] ) )
|
1774 | {
|
1775 | pat = args [ 1 ];
|
1776 | if ( typeof pat !== 'string' ) return BAD_SYNTAX;
|
1777 | if ( !get ) get = [];
|
1778 | get.push ( pat );
|
1779 | args.splice ( 0, 2 );
|
1780 | }
|
1781 |
|
1782 | if ( /^asc|desc$/i.test ( args [ 0 ] ) )
|
1783 | {
|
1784 | desc = /^desc$/i.test ( args [ 0 ] );
|
1785 | args.splice ( 0, 1 );
|
1786 | }
|
1787 |
|
1788 | if ( /^alpha$/i.test ( args [ 0 ] ) )
|
1789 | {
|
1790 | alpha = true;
|
1791 | args.splice ( 0, 1 );
|
1792 | }
|
1793 |
|
1794 | if ( /^store$/i.test ( args [ 0 ] ) )
|
1795 | {
|
1796 | store = args [ 1 ];
|
1797 | if ( typeof store !== 'string' ) return BAD_SYNTAX;
|
1798 | args.splice ( 0, 2 );
|
1799 | }
|
1800 |
|
1801 |
|
1802 |
|
1803 |
|
1804 | if ( args.length ) return BAD_SYNTAX;
|
1805 |
|
1806 |
|
1807 |
|
1808 | var type = this.TYPE ( key ), data, scoreFail = false;
|
1809 |
|
1810 | if ( type === NONE )
|
1811 | data = [];
|
1812 | else if ( type === LIST )
|
1813 | data = this.LRANGE ( key, '0', '-1' );
|
1814 | else if ( type === SET )
|
1815 | data = this.SMEMBERS ( key );
|
1816 | else if ( type === ZSET )
|
1817 | data = this.ZRANGE ( key, '0', '-1' );
|
1818 | else
|
1819 | return BAD_TYPE;
|
1820 |
|
1821 | data = data.map ( function ( id )
|
1822 | {
|
1823 | var entry = { id : id };
|
1824 | if ( by )
|
1825 | {
|
1826 | entry.by = self.sortSelect ( by, id );
|
1827 | if ( !alpha )
|
1828 | entry.num = str2float ( entry.by || '0' );
|
1829 | }
|
1830 | else if ( !alpha )
|
1831 | entry.num = str2float ( id );
|
1832 | else
|
1833 | entry.num = 0;
|
1834 |
|
1835 | if ( entry.num instanceof ERROR )
|
1836 | scoreFail = true;
|
1837 |
|
1838 | if ( get )
|
1839 | entry.get = get.map ( function ( get )
|
1840 | {
|
1841 | if ( get === '#' ) return id;
|
1842 | return self.sortSelect ( get, id );
|
1843 | });
|
1844 |
|
1845 | return entry;
|
1846 | });
|
1847 |
|
1848 | if ( scoreFail ) return BAD_SORT;
|
1849 |
|
1850 | //// Sort.
|
1851 |
|
1852 | data.sort ( function ( a, b )
|
1853 | {
|
1854 | var d = a.num - b.num;
|
1855 | if ( !d && by ) d = a.by < b.by ? -1 : a.by > b.by ? 1 : 0;
|
1856 | if ( !d ) d = a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
1857 | return desc ? -d : d;
|
1858 | });
|
1859 |
|
1860 |
|
1861 |
|
1862 | if ( parseInt ( offset ) < 0 )
|
1863 | offset = '0';
|
1864 |
|
1865 | if ( limit ) data = slice ( data, offset, count, true );
|
1866 |
|
1867 |
|
1868 |
|
1869 | var out = [], i;
|
1870 | n = data.length;
|
1871 | for ( i = 0; i < n; i ++ )
|
1872 | {
|
1873 | if ( get ) out.push.apply ( out, data [ i ].get );
|
1874 | else out [ i ] = data [ i ].id;
|
1875 | }
|
1876 |
|
1877 | //// Store or return.
|
1878 |
|
1879 | if ( store )
|
1880 | {
|
1881 | this.lStore ( store, out );
|
1882 | return this.LLEN ( store );
|
1883 | }
|
1884 | else
|
1885 | return out;
|
1886 | },
|
1887 |
|
1888 |
|
1889 |
|
1890 |
|
1891 |
|
1892 | PUBLISH : function ( channel, message )
|
1893 | {
|
1894 | return this.pub ( channel, message );
|
1895 | },
|
1896 |
|
1897 |
|
1898 |
|
1899 |
|
1900 |
|
1901 |
|
1902 | PING : function ()
|
1903 | {
|
1904 | if ( arguments.length )
|
1905 | return BAD_ARGS;
|
1906 |
|
1907 | return PONG;
|
1908 | },
|
1909 |
|
1910 | ECHO : function ( message )
|
1911 | {
|
1912 | return message;
|
1913 | },
|
1914 |
|
1915 |
|
1916 |
|
1917 |
|
1918 |
|
1919 |
|
1920 | DBSIZE : function ()
|
1921 | {
|
1922 | return this.getKeys ().length;
|
1923 | },
|
1924 |
|
1925 | FLUSHDB : function ()
|
1926 | {
|
1927 | var keys = this.getKeys (), i, n = keys.length;
|
1928 | for ( i = 0; i < n; i ++ )
|
1929 | this.setKey ( keys [ i ], null );
|
1930 |
|
1931 | return OK;
|
1932 | },
|
1933 |
|
1934 | TIME : function ()
|
1935 | {
|
1936 | var time = Date.now (),
|
1937 | sec = Math.round ( time / 1000 ),
|
1938 | msec = ( time % 1000 ) * 1000 + Math.floor ( Math.random () * 1000 );
|
1939 |
|
1940 | return [ sec, msec ];
|
1941 | },
|
1942 |
|
1943 |
|
1944 |
|
1945 |
|
1946 |
|
1947 |
|
1948 |
|
1949 |
|
1950 |
|
1951 |
|
1952 |
|
1953 |
|
1954 |
|
1955 |
|
1956 |
|
1957 |
|
1958 |
|
1959 |
|
1960 |
|
1961 |
|
1962 |
|
1963 |
|
1964 |
|
1965 |
|
1966 |
|
1967 | FAKE_DUMP : function ( pattern )
|
1968 | {
|
1969 | var keys = this.KEYS ( pattern ), i, n = keys.length, out = [], key, type;
|
1970 |
|
1971 | for ( i = 0; i < n; i ++ )
|
1972 | {
|
1973 | key = keys [ i ];
|
1974 | type = this.TYPE ( key );
|
1975 | out.push ( key, this.TTL ( key ), type.getStatus () );
|
1976 |
|
1977 | if ( type === STRING )
|
1978 | out.push ( this.GET ( key ) );
|
1979 | else if ( type === LIST )
|
1980 | out.push ( this.LRANGE ( key, '0', '-1' ) );
|
1981 | else if ( type === HASH )
|
1982 | out.push ( this.HGETALL ( key ) );
|
1983 | else if ( type === SET )
|
1984 | out.push ( this.SMEMBERS ( key ) );
|
1985 | else if ( type === ZSET )
|
1986 | out.push ( this.ZRANGE ( key, '0', '-1', 'withscores' ) );
|
1987 | else
|
1988 | throw new Error ( "WOOT! Key type is " + type );
|
1989 | }
|
1990 |
|
1991 | return out;
|
1992 | }
|
1993 | };
|
1994 |
|
1995 |
|
1996 |
|
1997 |
|
1998 |
|
1999 | exports.Backend.prototype.AUTH =
|
2000 | exports.Backend.prototype.BGREWRITEAOF =
|
2001 | exports.Backend.prototype.SAVE =
|
2002 | exports.Backend.prototype.BGSAVE = function () { return OK; };
|
2003 |
|
2004 |
|
2005 |
|
2006 |
|
2007 |
|
2008 | exports.Backend.prototype.QUIT =
|
2009 |
|
2010 | exports.Backend.prototype.SUBSCRIBE =
|
2011 | exports.Backend.prototype.PSUBSCRIBE =
|
2012 | exports.Backend.prototype.UNSUBSCRIBE =
|
2013 | exports.Backend.prototype.PUNSUBSCRIBE =
|
2014 |
|
2015 | exports.Backend.prototype.MULTI =
|
2016 | exports.Backend.prototype.EXEC =
|
2017 | exports.Backend.prototype.WATCH =
|
2018 | exports.Backend.prototype.UNWATCH =
|
2019 | exports.Backend.prototype.SELECT =
|
2020 | exports.Backend.prototype.DISCARD = function () { throw new Error ( "WOOT! This command shouldn't have reached the backend." ); };
|
2021 |
|
2022 |
|
2023 |
|