1 /*
2  * Hunt - A redis client library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11  
12 module hunt.redis.BinaryClient;
13 
14 import hunt.redis.AbstractClient;
15 import hunt.redis.BitOP;
16 import hunt.redis.BitPosParams;
17 import hunt.redis.ListPosition;
18 import hunt.redis.GeoCoordinate;
19 import hunt.redis.GeoUnit;
20 import hunt.redis.Protocol;
21 import hunt.redis.ScanParams;
22 import hunt.redis.SortingParams;
23 import hunt.redis.ZParams;
24 
25 // import hunt.redis.Protocol.Keyword;
26 import hunt.redis.params.ClientKillParams;
27 import hunt.redis.params.GeoRadiusParam;
28 import hunt.redis.params.MigrateParams;
29 import hunt.redis.params.SetParams;
30 import hunt.redis.params.ZAddParams;
31 import hunt.redis.params.ZIncrByParams;
32 import hunt.redis.util.SafeEncoder;
33 
34 import hunt.collection.ArrayList;
35 import hunt.collection.List;
36 import hunt.collection.Map;
37 
38 import std.conv;
39 
40 alias Keyword = Protocol.Keyword;
41 alias Command = Protocol.Command;
42 alias toByteArray = Protocol.toByteArray;
43 
44 class BinaryClient : AbstractClient { 
45 
46     private bool _isInMulti;
47 
48     private string password;
49 
50     private int db;
51 
52     private bool _isInWatch;
53 
54     this() {
55         super();
56     }
57 
58     this(string host) {
59         super(host);
60     }
61 
62     this(string host, int port) {
63         super(host, port);
64     }
65 
66     this(string host, int port, bool ssl) {
67         super(host, port, ssl);
68     }
69 
70     // this(string host, int port, bool ssl,
71     //     SSLSocketFactory sslSocketFactory, SSLParameters sslParameters,
72     //     HostnameVerifier hostnameVerifier) {
73     //   super(host, port, ssl, sslSocketFactory, sslParameters, hostnameVerifier);
74     // }
75 
76     bool isInMulti() {
77         return _isInMulti;
78     }
79 
80     bool isInWatch() {
81         return _isInWatch;
82     }
83 
84     private const(ubyte)[][] joinParameters(const(ubyte)[] first, const(ubyte)[][] rest) {
85         const(ubyte)[][] result = new const(ubyte)[][rest.length + 1];
86         result[0] = first;
87         // System.arraycopy(rest, 0, result, 1, rest.length);
88         result[1 .. $] = rest[0 .. $];
89         return result;
90     }
91 
92     private const(ubyte)[][] joinParameters(const(ubyte)[] first, const(ubyte)[] second, const(ubyte)[][] rest) {
93         const(ubyte)[][] result = new const(ubyte)[][rest.length + 2];
94         result[0] = first;
95         result[1] = second;
96         // System.arraycopy(rest, 0, result, 2, rest.length);
97         result[2 .. $] = rest[0 .. $];
98         return result;
99     }
100 
101     void setPassword(string password) {
102         this.password = password;
103     }
104 
105     void setDb(int db) {
106         this.db = db;
107     }
108 
109     override
110     void connect() {
111         if (!isConnected()) {
112             super.connect();
113             if (password !is null) {
114                 auth(password);
115                 getStatusCodeReply();
116             }
117             if (db > 0) {
118                 select(db);
119                 getStatusCodeReply();
120             }
121         }
122     }
123 
124     void ping() {
125         sendCommand(Command.PING);
126     }
127 
128     void ping(const(ubyte)[] message) {
129         sendCommand(Command.PING, [message]);
130     }
131 
132     void set(const(ubyte)[] key, const(ubyte)[] value) {
133         sendCommand(Command.SET, key, value);
134     }
135 
136     void set(const(ubyte)[] key, const(ubyte)[] value, SetParams params) {
137         sendCommand(Command.SET, params.getByteParams(key, value));
138     }
139 
140     void get(const(ubyte)[] key) {
141         sendCommand(Command.GET, key);
142     }
143 
144     void quit() {
145         db = 0;
146         sendCommand(Command.QUIT);
147     }
148 
149     void exists(const(ubyte)[][] keys...) {
150         sendCommand(Command.EXISTS, keys);
151     }
152 
153     void del(const(ubyte)[][] keys...) {
154         sendCommand(Command.DEL, keys);
155     }
156 
157     void unlink(const(ubyte)[][] keys...) {
158         sendCommand(Command.UNLINK, keys);
159     }
160 
161     void type(const(ubyte)[] key) {
162         sendCommand(Command.TYPE, key);
163     }
164 
165     void flushDB() {
166         sendCommand(Command.FLUSHDB);
167     }
168 
169     void keys(const(ubyte)[] pattern) {
170         sendCommand(Command.KEYS, pattern);
171     }
172 
173     void randomKey() {
174         sendCommand(Command.RANDOMKEY);
175     }
176 
177     void rename(const(ubyte)[] oldkey, const(ubyte)[] newkey) {
178         sendCommand(Command.RENAME, oldkey, newkey);
179     }
180 
181     void renamenx(const(ubyte)[] oldkey, const(ubyte)[] newkey) {
182         sendCommand(Command.RENAMENX, oldkey, newkey);
183     }
184 
185     void dbSize() {
186         sendCommand(Command.DBSIZE);
187     }
188 
189     void expire(const(ubyte)[] key, int seconds) {
190         sendCommand(Command.EXPIRE, key, toByteArray(seconds));
191     }
192 
193     void expireAt(const(ubyte)[] key, long unixTime) {
194         sendCommand(Command.EXPIREAT, key, toByteArray(unixTime));
195     }
196 
197     void ttl(const(ubyte)[] key) {
198         sendCommand(Command.TTL, key);
199     }
200 
201     void touch(const(ubyte)[][] keys...) {
202         sendCommand(Command.TOUCH, keys);
203     }
204 
205     void select(int index) {
206         sendCommand(Command.SELECT, toByteArray(index));
207     }
208 
209     void swapDB(int index1, int index2) {
210         sendCommand(Command.SWAPDB, toByteArray(index1), toByteArray(index2));
211     }
212 
213     void move(const(ubyte)[] key, int dbIndex) {
214         sendCommand(Command.MOVE, key, toByteArray(dbIndex));
215     }
216 
217     void flushAll() {
218         sendCommand(Command.FLUSHALL);
219     }
220 
221     void getSet(const(ubyte)[] key, const(ubyte)[] value) {
222         sendCommand(Command.GETSET, key, value);
223     }
224 
225     void mget(const(ubyte)[][] keys...) {
226         sendCommand(Command.MGET, keys);
227     }
228 
229     void setnx(const(ubyte)[] key, const(ubyte)[] value) {
230         sendCommand(Command.SETNX, key, value);
231     }
232 
233     void setex(const(ubyte)[] key, int seconds, const(ubyte)[] value) {
234         sendCommand(Command.SETEX, key, toByteArray(seconds), value);
235     }
236 
237     void mset(const(ubyte)[][] keysvalues...) {
238         sendCommand(Command.MSET, keysvalues);
239     }
240 
241     void msetnx(const(ubyte)[][] keysvalues...) {
242         sendCommand(Command.MSETNX, keysvalues);
243     }
244 
245     void decrBy(const(ubyte)[] key, long decrement) {
246         sendCommand(Command.DECRBY, key, toByteArray(decrement));
247     }
248 
249     void decr(const(ubyte)[] key) {
250         sendCommand(Command.DECR, key);
251     }
252 
253     void incrBy(const(ubyte)[] key, long increment) {
254         sendCommand(Command.INCRBY, key, toByteArray(increment));
255     }
256 
257     void incrByFloat(const(ubyte)[] key, double increment) {
258         sendCommand(Command.INCRBYFLOAT, key, toByteArray(increment));
259     }
260 
261     void incr(const(ubyte)[] key) {
262         sendCommand(Command.INCR, key);
263     }
264 
265     void append(const(ubyte)[] key, const(ubyte)[] value) {
266         sendCommand(Command.APPEND, key, value);
267     }
268 
269     void substr(const(ubyte)[] key, int start, int end) {
270         sendCommand(Command.SUBSTR, key, toByteArray(start), toByteArray(end));
271     }
272 
273     void hset(const(ubyte)[] key, const(ubyte)[] field, const(ubyte)[] value) {
274         sendCommand(Command.HSET, key, field, value);
275     }
276 
277     void hset(const(ubyte)[] key, Map!(const(ubyte)[], const(ubyte)[]) hash) {
278         const(ubyte)[][] params = new const(ubyte)[][1 + hash.size() * 2];
279 
280         int index = 0;
281         params[index++] = key;
282         foreach (const(ubyte)[] k, const(ubyte)[] value; hash) {
283             params[index++] = k;
284             params[index++] = value;
285         }
286         sendCommand(Command.HSET, params);
287     }
288 
289     void hget(const(ubyte)[] key, const(ubyte)[] field) {
290         sendCommand(Command.HGET, key, field);
291     }
292 
293     void hsetnx(const(ubyte)[] key, const(ubyte)[] field, const(ubyte)[] value) {
294         sendCommand(Command.HSETNX, key, field, value);
295     }
296 
297     void hmset(const(ubyte)[] key, Map!(const(ubyte)[], const(ubyte)[]) hash) {
298         List!(const(ubyte)[]) params = new ArrayList!(const(ubyte)[])();
299         params.add(key);
300 
301         foreach(const(ubyte)[] k, const(ubyte)[] value ; hash) {
302             params.add(k);
303             params.add(value);
304         }
305         sendCommand(Command.HMSET, params.toArray());
306     }
307 
308     void hmget(const(ubyte)[] key, const(ubyte)[][] fields...) {
309         sendCommand(Command.HMGET, joinParameters(key, fields));
310     }
311 
312     void hincrBy(const(ubyte)[] key, const(ubyte)[] field, long value) {
313         sendCommand(Command.HINCRBY, key, field, toByteArray(value));
314     }
315 
316     void hexists(const(ubyte)[] key, const(ubyte)[] field) {
317         sendCommand(Command.HEXISTS, key, field);
318     }
319 
320     void hdel(const(ubyte)[] key, const(ubyte)[][] fields...) {
321         sendCommand(Command.HDEL, joinParameters(key, fields));
322     }
323 
324     void hlen(const(ubyte)[] key) {
325         sendCommand(Command.HLEN, key);
326     }
327 
328     void hkeys(const(ubyte)[] key) {
329         sendCommand(Command.HKEYS, key);
330     }
331 
332     void hvals(const(ubyte)[] key) {
333         sendCommand(Command.HVALS, key);
334     }
335 
336     void hgetAll(const(ubyte)[] key) {
337         sendCommand(Command.HGETALL, key);
338     }
339 
340     void rpush(const(ubyte)[] key, const(ubyte)[][] strings...) {
341         sendCommand(Command.RPUSH, joinParameters(key, strings));
342     }
343 
344     void lpush(const(ubyte)[] key, const(ubyte)[][] strings...) {
345         sendCommand(Command.LPUSH, joinParameters(key, strings));
346     }
347 
348     void llen(const(ubyte)[] key) {
349         sendCommand(Command.LLEN, key);
350     }
351 
352     void lrange(const(ubyte)[] key, long start, long stop) {
353         sendCommand(Command.LRANGE, key, toByteArray(start), toByteArray(stop));
354     }
355 
356     void ltrim(const(ubyte)[] key, long start, long stop) {
357         sendCommand(Command.LTRIM, key, toByteArray(start), toByteArray(stop));
358     }
359 
360     void lindex(const(ubyte)[] key, long index) {
361         sendCommand(Command.LINDEX, key, toByteArray(index));
362     }
363 
364     void lset(const(ubyte)[] key, long index, const(ubyte)[] value) {
365         sendCommand(Command.LSET, key, toByteArray(index), value);
366     }
367 
368     void lrem(const(ubyte)[] key, long count, const(ubyte)[] value) {
369         sendCommand(Command.LREM, key, toByteArray(count), value);
370     }
371 
372     void lpop(const(ubyte)[] key) {
373         sendCommand(Command.LPOP, key);
374     }
375 
376     void rpop(const(ubyte)[] key) {
377         sendCommand(Command.RPOP, key);
378     }
379 
380     void rpoplpush(const(ubyte)[] srckey, const(ubyte)[] dstkey) {
381         sendCommand(Command.RPOPLPUSH, srckey, dstkey);
382     }
383 
384     void sadd(const(ubyte)[] key, const(ubyte)[][] members...) {
385         sendCommand(Command.SADD, joinParameters(key, members));
386     }
387 
388     void smembers(const(ubyte)[] key) {
389         sendCommand(Command.SMEMBERS, key);
390     }
391 
392     void srem(const(ubyte)[] key, const(ubyte)[][] members...) {
393         sendCommand(Command.SREM, joinParameters(key, members));
394     }
395 
396     void spop(const(ubyte)[] key) {
397         sendCommand(Command.SPOP, key);
398     }
399 
400     void spop(const(ubyte)[] key, long count) {
401         sendCommand(Command.SPOP, key, toByteArray(count));
402     }
403 
404     void smove(const(ubyte)[] srckey, const(ubyte)[] dstkey, const(ubyte)[] member) {
405         sendCommand(Command.SMOVE, srckey, dstkey, member);
406     }
407 
408     void scard(const(ubyte)[] key) {
409         sendCommand(Command.SCARD, key);
410     }
411 
412     void sismember(const(ubyte)[] key, const(ubyte)[] member) {
413         sendCommand(Command.SISMEMBER, key, member);
414     }
415 
416     void sinter(const(ubyte)[][] keys...) {
417         sendCommand(Command.SINTER, keys);
418     }
419 
420     void sinterstore(const(ubyte)[] dstkey, const(ubyte)[][] keys...) {
421         sendCommand(Command.SINTERSTORE, joinParameters(dstkey, keys));
422     }
423 
424     void sunion(const(ubyte)[][] keys...) {
425         sendCommand(Command.SUNION, keys);
426     }
427 
428     void sunionstore(const(ubyte)[] dstkey, const(ubyte)[][] keys...) {
429         sendCommand(Command.SUNIONSTORE, joinParameters(dstkey, keys));
430     }
431 
432     void sdiff(const(ubyte)[][] keys...) {
433         sendCommand(Command.SDIFF, keys);
434     }
435 
436     void sdiffstore(const(ubyte)[] dstkey, const(ubyte)[][] keys...) {
437         sendCommand(Command.SDIFFSTORE, joinParameters(dstkey, keys));
438     }
439 
440     void srandmember(const(ubyte)[] key) {
441         sendCommand(Command.SRANDMEMBER, key);
442     }
443 
444     void zadd(const(ubyte)[] key, double score, const(ubyte)[] member) {
445         sendCommand(Command.ZADD, key, toByteArray(score), member);
446     }
447 
448     void zadd(const(ubyte)[] key, double score, const(ubyte)[] member,
449             ZAddParams params) {
450         sendCommand(Command.ZADD, params.getByteParams(key, toByteArray(score), member));
451     }
452 
453     void zadd(const(ubyte)[] key, Map!(const(ubyte)[], double) scoreMembers) {
454         ArrayList!(const(ubyte)[]) args = new ArrayList!(const(ubyte)[])(scoreMembers.size() * 2 + 1);
455         args.add(key);
456         args.addAll(convertScoreMembersToByteArrays(scoreMembers));
457 
458         const(ubyte)[][] argsArray = args.toArray();
459 
460         sendCommand(Command.ZADD, argsArray);
461     }
462 
463     void zadd(const(ubyte)[] key, Map!(const(ubyte)[], double) scoreMembers, ZAddParams params) {
464         ArrayList!(const(ubyte)[]) args = convertScoreMembersToByteArrays(scoreMembers);
465         const(ubyte)[][] argsArray = args.toArray();
466 
467         sendCommand(Command.ZADD, params.getByteParams(key, argsArray));
468     }
469 
470     void zrange(const(ubyte)[] key, long start, long stop) {
471         sendCommand(Command.ZRANGE, key, toByteArray(start), toByteArray(stop));
472     }
473 
474     void zrem(const(ubyte)[] key, const(ubyte)[][] members...) {
475         sendCommand(Command.ZREM, joinParameters(key, members));
476     }
477 
478     void zincrby(const(ubyte)[] key, double increment, const(ubyte)[] member) {
479         sendCommand(Command.ZINCRBY, key, toByteArray(increment), member);
480     }
481 
482     void zincrby(const(ubyte)[] key, double increment, const(ubyte)[] member,
483             ZIncrByParams params) {
484         // Note that it actually calls ZADD with INCR option, so it requires Redis 3.0.2 or upper.
485         sendCommand(Command.ZADD, params.getByteParams(key, toByteArray(increment), member));
486     }
487 
488     void zrank(const(ubyte)[] key, const(ubyte)[] member) {
489         sendCommand(Command.ZRANK, key, member);
490     }
491 
492     void zrevrank(const(ubyte)[] key, const(ubyte)[] member) {
493         sendCommand(Command.ZREVRANK, key, member);
494     }
495 
496     void zrevrange(const(ubyte)[] key, long start, long stop) {
497         sendCommand(Command.ZREVRANGE, key, toByteArray(start), toByteArray(stop));
498     }
499 
500     void zrangeWithScores(const(ubyte)[] key, long start, long stop) {
501         sendCommand(Command.ZRANGE, key, toByteArray(start), toByteArray(stop), cast(const(ubyte)[])to!string(Keyword.WITHSCORES));
502     }
503 
504     void zrevrangeWithScores(const(ubyte)[] key, long start, long stop) {
505         sendCommand(Command.ZREVRANGE, key, toByteArray(start), toByteArray(stop), cast(const(ubyte)[])to!string(Keyword.WITHSCORES));
506     }
507 
508     void zcard(const(ubyte)[] key) {
509         sendCommand(Command.ZCARD, key);
510     }
511 
512     void zscore(const(ubyte)[] key, const(ubyte)[] member) {
513         sendCommand(Command.ZSCORE, key, member);
514     }
515 
516     void multi() {
517         sendCommand(Command.MULTI);
518         _isInMulti = true;
519     }
520 
521     void discard() {
522         sendCommand(Command.DISCARD);
523         _isInMulti = false;
524         _isInWatch = false;
525     }
526 
527     void exec() {
528         sendCommand(Command.EXEC);
529         _isInMulti = false;
530         _isInWatch = false;
531     }
532 
533     void watch(const(ubyte)[][] keys...) {
534         sendCommand(Command.WATCH, keys);
535         _isInWatch = true;
536     }
537 
538     void unwatch() {
539         sendCommand(Command.UNWATCH);
540         _isInWatch = false;
541     }
542 
543     void sort(const(ubyte)[] key) {
544         sendCommand(Command.SORT, key);
545     }
546 
547     void sort(const(ubyte)[] key, SortingParams sortingParameters) {
548         List!(const(ubyte)[]) args = new ArrayList!(const(ubyte)[])();
549         args.add(key);
550         args.addAll(sortingParameters.getParams());
551         sendCommand(Command.SORT, args.toArray());
552     }
553 
554     void blpop(const(ubyte)[][] args) {
555         sendCommand(Command.BLPOP, args);
556     }
557 
558     void blpop(int timeout, const(ubyte)[][] keys...) {
559         List!(const(ubyte)[]) args = new ArrayList!(const(ubyte)[])();
560         foreach(const(ubyte)[] arg ; keys) {
561             args.add(arg);
562         }
563         args.add(Protocol.toByteArray(timeout));
564         blpop(args.toArray());
565     }
566 
567     void sort(const(ubyte)[] key, SortingParams sortingParameters, const(ubyte)[] dstkey) {
568         List!(const(ubyte)[]) args = new ArrayList!(const(ubyte)[])();
569         args.add(key);
570         args.addAll(sortingParameters.getParams());
571         args.add(cast(const(ubyte)[])to!string(Keyword.STORE));
572         args.add(dstkey);
573         sendCommand(Command.SORT, args.toArray());
574     }
575 
576     void sort(const(ubyte)[] key, const(ubyte)[] dstkey) {
577         sendCommand(Command.SORT, key, cast(const(ubyte)[])to!string(Keyword.STORE), dstkey);
578     }
579 
580     void brpop(const(ubyte)[][] args) {
581         sendCommand(Command.BRPOP, args);
582     }
583 
584     void brpop(int timeout, const(ubyte)[][] keys...) {
585         List!(const(ubyte)[]) args = new ArrayList!(const(ubyte)[])();
586         foreach(const(ubyte)[] arg ; keys) {
587             args.add(arg);
588         }
589         args.add(Protocol.toByteArray(timeout));
590         brpop(args.toArray());
591     }
592 
593     void auth(string password) {
594         import std.range;
595         if(password.empty) return;
596         setPassword(password);
597         sendCommand(Command.AUTH, password);
598     }
599 
600     void subscribe(const(ubyte)[][] channels...) {
601         sendCommand(Command.SUBSCRIBE, channels);
602     }
603 
604     void publish(const(ubyte)[] channel, const(ubyte)[] message) {
605         sendCommand(Command.PUBLISH, channel, message);
606     }
607 
608     void unsubscribe() {
609         sendCommand(Command.UNSUBSCRIBE);
610     }
611 
612     void unsubscribe(const(ubyte)[][] channels...) {
613         sendCommand(Command.UNSUBSCRIBE, channels);
614     }
615 
616     void psubscribe(const(ubyte)[][] patterns...) {
617         sendCommand(Command.PSUBSCRIBE, patterns);
618     }
619 
620     void punsubscribe() {
621         sendCommand(Command.PUNSUBSCRIBE);
622     }
623 
624     void punsubscribe(const(ubyte)[][] patterns...) {
625         sendCommand(Command.PUNSUBSCRIBE, patterns);
626     }
627 
628     void pubsub(const(ubyte)[][] args...) {
629         sendCommand(Command.PUBSUB, args);
630     }
631 
632     void zcount(const(ubyte)[] key, double min, double max) {
633         sendCommand(Command.ZCOUNT, key, toByteArray(min), toByteArray(max));
634     }
635 
636     void zcount(const(ubyte)[] key, const(ubyte)[] min, const(ubyte)[] max) {
637         sendCommand(Command.ZCOUNT, key, min, max);
638     }
639 
640     void zrangeByScore(const(ubyte)[] key, double min, double max) {
641         sendCommand(Command.ZRANGEBYSCORE, key, toByteArray(min), toByteArray(max));
642     }
643 
644     void zrangeByScore(const(ubyte)[] key, const(ubyte)[] min, const(ubyte)[] max) {
645         sendCommand(Command.ZRANGEBYSCORE, key, min, max);
646     }
647 
648     void zrevrangeByScore(const(ubyte)[] key, double max, double min) {
649         sendCommand(Command.ZREVRANGEBYSCORE, key, toByteArray(max), toByteArray(min));
650     }
651 
652     void zrevrangeByScore(const(ubyte)[] key, const(ubyte)[] max, const(ubyte)[] min) {
653         sendCommand(Command.ZREVRANGEBYSCORE, key, max, min);
654     }
655 
656     void zrangeByScore(const(ubyte)[] key, double min, double max, int offset,
657             int count) {
658         sendCommand(Command.ZRANGEBYSCORE, key, toByteArray(min), toByteArray(max), cast(const(ubyte)[])to!string(Keyword.LIMIT), toByteArray(offset),
659             toByteArray(count));
660     }
661 
662     void zrevrangeByScore(const(ubyte)[] key, double max, double min,
663             int offset, int count) {
664         sendCommand(Command.ZREVRANGEBYSCORE, key, toByteArray(max), toByteArray(min), cast(const(ubyte)[])to!string(Keyword.LIMIT), toByteArray(offset),
665             toByteArray(count));
666     }
667 
668     void zrangeByScoreWithScores(const(ubyte)[] key, double min, double max) {
669         sendCommand(Command.ZRANGEBYSCORE, key, toByteArray(min), toByteArray(max), cast(const(ubyte)[])to!string(Keyword.WITHSCORES));
670     }
671 
672     void zrevrangeByScoreWithScores(const(ubyte)[] key, double max, double min) {
673         sendCommand(Command.ZREVRANGEBYSCORE, key, toByteArray(max), toByteArray(min), cast(const(ubyte)[])to!string(Keyword.WITHSCORES));
674     }
675 
676     void zrangeByScoreWithScores(const(ubyte)[] key, double min, double max,
677             int offset, int count) {
678         sendCommand(Command.ZRANGEBYSCORE, key, toByteArray(min), toByteArray(max), cast(const(ubyte)[])to!string(Keyword.LIMIT), toByteArray(offset),
679             toByteArray(count), cast(const(ubyte)[])to!string(Keyword.WITHSCORES));
680     }
681 
682     void zrevrangeByScoreWithScores(const(ubyte)[] key, double max, double min,
683             int offset, int count) {
684         sendCommand(Command.ZREVRANGEBYSCORE, key, toByteArray(max), toByteArray(min), cast(const(ubyte)[])to!string(Keyword.LIMIT), toByteArray(offset),
685             toByteArray(count), cast(const(ubyte)[])to!string(Keyword.WITHSCORES));
686     }
687 
688     void zrangeByScore(const(ubyte)[] key, const(ubyte)[] min, const(ubyte)[] max, int offset,
689             int count) {
690         sendCommand(Command.ZRANGEBYSCORE, key, min, max, cast(const(ubyte)[])to!string(Keyword.LIMIT), toByteArray(offset), toByteArray(count));
691     }
692 
693     void zrevrangeByScore(const(ubyte)[] key, const(ubyte)[] max, const(ubyte)[] min,
694             int offset, int count) {
695         sendCommand(Command.ZREVRANGEBYSCORE, key, max, min, cast(const(ubyte)[])to!string(Keyword.LIMIT), toByteArray(offset), toByteArray(count));
696     }
697 
698     void zrangeByScoreWithScores(const(ubyte)[] key, const(ubyte)[] min, const(ubyte)[] max) {
699         sendCommand(Command.ZRANGEBYSCORE, key, min, max, cast(const(ubyte)[])to!string(Keyword.WITHSCORES));
700     }
701 
702     void zrevrangeByScoreWithScores(const(ubyte)[] key, const(ubyte)[] max, const(ubyte)[] min) {
703         sendCommand(Command.ZREVRANGEBYSCORE, key, max, min, cast(const(ubyte)[])to!string(Keyword.WITHSCORES));
704     }
705 
706     void zrangeByScoreWithScores(const(ubyte)[] key, const(ubyte)[] min, const(ubyte)[] max,
707             int offset, int count) {
708         sendCommand(Command.ZRANGEBYSCORE, key, min, max, cast(const(ubyte)[])to!string(Keyword.LIMIT), toByteArray(offset), toByteArray(count),
709             cast(const(ubyte)[])to!string(Keyword.WITHSCORES));
710     }
711 
712     void zrevrangeByScoreWithScores(const(ubyte)[] key, const(ubyte)[] max, const(ubyte)[] min,
713             int offset, int count) {
714         sendCommand(Command.ZREVRANGEBYSCORE, key, max, min, cast(const(ubyte)[])to!string(Keyword.LIMIT), toByteArray(offset),
715             toByteArray(count), cast(const(ubyte)[])to!string(Keyword.WITHSCORES));
716     }
717 
718     void zremrangeByRank(const(ubyte)[] key, long start, long stop) {
719         sendCommand(Command.ZREMRANGEBYRANK, key, toByteArray(start), toByteArray(stop));
720     }
721 
722     void zremrangeByScore(const(ubyte)[] key, double min, double max) {
723         sendCommand(Command.ZREMRANGEBYSCORE, key, toByteArray(min), toByteArray(max));
724     }
725 
726     void zremrangeByScore(const(ubyte)[] key, const(ubyte)[] min, const(ubyte)[] max) {
727         sendCommand(Command.ZREMRANGEBYSCORE, key, min, max);
728     }
729 
730     void zunionstore(const(ubyte)[] dstkey, const(ubyte)[][] sets...) {
731         sendCommand(Command.ZUNIONSTORE, joinParameters(dstkey, toByteArray(sets.length), sets));
732     }
733 
734     void zunionstore(const(ubyte)[] dstkey, ZParams params, const(ubyte)[][] sets...) {
735         List!(const(ubyte)[]) args = new ArrayList!(const(ubyte)[])();
736         args.add(dstkey);
737         args.add(Protocol.toByteArray(sets.length));
738         foreach(const(ubyte)[] set ; sets) {
739             args.add(set);
740         }
741         args.addAll(params.getParams());
742         sendCommand(Command.ZUNIONSTORE, args.toArray());
743     }
744 
745     void zinterstore(const(ubyte)[] dstkey, const(ubyte)[][] sets...) {
746         sendCommand(Command.ZINTERSTORE, joinParameters(dstkey, Protocol.toByteArray(sets.length), sets));
747     }
748 
749     void zinterstore(const(ubyte)[] dstkey, ZParams params, const(ubyte)[][] sets...) {
750         List!(const(ubyte)[]) args = new ArrayList!(const(ubyte)[])();
751         args.add(dstkey);
752         args.add(Protocol.toByteArray(sets.length));
753         foreach(const(ubyte)[] set ; sets) {
754             args.add(set);
755         }
756         args.addAll(params.getParams());
757         sendCommand(Command.ZINTERSTORE, args.toArray());
758     }
759 
760     void zlexcount(const(ubyte)[] key, const(ubyte)[] min, const(ubyte)[] max) {
761         sendCommand(Command.ZLEXCOUNT, key, min, max);
762     }
763 
764     void zrangeByLex(const(ubyte)[] key, const(ubyte)[] min, const(ubyte)[] max) {
765         sendCommand(Command.ZRANGEBYLEX, key, min, max);
766     }
767 
768     void zrangeByLex(const(ubyte)[] key, const(ubyte)[] min, const(ubyte)[] max, int offset,
769             int count) {
770         sendCommand(Command.ZRANGEBYLEX, key, min, max, cast(const(ubyte)[])to!string(Keyword.LIMIT), toByteArray(offset), toByteArray(count));
771     }
772 
773     void zrevrangeByLex(const(ubyte)[] key, const(ubyte)[] max, const(ubyte)[] min) {
774         sendCommand(Command.ZREVRANGEBYLEX, key, max, min);
775     }
776 
777     void zrevrangeByLex(const(ubyte)[] key, const(ubyte)[] max, const(ubyte)[] min,
778             int offset, int count) {
779         sendCommand(Command.ZREVRANGEBYLEX, key, max, min, cast(const(ubyte)[])to!string(Keyword.LIMIT), toByteArray(offset), toByteArray(count));
780     }
781 
782     void zremrangeByLex(const(ubyte)[] key, const(ubyte)[] min, const(ubyte)[] max) {
783         sendCommand(Command.ZREMRANGEBYLEX, key, min, max);
784     }
785 
786     void save() {
787         sendCommand(Command.SAVE);
788     }
789 
790     void bgsave() {
791         sendCommand(Command.BGSAVE);
792     }
793 
794     void bgrewriteaof() {
795         sendCommand(Command.BGREWRITEAOF);
796     }
797 
798     void lastsave() {
799         sendCommand(Command.LASTSAVE);
800     }
801 
802     void shutdown() {
803         sendCommand(Command.SHUTDOWN);
804     }
805 
806     void info() {
807         sendCommand(Command.INFO);
808     }
809 
810     void info(string section) {
811         sendCommand(Command.INFO, section);
812     }
813 
814     void monitor() {
815         sendCommand(Command.MONITOR);
816     }
817 
818     void slaveof(string host, int port) {
819         sendCommand(Command.SLAVEOF, host, port.to!string);
820     }
821 
822     void slaveofNoOne() {
823         sendCommand(Command.SLAVEOF, cast(const(ubyte)[])to!string(Keyword.NO), cast(const(ubyte)[])to!string(Keyword.ONE));
824     }
825 
826     void configGet(const(ubyte)[] pattern) {
827         sendCommand(Command.CONFIG, cast(const(ubyte)[])to!string(Keyword.GET), pattern);
828     }
829 
830     void configSet(const(ubyte)[] parameter, const(ubyte)[] value) {
831         sendCommand(Command.CONFIG, cast(const(ubyte)[])to!string(Keyword.SET), parameter, value);
832     }
833 
834     void strlen(const(ubyte)[] key) {
835         sendCommand(Command.STRLEN, key);
836     }
837 
838     void sync() {
839         sendCommand(Command.SYNC);
840     }
841 
842     void lpushx(const(ubyte)[] key, const(ubyte)[][] strings...) {
843         sendCommand(Command.LPUSHX, joinParameters(key, strings));
844     }
845 
846     void persist(const(ubyte)[] key) {
847         sendCommand(Command.PERSIST, key);
848     }
849 
850     void rpushx(const(ubyte)[] key, const(ubyte)[][] strings...) {
851         sendCommand(Command.RPUSHX, joinParameters(key, strings));
852     }
853 
854     void echo(const(ubyte)[] strings) {
855         sendCommand(Command.ECHO, strings);
856     }
857 
858     void linsert(const(ubyte)[] key, ListPosition where, const(ubyte)[] pivot,
859             const(ubyte)[] value) {
860         sendCommand(Command.LINSERT, key, cast(const(ubyte)[])to!string(where), pivot, value);
861     }
862 
863     // void debug(DebugParams params) {
864     //   sendCommand(Command.DEBUG, params.getCommand());
865     // }
866 
867     void brpoplpush(const(ubyte)[] source, const(ubyte)[] destination, int timeout) {
868         sendCommand(Command.BRPOPLPUSH, source, destination, toByteArray(timeout));
869     }
870 
871     void configResetStat() {
872         sendCommand(Command.CONFIG, cast(const(ubyte)[])to!string(Keyword.RESETSTAT));
873     }
874 
875     void configRewrite() {
876         sendCommand(Command.CONFIG, cast(const(ubyte)[])to!string(Keyword.REWRITE));
877     }
878 
879     void setbit(const(ubyte)[] key, long offset, const(ubyte)[] value) {
880         sendCommand(Command.SETBIT, key, toByteArray(offset), value);
881     }
882 
883     void setbit(const(ubyte)[] key, long offset, bool value) {
884         sendCommand(Command.SETBIT, key, toByteArray(offset), toByteArray(value));
885     }
886 
887     void getbit(const(ubyte)[] key, long offset) {
888         sendCommand(Command.GETBIT, key, toByteArray(offset));
889     }
890 
891     void bitpos(const(ubyte)[] key, bool value, BitPosParams params) {
892         List!(const(ubyte)[]) args = new ArrayList!(const(ubyte)[])();
893         args.add(key);
894         args.add(toByteArray(value));
895         args.addAll(params.getParams());
896         sendCommand(Command.BITPOS, args.toArray());
897     }
898 
899     void setrange(const(ubyte)[] key, long offset, const(ubyte)[] value) {
900         sendCommand(Command.SETRANGE, key, toByteArray(offset), value);
901     }
902 
903     void getrange(const(ubyte)[] key, long startOffset, long endOffset) {
904         sendCommand(Command.GETRANGE, key, toByteArray(startOffset), toByteArray(endOffset));
905     }
906 
907     int getDB() {
908         return db;
909     }
910 
911     // override
912     // void disconnect() {
913     //     db = 0;
914     //     super.disconnect();
915     // }
916 
917     override
918     void close() {
919         db = 0;
920         super.close();
921     }
922 
923     void resetState() {
924         if (isInWatch()) {
925             unwatch();
926             getStatusCodeReply();
927         }
928     }
929 
930     void eval(const(ubyte)[] script, const(ubyte)[] keyCount, const(ubyte)[][] params) {
931         sendCommand(Command.EVAL, joinParameters(script, keyCount, params));
932     }
933 
934     void eval(const(ubyte)[] script, int keyCount, const(ubyte)[][] params...) {
935         sendCommand(Command.EVAL, joinParameters(script, toByteArray(keyCount), params));
936     }
937 
938     void evalsha(const(ubyte)[] sha1, const(ubyte)[] keyCount, const(ubyte)[][] params...) {
939         sendCommand(Command.EVALSHA, joinParameters(sha1, keyCount, params));
940     }
941 
942     void evalsha(const(ubyte)[] sha1, int keyCount, const(ubyte)[][] params...) {
943         sendCommand(Command.EVALSHA, joinParameters(sha1, toByteArray(keyCount), params));
944     }
945 
946     void scriptFlush() {
947         sendCommand(Command.SCRIPT, cast(const(ubyte)[])to!string(Keyword.FLUSH));
948     }
949 
950     void scriptExists(const(ubyte)[][] sha1...) {
951         sendCommand(Command.SCRIPT, joinParameters(cast(const(ubyte)[])to!string(Keyword.EXISTS), sha1));
952     }
953 
954     void scriptLoad(const(ubyte)[] script) {
955         sendCommand(Command.SCRIPT, cast(const(ubyte)[])to!string(Keyword.LOAD), script);
956     }
957 
958     void scriptKill() {
959         sendCommand(Command.SCRIPT, cast(const(ubyte)[])to!string(Keyword.KILL));
960     }
961 
962     void slowlogGet() {
963         sendCommand(Command.SLOWLOG, cast(const(ubyte)[])to!string(Keyword.GET));
964     }
965 
966     void slowlogGet(long entries) {
967         sendCommand(Command.SLOWLOG, cast(const(ubyte)[])to!string(Keyword.GET), toByteArray(entries));
968     }
969 
970     void slowlogReset() {
971         sendCommand(Command.SLOWLOG, cast(const(ubyte)[])to!string(Keyword.RESET));
972     }
973 
974     void slowlogLen() {
975         sendCommand(Command.SLOWLOG, cast(const(ubyte)[])to!string(Keyword.LEN));
976     }
977 
978     void objectRefcount(const(ubyte)[] key) {
979         sendCommand(Command.OBJECT, cast(const(ubyte)[])to!string(Keyword.REFCOUNT), key);
980     }
981 
982     void objectIdletime(const(ubyte)[] key) {
983         sendCommand(Command.OBJECT, cast(const(ubyte)[])to!string(Keyword.IDLETIME), key);
984     }
985 
986     void objectEncoding(const(ubyte)[] key) {
987         sendCommand(Command.OBJECT, cast(const(ubyte)[])to!string(Keyword.ENCODING), key);
988     }
989 
990     void bitcount(const(ubyte)[] key) {
991         sendCommand(Command.BITCOUNT, key);
992     }
993 
994     void bitcount(const(ubyte)[] key, long start, long end) {
995         sendCommand(Command.BITCOUNT, key, toByteArray(start), toByteArray(end));
996     }
997 
998     void bitop(BitOP op, const(ubyte)[] destKey, const(ubyte)[][] srcKeys...) {
999         sendCommand(Command.BITOP, joinParameters(cast(const(ubyte)[])to!string(op), destKey, srcKeys));
1000     }
1001 
1002     void sentinel(const(ubyte)[][] args...) {
1003         sendCommand(Command.SENTINEL, args);
1004     }
1005 
1006     void dump(const(ubyte)[] key) {
1007         sendCommand(Command.DUMP, key);
1008     }
1009 
1010     void restore(const(ubyte)[] key, int ttl, const(ubyte)[] serializedValue) {
1011         sendCommand(Command.RESTORE, key, toByteArray(ttl), serializedValue);
1012     }
1013 
1014     void restoreReplace(const(ubyte)[] key, int ttl, const(ubyte)[] serializedValue) {
1015         sendCommand(Command.RESTORE, key, toByteArray(ttl), serializedValue, cast(const(ubyte)[])to!string(Keyword.REPLACE));
1016     }
1017 
1018     void pexpire(const(ubyte)[] key, long milliseconds) {
1019         sendCommand(Command.PEXPIRE, key, toByteArray(milliseconds));
1020     }
1021 
1022     void pexpireAt(const(ubyte)[] key, long millisecondsTimestamp) {
1023         sendCommand(Command.PEXPIREAT, key, toByteArray(millisecondsTimestamp));
1024     }
1025 
1026     void pttl(const(ubyte)[] key) {
1027         sendCommand(Command.PTTL, key);
1028     }
1029 
1030     void psetex(const(ubyte)[] key, long milliseconds, const(ubyte)[] value) {
1031         sendCommand(Command.PSETEX, key, toByteArray(milliseconds), value);
1032     }
1033 
1034     void srandmember(const(ubyte)[] key, int count) {
1035         sendCommand(Command.SRANDMEMBER, key, toByteArray(count));
1036     }
1037 
1038     void memoryDoctor() {
1039         sendCommand(Command.MEMORY, cast(const(ubyte)[])to!string(Keyword.DOCTOR));
1040     }
1041 
1042     void clientKill(const(ubyte)[] ipPort) {
1043         sendCommand(Command.CLIENT, cast(const(ubyte)[])to!string(Keyword.KILL), ipPort);
1044     }
1045 
1046     void clientKill(string ip, int port) {
1047         sendCommand(Command.CLIENT, Keyword.KILL.to!string(), ip ~ ":" ~ port.to!string);
1048     }
1049 
1050     void clientKill(ClientKillParams params) {
1051         sendCommand(Command.CLIENT, joinParameters(cast(const(ubyte)[])to!string(Keyword.KILL), params.getByteParams()));
1052     }
1053 
1054     void clientGetname() {
1055         sendCommand(Command.CLIENT, cast(const(ubyte)[])to!string(Keyword.GETNAME));
1056     }
1057 
1058     void clientList() {
1059         sendCommand(Command.CLIENT, cast(const(ubyte)[])to!string(Keyword.LIST));
1060     }
1061 
1062     void clientSetname(const(ubyte)[] name) {
1063         sendCommand(Command.CLIENT, cast(const(ubyte)[])to!string(Keyword.SETNAME), name);
1064     }
1065 
1066     void clientPause(long timeout) {
1067         sendCommand(Command.CLIENT, cast(const(ubyte)[])to!string(Keyword.PAUSE), toByteArray(timeout));
1068     }
1069 
1070     void time() {
1071         sendCommand(Command.TIME);
1072     }
1073 
1074     void migrate(string host, int port, const(ubyte)[] key, int destinationDb,
1075             int timeout) {
1076         sendCommand(Command.MIGRATE, SafeEncoder.encode(host), toByteArray(port), key,
1077                 toByteArray(destinationDb), toByteArray(timeout));
1078     }
1079 
1080     void migrate(string host, int port, int destinationDB,
1081             int timeout, MigrateParams params, const(ubyte)[][] keys...) {
1082         const(ubyte)[][] bparams = params.getByteParams();
1083         int len = cast(int)(5 + bparams.length + 1 + keys.length);
1084         const(ubyte)[][] args = new const(ubyte)[][len];
1085         int i = 0;
1086         args[i++] = SafeEncoder.encode(host);
1087         args[i++] = toByteArray(port);
1088         args[i++] = [];
1089         args[i++] = toByteArray(destinationDB);
1090         args[i++] = toByteArray(timeout);
1091         // System.arraycopy(bparams, 0, args, i, bparams.length);
1092         args[i .. i+bparams.length] = bparams[0 .. $];
1093         i += bparams.length;
1094         args[i++] = cast(const(ubyte)[])to!string(Keyword.KEYS);
1095         // System.arraycopy(keys, 0, args, i, keys.length);
1096         args[i .. i+keys.length] = keys[0 .. $];
1097         sendCommand(Command.MIGRATE, args);
1098     }
1099 
1100     void hincrByFloat(const(ubyte)[] key, const(ubyte)[] field, double increment) {
1101         sendCommand(Command.HINCRBYFLOAT, key, field, toByteArray(increment));
1102     }
1103 
1104     void scan(const(ubyte)[] cursor, ScanParams params) {
1105         List!(const(ubyte)[]) args = new ArrayList!(const(ubyte)[])();
1106         args.add(cursor);
1107         args.addAll(params.getParams());
1108         sendCommand(Command.SCAN, args.toArray());
1109     }
1110 
1111     void hscan(const(ubyte)[] key, const(ubyte)[] cursor, ScanParams params) {
1112         List!(const(ubyte)[]) args = new ArrayList!(const(ubyte)[])();
1113         args.add(key);
1114         args.add(cursor);
1115         args.addAll(params.getParams());
1116         sendCommand(Command.HSCAN, args.toArray());
1117     }
1118 
1119     void sscan(const(ubyte)[] key, const(ubyte)[] cursor, ScanParams params) {
1120         List!(const(ubyte)[]) args = new ArrayList!(const(ubyte)[])();
1121         args.add(key);
1122         args.add(cursor);
1123         args.addAll(params.getParams());
1124         sendCommand(Command.SSCAN, args.toArray());
1125     }
1126 
1127     void zscan(const(ubyte)[] key, const(ubyte)[] cursor, ScanParams params) {
1128         List!(const(ubyte)[]) args = new ArrayList!(const(ubyte)[])();
1129         args.add(key);
1130         args.add(cursor);
1131         args.addAll(params.getParams());
1132         sendCommand(Command.ZSCAN, args.toArray());
1133     }
1134 
1135     void waitReplicas(int replicas, long timeout) {
1136         sendCommand(Command.WAIT, toByteArray(replicas), toByteArray(timeout));
1137     }
1138 
1139     void cluster(const(ubyte)[][] args...) {
1140         sendCommand(Command.CLUSTER, args);
1141     }
1142 
1143     void asking() {
1144         sendCommand(Command.ASKING);
1145     }
1146 
1147     void pfadd(const(ubyte)[] key, const(ubyte)[][] elements...) {
1148         sendCommand(Command.PFADD, joinParameters(key, elements));
1149     }
1150 
1151     void pfcount(const(ubyte)[] key) {
1152         sendCommand(Command.PFCOUNT, key);
1153     }
1154 
1155     void pfcount(const(ubyte)[][] keys...) {
1156         sendCommand(Command.PFCOUNT, keys);
1157     }
1158 
1159     void pfmerge(const(ubyte)[] destkey, const(ubyte)[][] sourcekeys...) {
1160         sendCommand(Command.PFMERGE, joinParameters(destkey, sourcekeys));
1161     }
1162 
1163     void readonly() {
1164         sendCommand(Command.READONLY);
1165     }
1166 
1167     void geoadd(const(ubyte)[] key, double longitude, double latitude, const(ubyte)[] member) {
1168         sendCommand(Command.GEOADD, key, toByteArray(longitude), toByteArray(latitude), member);
1169     }
1170 
1171     void geoadd(const(ubyte)[] key, Map!(const(ubyte)[], GeoCoordinate) memberCoordinateMap) {
1172         List!(const(ubyte)[]) args = new ArrayList!(const(ubyte)[])(memberCoordinateMap.size() * 3 + 1);
1173         args.add(key);
1174         args.addAll(convertGeoCoordinateMapToByteArrays(memberCoordinateMap));
1175 
1176         const(ubyte)[][] argsArray = args.toArray();
1177 
1178         sendCommand(Command.GEOADD, argsArray);
1179     }
1180 
1181     void geodist(const(ubyte)[] key, const(ubyte)[] member1, const(ubyte)[] member2) {
1182         sendCommand(Command.GEODIST, key, member1, member2);
1183     }
1184 
1185     void geodist(const(ubyte)[] key, const(ubyte)[] member1, const(ubyte)[] member2, GeoUnit unit) {
1186         sendCommand(Command.GEODIST, key, member1, member2, cast(const(ubyte)[])(unit.toString()));
1187     }
1188 
1189     void geohash(const(ubyte)[] key, const(ubyte)[][] members...) {
1190         sendCommand(Command.GEOHASH, joinParameters(key, members));
1191     }
1192 
1193     void geopos(const(ubyte)[] key, const(ubyte)[][] members) {
1194         sendCommand(Command.GEOPOS, joinParameters(key, members));
1195     }
1196 
1197     void georadius(const(ubyte)[] key, double longitude, double latitude, double radius, GeoUnit unit) {
1198         sendCommand(Command.GEORADIUS, key, toByteArray(longitude), toByteArray(latitude), toByteArray(radius),
1199             cast(const(ubyte)[])(unit.toString()));
1200     }
1201 
1202     void georadiusReadonly(const(ubyte)[] key, double longitude, double latitude, double radius, GeoUnit unit) {
1203         sendCommand(Command.GEORADIUS_RO, key, toByteArray(longitude), toByteArray(latitude), toByteArray(radius),
1204             cast(const(ubyte)[])(unit.toString()));
1205     }
1206 
1207     void georadius(const(ubyte)[] key, double longitude, double latitude, double radius, GeoUnit unit,
1208             GeoRadiusParam param) {
1209         sendCommand(Command.GEORADIUS, param.getByteParams(key, toByteArray(longitude), toByteArray(latitude),
1210             toByteArray(radius), cast(const(ubyte)[])(unit.toString())));
1211     }
1212 
1213     void georadiusReadonly(const(ubyte)[] key, double longitude, double latitude, double radius, GeoUnit unit,
1214             GeoRadiusParam param) {
1215         sendCommand(Command.GEORADIUS_RO, param.getByteParams(key, toByteArray(longitude), toByteArray(latitude),
1216             toByteArray(radius), cast(const(ubyte)[])(unit.toString())));
1217     }
1218 
1219     void georadiusByMember(const(ubyte)[] key, const(ubyte)[] member, double radius, GeoUnit unit) {
1220         sendCommand(Command.GEORADIUSBYMEMBER, key, member, toByteArray(radius), cast(const(ubyte)[])(unit.toString()));
1221     }
1222 
1223     void georadiusByMemberReadonly(const(ubyte)[] key, const(ubyte)[] member, double radius, GeoUnit unit) {
1224         sendCommand(Command.GEORADIUSBYMEMBER_RO, key, member, toByteArray(radius), cast(const(ubyte)[])(unit.toString()));
1225     }
1226 
1227     void georadiusByMember(const(ubyte)[] key, const(ubyte)[] member, double radius, GeoUnit unit,
1228             GeoRadiusParam param) {
1229         sendCommand(Command.GEORADIUSBYMEMBER, param.getByteParams(key, member, toByteArray(radius), cast(const(ubyte)[])(unit.toString())));
1230     }
1231 
1232     void georadiusByMemberReadonly(const(ubyte)[] key, const(ubyte)[] member, double radius, GeoUnit unit,
1233             GeoRadiusParam param) {
1234         sendCommand(Command.GEORADIUSBYMEMBER_RO, param.getByteParams(key, member, toByteArray(radius), cast(const(ubyte)[])(unit.toString())));
1235     }
1236 
1237     void moduleLoad(const(ubyte)[] path) {
1238         sendCommand(Command.MODULE, cast(const(ubyte)[])to!string(Keyword.LOAD), path);
1239     }
1240 
1241     void moduleList() {
1242         sendCommand(Command.MODULE, cast(const(ubyte)[])to!string(Keyword.LIST));
1243     }
1244 
1245     void moduleUnload(const(ubyte)[] name) {
1246         sendCommand(Command.MODULE, cast(const(ubyte)[])to!string(Keyword.UNLOAD), name);
1247     }
1248 
1249     private ArrayList!(const(ubyte)[]) convertScoreMembersToByteArrays(Map!(const(ubyte)[], double) scoreMembers) {
1250         ArrayList!(const(ubyte)[]) args = new ArrayList!(const(ubyte)[])(scoreMembers.size() * 2);
1251 
1252         foreach(const(ubyte)[] key, double value ; scoreMembers) {
1253             args.add(toByteArray(value));
1254             args.add(key);
1255         }
1256 
1257         return args;
1258     }
1259 
1260     private List!(const(ubyte)[]) convertGeoCoordinateMapToByteArrays(
1261             Map!(const(ubyte)[], GeoCoordinate) memberCoordinateMap) {
1262         List!(const(ubyte)[]) args = new ArrayList!(const(ubyte)[])(memberCoordinateMap.size() * 3);
1263 
1264         foreach(const(ubyte)[] key, GeoCoordinate coordinate ; memberCoordinateMap) {
1265             args.add(toByteArray(coordinate.getLongitude()));
1266             args.add(toByteArray(coordinate.getLatitude()));
1267             args.add(key);
1268         }
1269 
1270         return args;
1271     }
1272 
1273     void bitfield(const(ubyte)[] key, const(ubyte)[][] value...) {
1274         sendCommand(Command.BITFIELD, joinParameters(key, value));
1275     }
1276 
1277     void hstrlen(const(ubyte)[] key, const(ubyte)[] field) {
1278         sendCommand(Command.HSTRLEN, key, field);
1279     }
1280     
1281     void xadd(const(ubyte)[] key, const(ubyte)[] id, Map!(const(ubyte)[], const(ubyte)[]) hash, long maxLen, bool approximateLength) {
1282             int maxLexArgs = 0;
1283             if(maxLen < long.max) { // optional arguments
1284                 if(approximateLength) {
1285                     maxLexArgs = 3; // e.g. MAXLEN ~ 1000 
1286                 } else {
1287                     maxLexArgs = 2; // e.g. MAXLEN 1000
1288                 }
1289             }
1290         
1291         const(ubyte)[][] params = new const(ubyte)[][2 + maxLexArgs + hash.size() * 2];
1292         int index = 0;
1293         params[index++] = key;
1294         if(maxLen < long.max) {
1295             params[index++] = cast(const(ubyte)[])to!string(Keyword.MAXLEN);
1296             if(approximateLength) {
1297                 params[index++] = cast(const(ubyte)[])Protocol.BYTES_TILDE;
1298             }
1299             params[index++] = toByteArray(maxLen);
1300         }
1301         
1302         params[index++] = id;
1303         foreach(const(ubyte)[] k, const(ubyte)[] value ; hash) {
1304             params[index++] = k;
1305             params[index++] = value;
1306         }
1307         sendCommand(Command.XADD, params);
1308     }
1309     
1310     void xlen(const(ubyte)[] key) {
1311          sendCommand(Command.XLEN, key);
1312     }
1313     
1314     void xrange(const(ubyte)[] key, const(ubyte)[] start, const(ubyte)[] end, long count) { 
1315          sendCommand(Command.XRANGE, key, start, end, cast(const(ubyte)[])to!string(Keyword.COUNT), toByteArray(count));
1316     }
1317     
1318     void xrevrange(const(ubyte)[] key, const(ubyte)[] end, const(ubyte)[] start, int count) {
1319         sendCommand(Command.XREVRANGE, key, end, start, cast(const(ubyte)[])to!string(Keyword.COUNT), toByteArray(count));
1320     }
1321 
1322     void xread(int count, long block, Map!(const(ubyte)[], const(ubyte)[]) streams) {
1323         const(ubyte)[][] params = new const(ubyte)[][3 + streams.size() * 2 + (block > 0 ? 2 : 0)];
1324 
1325         int streamsIndex = 0;
1326         params[streamsIndex++] = cast(const(ubyte)[])to!string(Keyword.COUNT);
1327         params[streamsIndex++] = toByteArray(count);
1328         if(block > 0) {
1329             params[streamsIndex++] = cast(const(ubyte)[])to!string(Keyword.BLOCK);
1330             params[streamsIndex++] = toByteArray(block);
1331         }
1332         
1333         params[streamsIndex++] = cast(const(ubyte)[])to!string(Keyword.STREAMS);
1334         int idsIndex = streamsIndex + streams.size();
1335 
1336         foreach(const(ubyte)[] key, const(ubyte)[] value; streams) {
1337             params[streamsIndex++] = key;
1338             params[idsIndex++] = value;
1339         }
1340         
1341         sendCommand(Command.XREAD, params);
1342  }
1343     
1344     void xack(const(ubyte)[] key, const(ubyte)[] group, const(ubyte)[][] ids...) {
1345         const(ubyte)[][] params = new const(ubyte)[][2 + ids.length];
1346         int index = 0;
1347         params[index++] = key;
1348         params[index++] = group;
1349         foreach(const(ubyte)[] id ; ids) {
1350             params[index++] = id;
1351         }
1352         sendCommand(Command.XACK, params);
1353     }
1354      
1355     void xgroupCreate(const(ubyte)[] key, const(ubyte)[] groupname, const(ubyte)[] id, bool makeStream) {
1356         if(makeStream) {
1357             sendCommand(Command.XGROUP, cast(const(ubyte)[])to!string(Keyword.CREATE), key, groupname, id, cast(const(ubyte)[])to!string(Keyword.MKSTREAM));  
1358         } else {
1359             sendCommand(Command.XGROUP, cast(const(ubyte)[])to!string(Keyword.CREATE), key, groupname, id);  
1360         }
1361     }
1362 
1363     void xgroupSetID(const(ubyte)[] key, const(ubyte)[] groupname, const(ubyte)[] id) {
1364         sendCommand(Command.XGROUP, cast(const(ubyte)[])to!string(Keyword.SETID), key, groupname, id);    
1365     }
1366 
1367     void xgroupDestroy(const(ubyte)[] key, const(ubyte)[] groupname) {
1368         sendCommand(Command.XGROUP, cast(const(ubyte)[])to!string(Keyword.DESTROY), key, groupname);    
1369     }
1370 
1371     void xgroupDelConsumer(const(ubyte)[] key, const(ubyte)[] groupname, const(ubyte)[] consumerName) {
1372         sendCommand(Command.XGROUP, cast(const(ubyte)[])to!string(Keyword.DELCONSUMER), key, groupname, consumerName);    
1373     }
1374      
1375     void xdel(const(ubyte)[] key, const(ubyte)[][] ids...) {
1376         const(ubyte)[][] params = new const(ubyte)[][1 + ids.length];
1377         int index = 0;
1378         params[index++] = key;
1379         foreach(const(ubyte)[] id ; ids) {
1380             params[index++] = id;
1381         }
1382         sendCommand(Command.XDEL, params);
1383     }
1384     
1385     void xtrim(const(ubyte)[] key, long maxLen, bool approximateLength) {
1386         if(approximateLength) {
1387             sendCommand(Command.XTRIM, key, cast(const(ubyte)[])to!string(Keyword.MAXLEN), Protocol.BYTES_TILDE ,toByteArray(maxLen));
1388         } else {
1389             sendCommand(Command.XTRIM, key, cast(const(ubyte)[])to!string(Keyword.MAXLEN), toByteArray(maxLen));
1390         }
1391     }
1392     
1393     void xreadGroup(const(ubyte)[] groupname, const(ubyte)[] consumer, int count, 
1394                 long block, bool noAck, Map!(const(ubyte)[], const(ubyte)[]) streams) {
1395         
1396         int optional = 0;
1397         if(count>0) {
1398             optional += 2;
1399         }
1400         if(block > 0) {
1401             optional += 2;
1402         }
1403         if(noAck) {
1404             optional += 1;
1405         }
1406         
1407         
1408         const(ubyte)[][] params = new const(ubyte)[][4 + optional + streams.size() * 2];
1409 
1410         int streamsIndex = 0;
1411         params[streamsIndex++] = cast(const(ubyte)[])to!string(Keyword.GROUP);
1412         params[streamsIndex++] = groupname;
1413         params[streamsIndex++] = consumer;
1414         if(count>0) {
1415             params[streamsIndex++] = cast(const(ubyte)[])to!string(Keyword.COUNT);
1416             params[streamsIndex++] = toByteArray(count);
1417         }
1418         if(block > 0) {
1419             params[streamsIndex++] = cast(const(ubyte)[])to!string(Keyword.BLOCK);
1420             params[streamsIndex++] = toByteArray(block);
1421         }
1422         if(noAck) {
1423             params[streamsIndex++] = cast(const(ubyte)[])to!string(Keyword.NOACK);
1424         }
1425         params[streamsIndex++] = cast(const(ubyte)[])to!string(Keyword.STREAMS);
1426         
1427         int idsIndex = streamsIndex + streams.size();
1428         foreach(const(ubyte)[] key, const(ubyte)[] value ; streams) {
1429             params[streamsIndex++] = key;
1430             params[idsIndex++] = value;
1431         }
1432         
1433         sendCommand(Command.XREADGROUP, params);
1434     }
1435 
1436     
1437     void xpending(const(ubyte)[] key, const(ubyte)[] groupname, const(ubyte)[] start, 
1438                                 const(ubyte)[] end, int count, const(ubyte)[] consumername) {
1439         if(consumername is null) {
1440             sendCommand(Command.XPENDING, key, groupname, start, end, toByteArray(count));
1441         } else {
1442             sendCommand(Command.XPENDING, key, groupname, start, end, toByteArray(count), consumername);
1443         }
1444     }
1445 
1446     void xclaim(const(ubyte)[] key, const(ubyte)[] groupname, const(ubyte)[] consumername, long minIdleTime, 
1447                             long newIdleTime, int retries, bool force, const(ubyte)[][] ids) {
1448             
1449             ArrayList!(const(ubyte)[]) arguments = new ArrayList!(const(ubyte)[])(cast(int)(10 + ids.length));
1450 
1451             arguments.add(key);
1452             arguments.add(groupname);
1453             arguments.add(consumername);
1454             arguments.add(toByteArray(minIdleTime));
1455             
1456             foreach(const(ubyte)[] id ; ids) {
1457                 arguments.add(id);  
1458             }
1459             if(newIdleTime > 0) {
1460                 arguments.add(cast(const(ubyte)[])to!string(Keyword.IDLE));
1461                 arguments.add(toByteArray(newIdleTime));
1462             }
1463             if(retries > 0) {
1464                 arguments.add(cast(const(ubyte)[])to!string(Keyword.RETRYCOUNT));
1465                 arguments.add(toByteArray(retries));        
1466             }
1467             if(force) {
1468                 arguments.add(cast(const(ubyte)[])to!string(Keyword.FORCE));        
1469             }
1470             sendCommand(Command.XCLAIM, arguments.toArray());
1471     }
1472 
1473 }