PageRenderTime 134ms CodeModel.GetById 5ms app.highlight 115ms RepoModel.GetById 1ms app.codeStats 1ms

/Server/GameServer/redis/redisclient.h

https://github.com/lovemychobits/c2p_server
C++ Header | 3197 lines | 2552 code | 507 blank | 138 comment | 286 complexity | b3b6e186a284494de02e206ffc45274b MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/* redisclient.h -- a C++ client library for redis.
   2 *
   3 * Copyright (c) 2009, Brian Hammond <brian at fictorial dot com>
   4 * All rights reserved.
   5 *
   6 * Redistribution and use in source and binary forms, with or without
   7 * modification, are permitted provided that the following conditions are met:
   8 *
   9 *   * Redistributions of source code must retain the above copyright notice,
  10 *     this list of conditions and the following disclaimer.
  11 *   * Redistributions in binary form must reproduce the above copyright
  12 *     notice, this list of conditions and the following disclaimer in the
  13 *     documentation and/or other materials provided with the distribution.
  14 *   * Neither the name of Redis nor the names of its contributors may be used
  15 *     to endorse or promote products derived from this software without
  16 *     specific prior written permission.
  17 *
  18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28 * POSSIBILITY OF SUCH DAMAGE.
  29 */
  30
  31#ifndef REDISCLIENT_H
  32#define REDISCLIENT_H
  33
  34#ifndef _MSC_VER
  35#include <errno.h>
  36#include <sys/socket.h>
  37
  38#include <string>
  39#include <vector>
  40#include <map>
  41#include <set>
  42#include <stdexcept>
  43#include <ctime>
  44#include <sstream>
  45
  46#include <boost/concept_check.hpp>
  47#include <boost/lexical_cast.hpp>
  48#include <boost/functional/hash.hpp>
  49#include <boost/foreach.hpp>
  50#include <boost/thread/mutex.hpp>
  51#include <boost/thread/condition_variable.hpp>
  52#include <boost/random.hpp>
  53#include <boost/cstdint.hpp>
  54#include <boost/date_time/posix_time/ptime.hpp>
  55#include <boost/date_time/posix_time/posix_time_types.hpp>
  56#include <boost/optional.hpp>
  57#include <boost/variant.hpp>
  58
  59#include "anet.h"
  60
  61#define REDIS_LBR                       "\r\n"
  62#define REDIS_STATUS_REPLY_OK           "OK"
  63#define REDIS_PREFIX_STATUS_REPLY_ERROR "-ERR "
  64#define REDIS_PREFIX_STATUS_REPLY_ERR_C '-'
  65#define REDIS_PREFIX_STATUS_REPLY_VALUE '+'
  66#define REDIS_PREFIX_SINGLE_BULK_REPLY  '$'
  67#define REDIS_PREFIX_MULTI_BULK_REPLY   '*'
  68#define REDIS_PREFIX_INT_REPLY          ':'
  69#define REDIS_WHITESPACE                " \f\n\r\t\v"
  70
  71template<class Object >
  72struct make;
  73namespace redis 
  74{
  75  template<typename CONSISTENT_HASHER>
  76  class base_client;
  77  
  78  enum reply_t
  79  {
  80    no_reply,
  81    status_code_reply,
  82    error_reply,
  83    int_reply,
  84    bulk_reply,
  85    multi_bulk_reply
  86  };
  87  
  88  struct connection_data
  89  {
  90    connection_data(const std::string & host = "localhost", uint16_t port = 6379, int dbindex = 0)
  91     : host(host), port(port), dbindex(dbindex), socket(ANET_ERR)
  92    {
  93    }
  94
  95    bool operator==(const connection_data & other) const
  96    {
  97      if(host != other.host)
  98        return false;
  99      if(port != other.port)
 100        return false;
 101      if(dbindex != other.dbindex)
 102        return false;
 103
 104      return true;
 105    }
 106    
 107    std::string host;
 108    boost::uint16_t port;
 109    int dbindex;
 110
 111  private:
 112    int socket;
 113
 114    template<typename CONSISTENT_HASHER>
 115    friend class base_client;
 116  };
 117  
 118  enum server_role
 119  {
 120    role_master,
 121    role_slave
 122  };
 123  
 124  // Generic error that is thrown when communicating with the redis server.
 125  
 126  class redis_error : public std::exception
 127  {
 128  public:
 129    redis_error(const std::string & err) : err_(err) {}
 130    virtual ~redis_error() throw () {}
 131    operator const std::string () const { return err_; }
 132    virtual const char* what() const throw ()
 133    {
 134      return err_.c_str();
 135    }
 136    
 137  private:
 138    std::string err_;
 139  };
 140  
 141  // Some socket-level I/O or general connection error.
 142  
 143  class connection_error : public redis_error
 144  {
 145  public:
 146    connection_error(const std::string & err) : redis_error(err) {}
 147  };
 148  
 149  // Redis gave us a reply we were not expecting.
 150  // Possibly an internal error (here or in redis, probably here).
 151  
 152  class protocol_error : public redis_error
 153  {
 154  public:
 155    protocol_error(const std::string & err) : redis_error(err) {};
 156  };
 157  
 158  // A key that you expected to exist does not in fact exist.
 159  
 160  class key_error : public redis_error
 161  {
 162  public:
 163    key_error(const std::string & err) : redis_error(err) {};
 164  };
 165  
 166  // A operation with time limit does not deliver an result early enough.
 167  
 168  class timeout_error : public redis_error
 169  {
 170  public:
 171    timeout_error(const std::string & err) : redis_error(err) {};
 172  };
 173  
 174  // A value of an expected type or other semantics was found to be invalid.
 175  
 176  class value_error : public redis_error
 177  {
 178  public:
 179    value_error(const std::string & err) : redis_error(err) {};
 180  };
 181
 182  struct key
 183  {
 184    explicit key(const std::string & name)
 185    : name(name)
 186    {
 187    }
 188    
 189    std::string name;
 190  };
 191  
 192  class makecmd
 193  {
 194  public:
 195    explicit makecmd(const std::string & cmd_name)
 196    {
 197      append(cmd_name);
 198      //if (!finalize)
 199      //  buffer_ << " ";
 200    }
 201
 202    const std::string & key_name() const
 203    {
 204      if(!key_name_)
 205        throw std::runtime_error("No key defined!");
 206      return *key_name_;
 207    }
 208    
 209    inline makecmd & operator<<(const key & datum)
 210    {
 211      if(key_name_)
 212        throw std::runtime_error("You could not add a second key");
 213      else
 214        key_name_ = datum.name;
 215      append(datum.name);
 216      return *this;
 217    }
 218    
 219    inline makecmd & operator<<(const std::string & datum)
 220    {
 221      append(datum);
 222      return *this;
 223    }
 224    
 225    template <typename T>
 226    makecmd & operator<<(T const & datum)
 227    {
 228      append( boost::lexical_cast<std::string>(datum) );
 229      return *this;
 230    }
 231    
 232    makecmd & operator<<(const std::vector<std::string> & data)
 233    {
 234      lines_.insert( lines_.end(), data.begin(), data.end() );
 235      return *this;
 236    }
 237    
 238    template <typename T>
 239    makecmd & operator<<(const std::vector<T> & data)
 240    {
 241      size_t n = data.size();
 242      for (size_t i = 0; i < n; ++i)
 243      {
 244        append( boost::lexical_cast<std::string>( data[i] ) );
 245        //if (i < n - 1)
 246        //  buffer_ << " ";
 247      }
 248      return *this;
 249    }
 250    
 251    operator std::string () const
 252    {
 253      std::ostringstream oss;
 254      size_t n = lines_.size();
 255      oss << REDIS_PREFIX_MULTI_BULK_REPLY << n << REDIS_LBR;
 256      
 257      for (size_t i = 0; i < n; ++i)
 258      {
 259        const std::string & param = lines_[i];
 260        oss << REDIS_PREFIX_SINGLE_BULK_REPLY << param.size() << REDIS_LBR;
 261        oss << param << REDIS_LBR;
 262      }
 263      
 264      return oss.str();
 265    }
 266    
 267  private:
 268    void append(const std::string & param)
 269    {
 270      lines_.push_back(param);
 271    }
 272    
 273    std::vector<std::string> lines_;
 274    boost::optional<std::string> key_name_;
 275  };
 276
 277  template<typename CONSISTENT_HASHER>
 278  class base_client;
 279
 280  typedef boost::variant< std::string, int, std::vector<std::string> > reply_val_t;
 281  typedef std::pair<reply_t, reply_val_t> reply_data_t;
 282  
 283  class command
 284  {
 285  private:
 286    std::string request_;
 287    std::string hash_key_;
 288    reply_data_t reply_;
 289
 290    void check_reply_t(reply_t reply_type) const
 291    {
 292      if( reply_.first != reply_type )
 293        throw std::runtime_error("invalid reply type");
 294    }
 295
 296    void set_reply(const reply_data_t & reply)
 297    {
 298      reply_ = reply;
 299    }
 300
 301    template<typename CONSISTENT_HASHER>
 302    friend class base_client;
 303    
 304  public:
 305    command( const makecmd & cmd_input )
 306    : request_(cmd_input), hash_key_(cmd_input.key_name())
 307    {
 308      reply_.first = no_reply;
 309    }
 310
 311    reply_t reply_type() const
 312    {
 313      return reply_.first;
 314    }
 315    
 316    const std::string & get_status_code_reply() const
 317    {
 318      check_reply_t(status_code_reply);
 319      return boost::get<std::string>(reply_.second);
 320    }
 321    
 322    const std::string & get_error_reply() const
 323    {
 324      check_reply_t(error_reply);
 325      return boost::get<std::string>(reply_.second);
 326    }
 327    
 328    int get_int_reply() const
 329    {
 330      check_reply_t(int_reply);
 331      return boost::get<int>(reply_.second);
 332    }
 333    
 334    const std::string & get_bulk_reply() const
 335    {
 336      check_reply_t(bulk_reply);
 337      return boost::get<std::string>(reply_.second);
 338    }
 339    
 340    const std::vector<std::string> & get_multi_bulk_reply() const
 341    {
 342      check_reply_t(multi_bulk_reply);
 343      return boost::get< std::vector<std::string> >(reply_.second);
 344    }
 345  };
 346
 347  struct server_info 
 348  {
 349    std::string version;
 350    bool bgsave_in_progress;
 351    unsigned long connected_clients;
 352    unsigned long connected_slaves;
 353    unsigned long used_memory;
 354    unsigned long changes_since_last_save;
 355    unsigned long last_save_time;
 356    unsigned long total_connections_received;
 357    unsigned long total_commands_processed;
 358    unsigned long uptime_in_seconds;
 359    unsigned long uptime_in_days;
 360    server_role role;
 361    unsigned short arch_bits;
 362    std::string multiplexing_api;
 363    std::map<std::string, std::string> param_map;
 364  };
 365  
 366  inline ssize_t recv_or_throw(int fd, void* buf, size_t n, int flags)
 367  {
 368    ssize_t bytes_received;
 369    
 370    do
 371      bytes_received = ::recv(fd, buf, n, flags);
 372    while(bytes_received < static_cast<ssize_t>(0) && errno == EINTR);
 373    
 374    if( bytes_received == static_cast<ssize_t>(0) )
 375      throw connection_error("connection was closed");
 376    
 377    // Handle receive errors. I overlooked it totally, thanks mkx!
 378    if( bytes_received == static_cast<ssize_t>(-1) )
 379      throw connection_error(std::string("recv error: ") + strerror(errno));
 380      
 381    return bytes_received;
 382  }
 383  
 384  // You should construct a 'client' object per connection to a redis-server.
 385  //
 386  // Please read the online redis command reference:
 387  // http://code.google.com/p/redis/wiki/CommandReference
 388  //
 389  // No provisions for customizing the allocator on the string/bulk value type
 390  // (std::string) are provided.  If needed, you can always change the
 391  // string_type typedef in your local version.
 392
 393  template<typename CONSISTENT_HASHER>
 394  class base_client
 395  {
 396  private:
 397    void init(connection_data & con)
 398    {
 399      char err[ANET_ERR_LEN];
 400      con.socket = anetTcpConnect(err, const_cast<char*>(con.host.c_str()), con.port);
 401      if (con.socket == ANET_ERR)
 402      {
 403        std::ostringstream os;
 404        os << err << " (redis://" << con.host << ':' << con.port << ")";
 405        throw connection_error( os.str() );
 406      }
 407      anetTcpNoDelay(NULL, con.socket);
 408      select(con.dbindex, con);
 409    }
 410    
 411  public:
 412    typedef std::string string_type;
 413    typedef std::vector<string_type> string_vector;
 414    typedef std::pair<string_type, string_type> string_pair;
 415    typedef std::vector<string_pair> string_pair_vector;
 416    typedef std::pair<string_type, double> string_score_pair;
 417    typedef std::vector<string_score_pair> string_score_vector;
 418    typedef std::set<string_type> string_set;
 419
 420    typedef long int_type;
 421
 422    explicit base_client(const string_type & host = "localhost",
 423                    uint16_t port = 6379, int_type dbindex = 0)
 424    {
 425      connection_data con;
 426      con.host = host;
 427      con.port = port;
 428      con.dbindex = dbindex;
 429      init(con);
 430      connections_.push_back(con);
 431    }
 432
 433    template<typename CON_ITERATOR>
 434    base_client(CON_ITERATOR begin, CON_ITERATOR end)
 435    {
 436      while(begin != end)
 437      {
 438        connection_data con = *begin;
 439        init(con);
 440        connections_.push_back(con);
 441        begin++;
 442      }
 443
 444      if( connections_.empty() )
 445        throw std::runtime_error("No connections given!");
 446    }
 447
 448    base_client<CONSISTENT_HASHER>* clone() const
 449    {
 450      return new base_client<CONSISTENT_HASHER>(connections_.begin(), connections_.end());
 451    }
 452
 453    inline static string_type missing_value()
 454    {
 455      return "**nonexistent-key**";
 456    }
 457
 458    enum datatype 
 459    {
 460      datatype_none,      // key doesn't exist
 461      datatype_string,
 462      datatype_list,
 463      datatype_set,
 464      datatype_zset,
 465      datatype_hash,
 466      datatype_unknown
 467    };
 468
 469    int_type get_list(const string_type & key, string_vector & out)
 470    {
 471      return lrange(key, 0, -1, out);
 472    }
 473
 474    void lrem_exact(const string_type & key,
 475                    int_type count,
 476                    const string_type & value)
 477    { 
 478      if (lrem(key, count, value) != count)
 479        throw value_error("failed to remove exactly N elements from list");
 480    }
 481    enum range_specifier
 482    {
 483      exclude_min = 1 << 0,
 484      exclude_max = 1 << 1
 485    };
 486
 487    enum aggregate_type
 488    {
 489      aggregate_sum = 1 << 0,
 490      aggregate_min = 1 << 1,
 491      aggregate_max = 1 << 2
 492    };
 493
 494    enum sort_order
 495    {
 496      sort_order_ascending,
 497      sort_order_descending
 498    };
 499    
 500    ~base_client()
 501    {
 502      BOOST_FOREACH(connection_data & con, connections_)
 503      {
 504        if (con.socket != ANET_ERR)
 505          close(con.socket);
 506      }
 507    }
 508
 509    const std::vector<connection_data> & connections() const
 510    {
 511      return connections_;
 512    }
 513    
 514    void auth(const string_type & pass)
 515    {
 516      if( connections_.size() > 1 )
 517        throw std::runtime_error("feature is not available in cluster mode");
 518
 519      int socket = connections_[0].socket;
 520      send_(socket, makecmd("AUTH") << pass);
 521      recv_ok_reply_(socket);
 522    }
 523    
 524    void set(const string_type & key,
 525                          const string_type & value)
 526    {
 527      int socket = get_socket(key);
 528      send_(socket, makecmd("SET") << key << value);
 529      recv_ok_reply_(socket);
 530    }
 531    
 532    void mset( const string_vector & keys, const string_vector & values )
 533    {
 534      assert( keys.size() == values.size() );
 535
 536      std::map< int, boost::optional<makecmd> > socket_commands;
 537
 538      for(size_t i=0; i < keys.size(); i++)
 539      {
 540        int socket = get_socket(keys);
 541        boost::optional<makecmd> & cmd = socket_commands[socket];
 542        if(!cmd)
 543          cmd = makecmd("MSET");
 544        *cmd << keys[i] << values[i];
 545      }
 546
 547      typedef std::pair< int, boost::optional<makecmd> > sock_pair;
 548      BOOST_FOREACH(const sock_pair & sp, socket_commands)
 549      {
 550        send_(sp.first, *sp.second);
 551      }
 552      
 553      BOOST_FOREACH(const sock_pair & sp, socket_commands)
 554      {
 555        recv_ok_reply_(sp.first);
 556      }
 557    }
 558    
 559    void mset( const string_pair_vector & key_value_pairs )
 560    {
 561      std::map< int, boost::optional<makecmd> > socket_commands;
 562      
 563      for(size_t i=0; i < key_value_pairs.size(); i++)
 564      {
 565        const string_type & key = key_value_pairs[i].first;
 566        const string_type & value = key_value_pairs[i].second;
 567        
 568        int socket = get_socket(key);
 569        boost::optional<makecmd> & cmd = socket_commands[socket];
 570        if(!cmd)
 571          cmd = makecmd("MSET");
 572        *cmd << key << value;
 573      }
 574      
 575      typedef std::pair< int, boost::optional<makecmd> > sock_pair;
 576      BOOST_FOREACH(const sock_pair & sp, socket_commands)
 577      {
 578        send_(sp.first, *sp.second);
 579      }
 580
 581      BOOST_FOREACH(const sock_pair & sp, socket_commands)
 582      {
 583        recv_ok_reply_(sp.first);
 584      }
 585    }
 586
 587  private:
 588    struct msetex_data
 589    {
 590      boost::optional<makecmd> mset_cmd;
 591      std::string expire_cmds;
 592      size_t count;
 593    };
 594
 595  public:
 596    void msetex( const string_pair_vector & key_value_pairs, int_type seconds )
 597    {
 598      std::map< int, msetex_data > socket_commands;
 599      
 600      for(size_t i=0; i < key_value_pairs.size(); i++)
 601      {
 602        const string_type & key = key_value_pairs[i].first;
 603        const string_type & value = key_value_pairs[i].second;
 604        
 605        int socket = get_socket(key);
 606        msetex_data & dat = socket_commands[socket];
 607        boost::optional<makecmd> & cmd = dat.mset_cmd;
 608        if(!cmd)
 609        {
 610          cmd = makecmd("MSET");
 611          dat.count = 0;
 612        }
 613        *cmd << key << value;
 614
 615        std::string & expire_cmds = dat.expire_cmds;
 616        expire_cmds += makecmd("EXPIRE") << key << seconds;
 617        dat.count++;
 618      }
 619      
 620      typedef std::pair< int, msetex_data > sock_pair;
 621      BOOST_FOREACH(const sock_pair & sp, socket_commands)
 622      {
 623        std::string cmds = *sp.second.mset_cmd;
 624        cmds += sp.second.expire_cmds;
 625        send_(sp.first, cmds);
 626      }
 627      
 628      BOOST_FOREACH(const sock_pair & sp, socket_commands)
 629      {
 630        recv_ok_reply_(sp.first);
 631        for(size_t i= 0; i < sp.second.count; i++)
 632          recv_int_ok_reply_(sp.first);
 633        
 634      }
 635    }
 636    
 637    string_type get(const string_type & key)
 638    {
 639      int socket = get_socket(key);
 640      send_(socket, makecmd("GET") << key);
 641      return recv_bulk_reply_(socket);
 642    }
 643    
 644    string_type getset(const string_type & key, const string_type & value)
 645    {
 646      int socket = get_socket(key);
 647      send_(socket, makecmd("GETSET") << key << value);
 648      return recv_bulk_reply_(socket);
 649    }
 650
 651  private:
 652    struct connection_keys
 653    {
 654      boost::optional<makecmd> cmd;
 655
 656      /// Gives the position of the value in the original array
 657      std::vector<size_t> indices;
 658    };
 659
 660  public:
 661    void exec(command & cmd)
 662    {
 663      int socket = get_socket(cmd.hash_key_);
 664      send_( socket, cmd.request_ );
 665      cmd.set_reply( recv_generic_reply_(socket) );
 666    }
 667
 668    void exec(std::vector<command> & commands)
 669    {
 670      std::map< int, std::string > socket_commands;
 671      
 672      for(size_t i=0; i < commands.size(); i++)
 673      {
 674        int socket = get_socket( commands[i].hash_key_ );
 675        socket_commands[socket] += commands[i].request_;
 676      }
 677      
 678      typedef std::pair< int, std::string > sock_pair;
 679      BOOST_FOREACH(const sock_pair & sp, socket_commands)
 680      {
 681        send_(sp.first, sp.second);
 682      }
 683      
 684      for(size_t i=0; i < commands.size(); i++)
 685      {
 686        int socket = get_socket( commands[i].hash_key_ );
 687        commands[i].set_reply( recv_generic_reply_(socket) );
 688      }
 689    }
 690    
 691    void exec_transaction(std::vector<command> & commands)
 692    {
 693      int cmd_socket = -1;
 694      std::string cmd_str = makecmd("MULTI");
 695      
 696      for(size_t i=0; i < commands.size(); i++)
 697      {
 698        int socket = get_socket( commands[i].hash_key_ );
 699        if( cmd_socket == -1 )
 700          cmd_socket = socket;
 701        else if( cmd_socket != socket )
 702          throw std::runtime_error("calls in transaction map to different server!");
 703           
 704        cmd_str += commands[i].request_;
 705      }
 706
 707      cmd_str += makecmd("EXEC");
 708      
 709      send_(cmd_socket, cmd_str);
 710      recv_ok_reply_(cmd_socket); // MULTI => +OK
 711      for(size_t i=0; i < commands.size(); i++)
 712      {
 713        std::string resp = recv_single_line_reply_(cmd_socket);
 714        if( resp != "QUEUED" )
 715          throw std::runtime_error("invalid state (expected 'QUEUED' in transaction but got '" + resp + "')");
 716      }
 717      if( read_line(cmd_socket)[0] != REDIS_PREFIX_MULTI_BULK_REPLY )
 718        throw std::runtime_error("EXEC does not return a multi bulk reply");
 719        
 720      for(size_t i=0; i < commands.size(); i++)
 721      {
 722        commands[i].set_reply( recv_generic_reply_(cmd_socket) );
 723      }
 724    }
 725    
 726    void mget(const string_vector & keys, string_vector & out)
 727    {
 728      out = string_vector( keys.size() );
 729      std::map< int, connection_keys > socket_commands;
 730      
 731      for(size_t i=0; i < keys.size(); i++)
 732      {
 733        int socket = get_socket(keys[i]);
 734        connection_keys & con_keys = socket_commands[socket];
 735        boost::optional<makecmd> & cmd = con_keys.cmd;
 736        if(!cmd)
 737          cmd = makecmd("MGET");
 738        *cmd << keys[i];
 739        con_keys.indices.push_back(i);
 740      }
 741      
 742      typedef std::pair< int, connection_keys > sock_pair;
 743      BOOST_FOREACH(const sock_pair & sp, socket_commands)
 744      {
 745        send_(sp.first, *sp.second.cmd);
 746      }
 747      
 748      BOOST_FOREACH(const sock_pair & sp, socket_commands)
 749      {
 750        const connection_keys & con_keys = sp.second;
 751        string_vector cur_out;
 752        recv_multi_bulk_reply_(sp.first, cur_out);
 753        
 754        for(size_t i=0; i < cur_out.size(); i++)
 755          out[con_keys.indices[i]] = cur_out[i];
 756      }
 757    }
 758    
 759    bool setnx(const string_type & key,
 760                            const string_type & value)
 761    {
 762      int socket = get_socket(key);
 763      send_(socket, makecmd("SETNX") << key << value);
 764      return recv_int_reply_(socket) == 1;
 765    }
 766    
 767    bool msetnx( const string_vector & keys, const string_vector & values )
 768    {
 769      assert( keys.size() == values.size() );
 770      
 771      std::map< int, boost::optional<makecmd> > socket_commands;
 772      
 773      for(size_t i=0; i < keys.size(); i++)
 774      {
 775        int socket = get_socket(keys);
 776        boost::optional<makecmd> & cmd = socket_commands[socket];
 777        if(!cmd)
 778          cmd = makecmd("MSETNX");
 779        *cmd << keys[i] << values[i];
 780      }
 781
 782      if( socket_commands.size() > 1 )
 783        throw std::runtime_error("feature is not available in cluster mode");
 784      
 785      typedef std::pair< int, boost::optional<makecmd> > sock_pair;
 786      BOOST_FOREACH(const sock_pair & sp, socket_commands)
 787	  {
 788		  send_(sp.first, *sp.second);
 789		  recv_ok_reply_(sp.first);
 790	  }
 791    }
 792    
 793    bool msetnx( const string_pair_vector & key_value_pairs )
 794    {
 795      std::map< int, boost::optional<makecmd> > socket_commands;
 796      
 797      for(size_t i=0; i < key_value_pairs.size(); i++)
 798      {
 799        int socket = get_socket(keys);
 800        boost::optional<makecmd> & cmd = socket_commands[socket];
 801        if(!cmd)
 802          cmd = makecmd("MSETNX");
 803        *cmd << key_value_pairs[i].first << key_value_pairs[i].second;
 804      }
 805      
 806	  if( socket_commands.size() > 1 )
 807		  throw std::runtime_error("feature is not available in cluster mode");
 808
 809	  typedef std::pair< int, boost::optional<makecmd> > sock_pair;
 810	  BOOST_FOREACH(const sock_pair & sp, socket_commands)
 811      {
 812        send_(sp.first, *sp.second);
 813        recv_ok_reply_(sp.first);
 814      }
 815    }
 816    
 817    void setex(const string_type & key, const string_type & value, unsigned int secs)
 818    {
 819      int socket = get_socket(key);
 820      send_(socket, makecmd("SETEX") << key << secs << value);
 821      recv_ok_reply_(socket);
 822    }
 823    
 824    size_t append(const string_type & key, const string_type & value)
 825    {
 826      int socket = get_socket(key);
 827      send_(socket, makecmd("APPEND") << key << value);
 828      int res = recv_int_reply_(socket);
 829      if(res < 0)
 830        throw protocol_error("expected value size");
 831      
 832      assert( static_cast<size_t>(res) >= value.size() );
 833      return static_cast<size_t>(res);
 834    }
 835    
 836    string_type substr(const string_type & key, int start, int end)
 837    {
 838      int socket = get_socket(key);
 839      send_(socket, makecmd("SUBSTR") << key << start << end);
 840      return recv_bulk_reply_(socket);
 841    }
 842    
 843    int_type incr(const string_type & key)
 844    {
 845      int socket = get_socket(key);
 846      send_(socket, makecmd("INCR") << key);
 847      return recv_int_reply_(socket);
 848    }
 849
 850    template<typename INT_TYPE>
 851    INT_TYPE incr(const string_type & key)
 852    {
 853      int socket = get_socket(key);
 854      send_(socket, makecmd("INCR") << key);
 855      return recv_int_reply_<INT_TYPE>(socket);
 856    }
 857    
 858    int_type incrby(const string_type & key, int_type by)
 859    {
 860      int socket = get_socket(key);
 861      send_(socket, makecmd("INCRBY") << key << by);
 862      return recv_int_reply_(socket);
 863    }
 864    
 865    template<typename INT_TYPE>
 866    INT_TYPE incrby(const string_type & key, INT_TYPE by)
 867    {
 868      int socket = get_socket(key);
 869      send_(socket, makecmd("INCRBY") << key << by);
 870      return recv_int_reply_<INT_TYPE>(socket);
 871    }
 872    
 873    int_type decr(const string_type & key)
 874    {
 875      int socket = get_socket(key);
 876      send_(socket, makecmd("DECR") << key);
 877      return recv_int_reply_(socket);
 878    }
 879    
 880    template<typename INT_TYPE>
 881    INT_TYPE decr(const string_type & key)
 882    {
 883      int socket = get_socket(key);
 884      send_(socket, makecmd("DECR") << key);
 885      return recv_int_reply_<INT_TYPE>(socket);
 886    }
 887    
 888    int_type decrby(const string_type & key, int_type by)
 889    {
 890      int socket = get_socket(key);
 891      send_(socket, makecmd("DECRBY") << key << by);
 892      return recv_int_reply_(socket);
 893    }
 894    
 895    template<typename INT_TYPE>
 896    INT_TYPE decrby(const string_type & key, INT_TYPE by)
 897    {
 898      int socket = get_socket(key);
 899      send_(socket, makecmd("DECRBY") << key << by);
 900      return recv_int_reply_<INT_TYPE>(socket);
 901    }
 902    
 903    bool exists(const string_type & key)
 904    {
 905      int socket = get_socket(key);
 906      send_(socket, makecmd("EXISTS") << key);
 907      return recv_int_reply_(socket) == 1;
 908    }
 909    
 910    bool del(const string_type & key)
 911    {
 912      int socket = get_socket(key);
 913      send_(socket, makecmd("DEL") << key);
 914      return recv_int_reply_(socket) != 0;
 915    }
 916
 917    template<typename ITERATOR>
 918    bool del(ITERATOR begin, ITERATOR end)
 919    {
 920      std::map<int, string_vector> sock_key_map;
 921      while( begin != end )
 922      {
 923        string_type key = *begin++;
 924        sock_key_map[ get_socket(key) ].push_back(key);
 925      }
 926
 927      typedef std::pair<const int, string_vector> sock_key_pair;
 928      BOOST_FOREACH(const sock_key_pair & p, sock_key_map)
 929      {
 930        send_(p.first, makecmd("DEL") << p.second);
 931      }
 932
 933      int_type res =  false;
 934
 935      BOOST_FOREACH(const sock_key_pair & p, sock_key_map)
 936      {
 937        res += recv_int_reply_(p.first);
 938      }
 939
 940      return res;
 941    }
 942    
 943    datatype type(const string_type & key)
 944    {
 945      int socket = get_socket(key);
 946      send_(socket, makecmd("TYPE") << key);
 947      std::string response = recv_single_line_reply_(socket);
 948      
 949      if(response == "none")   return datatype_none;
 950      if(response == "string") return datatype_string;
 951      if(response == "list")   return datatype_list;
 952      if(response == "set")    return datatype_set;
 953      if(response == "zset")   return datatype_zset;
 954      if(response == "hash")   return datatype_hash;
 955
 956#ifndef NDEBUG
 957      std::cerr << "Got unknown datatype name: " << response << std::endl;
 958#endif // NDEBUG
 959      
 960      return datatype_unknown;
 961    }
 962    
 963    int_type keys(const string_type & pattern, string_vector & out)
 964    {
 965      BOOST_FOREACH(const connection_data & con, connections_)
 966      {
 967        send_(con.socket, makecmd("KEYS") << pattern);
 968      }
 969
 970      int_type res = 0;
 971      
 972      BOOST_FOREACH(const connection_data & con, connections_)
 973      {
 974        res += recv_multi_bulk_reply_(con.socket, out);
 975      }
 976      
 977      return res;
 978    }
 979    
 980    string_type randomkey()
 981    {      
 982      int socket = connections_[0].socket;
 983      if( connections_.size() > 1 )
 984      {
 985        /*
 986         * Select a random server if there are more then one
 987         */
 988        
 989        boost::mt19937 gen;
 990        boost::posix_time::ptime now = boost::posix_time::microsec_clock::local_time();
 991        boost::posix_time::ptime epoch( boost::gregorian::date(1970, 1, 1) );
 992        gen.seed( (now-epoch).total_seconds() );
 993        
 994        boost::uniform_int<> dist(0, connections_.size()-1);
 995        boost::variate_generator< boost::mt19937&, boost::uniform_int<> > die(gen, dist);
 996        socket = connections_[die()].socket;
 997      }
 998      
 999      send_(socket, makecmd("RANDOMKEY") );
1000      return recv_bulk_reply_(socket);
1001    }
1002    
1003    /**
1004     * @warning Not cluster save (the old name and the new one must be on the same redis server)
1005     */
1006    void rename(const string_type & old_name, const string_type & new_name)
1007    {
1008      int source_socket = get_socket(old_name);
1009      int destin_socket = get_socket(new_name);
1010      if( source_socket != destin_socket )
1011      {
1012        switch( type(old_name) )
1013        {
1014          case datatype_none:      // key doesn't exist
1015            return;
1016          case datatype_string:
1017            set(new_name, get(old_name));
1018            break;
1019          case datatype_list:
1020          {
1021			  string_vector content;
1022			  lrange(old_name, 0, -1, content);
1023			  del(new_name);
1024			  BOOST_FOREACH(const string_type & val, content)
1025			  {
1026				  rpush(new_name, val);
1027			  }
1028            break;
1029          }
1030          case datatype_set:
1031          {
1032            string_set content;
1033            smembers(old_name, content);
1034            del(new_name);
1035            BOOST_FOREACH(const string_type & val, content)
1036            {
1037              sadd(new_name, val);
1038            }
1039            break;
1040          }
1041          case datatype_zset:
1042          case datatype_hash:
1043          case datatype_unknown:
1044          default:
1045            throw std::runtime_error("renaming is not supported for this datatype in cluster mode");
1046        }
1047        del(old_name);
1048        return;
1049      }
1050
1051      send_(source_socket, makecmd("RENAME") << old_name << new_name);
1052      recv_ok_reply_(source_socket);
1053    }
1054    
1055    /**
1056     * @warning Not cluster save (the old name and the new one must be on the same redis server)
1057     */
1058    bool renamenx(const string_type & old_name, const string_type & new_name)
1059    {
1060      int source_socket = get_socket(old_name);
1061      int destin_socket = get_socket(new_name);
1062      
1063      if( source_socket != destin_socket )
1064      {
1065        if( exists(new_name) )
1066          return false;
1067        rename(old_name, new_name);
1068        return true;
1069      }
1070      
1071      send_(source_socket, makecmd("RENAMENX") << old_name << new_name);
1072      return recv_int_reply_(source_socket) == 1;
1073    }
1074
1075    /**
1076     * @returns the number of keys in the currently selected database. In cluster mode the number
1077     * of keys in all currently selected databases is returned.
1078     */
1079    int_type dbsize()
1080    {
1081      int_type val = 0;
1082      
1083      BOOST_FOREACH(const connection_data & con, connections_)
1084      {
1085        send_(con.socket, makecmd("DBSIZE"));
1086      }
1087      
1088      BOOST_FOREACH(const connection_data & con, connections_)
1089      {
1090        val += recv_int_reply_(con.socket);
1091      }
1092      
1093      return val;
1094    }
1095
1096    /**
1097     * @returns the number of keys in the currently selected database with the given connection.
1098     */
1099    int_type dbsize(const connection_data & con)
1100    {
1101      send_(con.socket, makecmd("DBSIZE"));
1102      return recv_int_reply_(con.socket);
1103    }
1104    
1105    void expire(const string_type & key, unsigned int secs)
1106    {
1107      int socket = get_socket(key);
1108      send_(socket, makecmd("EXPIRE") << key << secs);
1109      recv_int_ok_reply_(socket);
1110    }
1111    
1112    int ttl(const string_type & key)
1113    {
1114      int socket = get_socket(key);
1115      send_(socket, makecmd("TTL") << key);
1116      return recv_int_reply_(socket);
1117    }
1118    
1119    int_type rpush(const string_type & key,
1120                            const string_type & value)
1121    {
1122      int socket = get_socket(key);
1123      send_(socket, makecmd("RPUSH") << key << value);
1124      return recv_int_reply_(socket);
1125    }
1126    
1127    int_type lpush(const string_type & key,
1128                            const string_type & value)
1129    {
1130      int socket = get_socket(key);
1131      send_(socket, makecmd("LPUSH") << key << value);
1132      return recv_int_reply_(socket);
1133    }
1134    
1135    int_type llen(const string_type & key)
1136    {
1137      int socket = get_socket(key);
1138      send_(socket, makecmd("LLEN") << key);
1139      return recv_int_reply_(socket);
1140    }
1141    
1142    int_type lrange(const string_type & key,
1143                    int_type start,
1144                    int_type end,
1145                    string_vector & out)
1146    {
1147      int socket = get_socket(key);
1148      send_(socket, makecmd("LRANGE") << key << start << end);
1149      return recv_multi_bulk_reply_(socket, out);
1150    }
1151    
1152    void ltrim(const string_type & key,
1153                            int_type start,
1154                            int_type end)
1155    {
1156      int socket = get_socket(key);
1157      send_(socket, makecmd("LTRIM") << key << start << end);
1158      recv_ok_reply_(socket);
1159    }
1160    
1161    string_type lindex(const string_type & key,
1162                                                 int_type index)
1163    {
1164      int socket = get_socket(key);
1165      send_(socket, makecmd("LINDEX") << key << index);
1166      return recv_bulk_reply_(socket);
1167    }
1168    
1169    void lset(const string_type & key, int_type index, const string_type & value)
1170    {
1171      int socket = get_socket(key);
1172      send_(socket, makecmd("LSET") << key << index << value);
1173      recv_ok_reply_(socket);
1174    }
1175    
1176    int_type lrem(const string_type & key, int_type count, const string_type & value)
1177    {
1178      int socket = get_socket(key);
1179      send_(socket, makecmd("LREM") << key << count << value);
1180      return recv_int_reply_(socket);
1181    }
1182    
1183    string_type lpop(const string_type & key)
1184    {
1185      int socket = get_socket(key);
1186      send_(socket, makecmd("LPOP") << key);
1187      return recv_bulk_reply_(socket);
1188    }
1189    
1190    string_type rpop(const string_type & key)
1191    {
1192      int socket = get_socket(key);
1193      send_(socket, makecmd("RPOP") << key);
1194      return recv_bulk_reply_(socket);
1195    }
1196
1197    /**
1198     * @warning Not cluster save (all keys must be on the same redis server)
1199     */
1200    string_pair blpop(const string_vector & keys, int_type timeout_seconds = 0)
1201    {
1202      int socket = get_socket(keys);
1203      if(socket == -1)
1204        // How to do in cluster mode? Is reinserting of to much poped values a solution?
1205        throw std::runtime_error("feature is not available in cluster mode");
1206      
1207      send_(socket, makecmd("BLPOP") << keys << timeout_seconds);
1208      string_vector sv;
1209      try
1210      {
1211        recv_multi_bulk_reply_(socket, sv);
1212      }
1213      catch(key_error & e)
1214      {
1215        assert(timeout_seconds > 0);
1216        throw timeout_error("could not pop value in time");
1217      }
1218      if(sv.size() == 2)
1219        return make_pair( sv[0], sv[1] );
1220      else
1221        return make_pair( "", missing_value() );
1222    }
1223
1224    string_type blpop(const string_type & key, int_type timeout_seconds = 0)
1225    {
1226      int socket = get_socket(key);
1227      if(socket == -1)
1228        // How to do in cluster mode? Is reinserting of to much poped values a solution?
1229        throw std::runtime_error("feature is not available in cluster mode");
1230      
1231      send_(socket, makecmd("BLPOP") << key << timeout_seconds);
1232      string_vector sv;
1233      try
1234      {
1235        recv_multi_bulk_reply_(socket, sv);
1236      }
1237      catch(key_error & e)
1238      {
1239        assert(timeout_seconds > 0);
1240        return missing_value(); // should we throw a timeout_error?
1241                                // we set a timeout so we expect that this can happen
1242      }
1243      if(sv.size() == 2)
1244      {
1245        assert(key == sv[0]);
1246        return sv[1];
1247      }
1248      else
1249        return missing_value();
1250    }
1251    
1252    /**
1253     * @warning Not cluster save (all keys must be on the same redis server)
1254     */
1255    string_pair brpop(const string_vector & keys, int_type timeout_seconds)
1256    {
1257      int socket = get_socket(keys);
1258      makecmd m("BRPOP");
1259      for(size_t i=0; i < keys.size(); i++)
1260        m << keys[i];
1261      m << timeout_seconds;
1262      send_(socket, m);
1263      string_vector sv;
1264      try
1265      {
1266        recv_multi_bulk_reply_(socket, sv);
1267      }
1268      catch(key_error & e)
1269      {
1270        assert(timeout_seconds > 0);
1271        return missing_value(); // should we throw a timeout_error?
1272                                // we set a timeout so we expect that this can happen
1273      }
1274      if(sv.size() == 2)
1275        return make_pair( sv[0], sv[1] );
1276      else
1277        return make_pair( "", missing_value );
1278    }
1279    
1280    string_type brpop(const string_type & key, int_type timeout_seconds)
1281    {
1282      int socket = get_socket(key);
1283      send_(socket, makecmd("BRPOP") << key << timeout_seconds);
1284      string_vector sv;
1285      try
1286      {
1287        recv_multi_bulk_reply_(socket, sv);
1288      }
1289      catch(key_error & e)
1290      {
1291        assert(timeout_seconds > 0);
1292        return missing_value(); // should we throw a timeout_error?
1293                                // we set a timeout so we expect that this can happen
1294      }
1295      if(sv.size() == 2)
1296      {
1297        assert(key == sv[0]);
1298        return sv[1];
1299      }
1300      else
1301        return missing_value();
1302    }
1303    
1304    bool sadd(const string_type & key,
1305                           const string_type & value)
1306    {
1307      int socket = get_socket(key);
1308      send_(socket, makecmd("SADD") << key << value);
1309      return recv_int_reply_(socket) == 1;
1310    }
1311
1312    template<typename ITERATOR>
1313    int_type sadd(const string_type & key, ITERATOR begin, ITERATOR end)
1314    {
1315      int socket = get_socket(key);
1316      std::string commands;
1317      size_t count = 0;
1318      
1319      while(begin != end)
1320      {
1321        string_type val = *begin++;
1322        commands += makecmd("SADD") << key << val;
1323        count++;
1324      }
1325      
1326      send_(socket, commands);
1327
1328      int_type res = 0;
1329      for(size_t i=0; i < count; i++)
1330      {
1331        res += recv_int_reply_(socket);
1332      }
1333      return res;
1334    }
1335    
1336    void srem(const string_type & key,
1337                           const string_type & value)
1338    {
1339      int socket = get_socket(key);
1340      send_(socket, makecmd("SREM") << key << value);
1341      recv_int_ok_reply_(socket);
1342    }
1343    
1344    string_type spop(const string_type & key)
1345    {
1346      int socket = get_socket(key);
1347      send_(socket, makecmd("SPOP") << key);
1348      return recv_bulk_reply_(socket);
1349    }
1350    
1351    void smove(const string_type & srckey, const string_type & dstkey, const string_type & member)
1352    {
1353      int src_socket = get_socket(srckey);
1354      int dst_socket = get_socket(dstkey);
1355      if(dst_socket != src_socket)
1356      {
1357        srem(srckey, member);
1358        sadd(dstkey, member);
1359        return;
1360      }
1361        
1362      send_(src_socket, makecmd("SMOVE") << srckey << dstkey << member);
1363      recv_int_ok_reply_(src_socket);
1364    }
1365    
1366    int_type scard(const string_type & key)
1367    {
1368      int socket = get_socket(key);
1369      send_(socket, makecmd("SCARD") << key);
1370      return recv_int_reply_(socket);
1371    }
1372    
1373    bool sismember(const string_type & key,
1374                                const string_type & value)
1375    {
1376      int socket = get_socket(key);
1377      send_(socket, makecmd("SISMEMBER") << key << value);
1378      return recv_int_reply_(socket) == 1;
1379    }
1380
1381    /**
1382     * @returns the intersection between the Sets stored at key1, key2, ..., keyN
1383     * @warning Not cluster save (all keys must be on the same redis server)
1384     */
1385    int_type sinter(const string_vector & keys, string_set & out)
1386    {
1387      std::map<int, string_vector> per_server;
1388      BOOST_FOREACH(const string_type & key, keys)
1389      {
1390        per_server[get_socket(key)].push_back(key);
1391      }
1392
1393      size_t i = 0;
1394
1395      typedef std::pair<const int, string_vector> per_server_pair;
1396      BOOST_FOREACH(const per_server_pair & p, per_server)
1397      {
1398        send_(p.first, makecmd("SINTER") << p.second);
1399      }
1400      
1401      BOOST_FOREACH(const per_server_pair & p, per_server)
1402      {
1403        string_set cur;
1404        recv_multi_bulk_reply_(p.first, cur);
1405        if(i > 0)
1406        {
1407          string_set prev = out;
1408          out.clear();
1409          
1410          std::set_intersection(prev.begin(), prev.end(), cur.begin(), cur.end(), std::inserter(out, out.end()));
1411        }
1412        else
1413        {
1414          out = cur;
1415        }
1416        i++;
1417      }
1418  
1419      return out.size();
1420    }
1421
1422    /**
1423     * @warning Not cluster save (all keys must be on the same redis server)
1424     */
1425    int_type sinterstore(const string_type & dstkey, const string_vector & keys)
1426    {
1427      int socket = get_socket(dstkey);
1428      int source_sockets = get_socket(keys);
1429      if(socket != source_sockets)
1430      {
1431        std::cerr << 0 << std::endl;
1432        string_set content;
1433        std::cerr << 1 << std::endl;
1434        sinter(keys, content);
1435        std::cerr << 2 << std::endl;
1436        del(dstkey);
1437        std::cerr << 3 << std::endl;
1438        BOOST_FOREACH(const string_type & val, content)
1439        {
1440          std::cerr << 4 << std::endl;
1441          sadd(dstkey, val);
1442        } 
1443        std::cerr << 5 << std::endl;
1444        return content.size();
1445      }
1446      
1447      send_(socket, makecmd("SINTERSTORE") << dstkey << keys);
1448      return recv_int_reply_(socket);
1449    }
1450    
1451    int_type sunion(const string_vector & keys, string_set & out)
1452    {
1453      int socket = get_socket(keys);
1454      if(socket == -1)
1455      {
1456        std::map< int, boost::optional<makecmd> > per_socket_keys;
1457        BOOST_FOREACH(const string_type & key, keys)
1458        {
1459          boost::optional<makecmd> & optCmd = per_socket_keys[ get_socket(key) ];
1460          if( !optCmd )
1461            optCmd = makecmd("SUNION");
1462          *optCmd << key;
1463        }
1464
1465        typedef std::pair< const int, boost::optional<makecmd> > per_sock_pair;
1466        BOOST_FOREACH(const per_sock_pair & p, per_socket_keys)
1467        {
1468          send_(p.first, *p.second);
1469        }
1470        
1471        BOOST_FOREACH(const per_sock_pair & p, per_socket_keys)
1472        {
1473          recv_multi_bulk_reply_(p.first, out);
1474        }
1475        return out.size();
1476      }
1477      
1478      send_(socket, makecmd("SUNION") << keys);
1479      return recv_multi_bulk_reply_(socket, out);
1480    }
1481    
1482    int_type sunionstore(const string_type & dstkey,
1483                                                   const string_vector & keys)
1484    {
1485      int socket = get_socket(dstkey);
1486      int source_sockets = get_socket(keys);
1487      if(socket != source_sockets)
1488      {
1489        string_set content;
1490        sunion(keys, content);
1491        del(dstkey);
1492        return sadd(dstkey, content.begin(), content.end());
1493      }
1494      
1495      send_(socket, makecmd("SUNIONSTORE") << dstkey << keys);
1496      return recv_int_reply_(socket);
1497    }
1498    
1499    int_type sdiff(const string_vector & keys, string_set & out)
1500    {
1501      int socket = get_socket(keys);
1502      send_(socket, makecmd("SDIFF") << keys);
1503      return recv_multi_bulk_reply_(socket, out);
1504    }
1505    
1506    int_type sdiffstore(const string_type & dstkey, const string_vector & keys)
1507    {
1508      int socket = get_socket(dstkey);
1509      int source_sockets = get_socket(keys);
1510      if(socket != source_sockets)
1511        throw std::runtime_error("not available in cluster mode");
1512      
1513      send_(socket, makecmd("SDIFFSTORE") << dstkey << keys);
1514      return recv_int_reply_(socket);
1515    }
1516    
1517    int_type smembers(const string_type & key, string_set & out)
1518    {
1519      int socket = get_socket(key);
1520      send_(socket, makecmd("SMEMBERS") << key);
1521      return recv_multi_bulk_reply_(socket, out);
1522    }
1523    
1524    string_type srandmember(const string_type & key)
1525    {
1526      int socket = get_socket(key);
1527      send_(socket, makecmd("SPOP") << key);
1528      return recv_bulk_reply_(socket);
1529    }
1530    
1531    void zadd(const string_type & key, double score, const string_type & member)
1532    {
1533      int socket = get_socket(key);
1534      send_(socket, makecmd("ZADD") << key << score << member);
1535      recv_int_ok_reply_(socket);
1536    }
1537    
1538    void zadd(const string_type & key, const string_score_pair & value)
1539    {
1540      zadd(key, value.second, value.first);
1541    }
1542    
1543    void zrem(const string_type & key, const string_type & member)
1544    {
1545      int socket = get_socket(key);
1546      send_(socket, makecmd("ZREM") << key << member);
1547      recv_int_ok_reply_(socket);
1548    }
1549    
1550    double zincrby(const string_type & key, const string_type & member, double increment)
1551    {
1552      int socket = get_socket(key);
1553      send_(socket, makecmd("ZINCRBY") << key << increment << member);
1554      return boost::lexical_cast<double>( recv_bulk_reply_(socket) );
1555    }
1556    
1557    int_type zrank(const string_type & key, const string_type & member)
1558    {
1559      int socket = get_socket(key);
1560      send_(socket, makecmd("ZRANK") << key << member);
1561      return recv_int_reply_(socket);
1562    }
1563    
1564    int_type zrevrank(const string_type & key, const string_type & value)
1565    {
1566      int socket = get_socket(key);
1567      send_(socket, makecmd("ZREVRANK") << key << value);
1568      return boost::lexical_cast<int_type>( recv_int_reply_(socket) );
1569    }
1570    
1571    void zrange(const string_type & key, int_type start, int_type end, string_vector & out)
1572    {
1573      int socket = get_socket(key);
1574      send_( socket, makecmd("ZRANGE") << key << start << end );
1575      recv_multi_bulk_reply_(socket, out);
1576    }
1577
1578  private:
1579    void convert(const string_vector & in, string_score_vector & out)
1580    {
1581      assert( in.size() % 2 == 0 );
1582
1583      for(size_t i=0; i < in.size(); i += 2)
1584      {
1585        const std::string & value = in[i];
1586        const std::string & str_score = in[i+1];
1587        double score = boost::lexical_cast<double>(str_score);
1588        out.push_back( make_pair(value, score) );
1589      }
1590    }
1591    
1592
1593  public:
1594    
1595    void zrange(const string_type & key, int_type start, int_type end, string_score_vector & out)
1596    {
1597      int socket = get_socket(key);
1598      send_( socket, makecmd("ZRANGE") << key << start << end << "WITHSCORES" );
1599      string_vector res;
1600      recv_multi_bulk_reply_(socket, res);
1601      convert(res, out);
1602    }
1603    
1604    void zrevrange(const string_type & key, int_type start, int_type end, string_vector & out)
1605    {
1606      int socket = get_socket(key);
1607      send_( socket, makecmd("ZREVRANGE") << key << start << end );
1608      recv_multi_bulk_reply_(socket, out);
1609    }
1610    
1611    void zrevrange(const string_type & key, int_type start, int_type end, string_score_vector & out)
1612    {
1613      int socket = get_socket(key);
1614      send_( socket, makecmd("ZREVRANGE") << key << start << end << "WITHSCORES" );
1615      string_vector res;
1616      recv_multi_bulk_reply_(socket, res);
1617      convert(res, out);
1618    }
1619
1620  protected:
1621    void zrangebyscore_base(bool withscores, const string_type & key, double min, double max, string_vector & out, int_type offset, int_type max_count, int range_modification)
1622    {
1623      int socket = get_socket(key);
1624      std::string min_str, max_str;
1625      if( range_modification & exclude_min )
1626        min_str = "(";
1627      if( range_modification & exclude_max )
1628        max_str = "(";
1629      
1630      min_str += boost::lexical_cast<std::string>(min);
1631      max_str += boost::lexical_cast<std::string>(max);
1632      
1633      makecmd m("ZRANGEBYSCORE");
1634      m << key << min_str << max_str;
1635        
1636      if(max_count != -1 || offset > 0)
1637      {
1638        std::cerr << "Adding limit: " << offset << " " << max_count << std::endl;
1639        m << "LIMIT" << offset << max_count;
1640      }
1641        
1642      send_(socket, m);
1643      recv_multi_bulk_reply_(socket, out);
1644    }
1645    
1646  public:
1647    void zrangebyscore(const string_type & key, double min, double max, string_vector & out, int_type offset = 0, int_type max_count = -1, int range_modification = 0)
1648    {
1649      zrangebyscore_base(false, key, min, max, out, offset, max_count, range_modification);
1650    }
1651    
1652    void zrangebyscore(const string_type & key, double min, double max, string_score_vector & out, int_type offset = 0, int_type max_count = -1, int range_modification = 0)
1653    {
1654      string_vector res;
1655      zrangebyscore_base(true, key, min, max, res, offset, max_count, range_modification);
1656      convert(res, out);
1657    }
1658    
1659    int_type zcount(const string_type & key, double min, double max, int range_modification = 0)
1660    {
1661      int socket = get_socket(key);
1662      std::string min_str, max_str;
1663      if( range_modification & exclude_min )
1664        min_str = "(";
1665      if( range_modification & exclude_max )
1666        max_str = "(";
1667      
1668      min_str += boost::lexical_cast<std::string>(min);
1669      max_str += boost::lexical_cast<std::string>(max);
1670      
1671      send_(socket, makecmd("ZCOUNT") << key << min_str << max_str);
1672      return recv_int_reply_(socket);
1673    }
1674    
1675    int_type zremrangebyrank( const string_type & key, int_type start, int_type end )
1676    {
1677      int socket = get_socket(key);
1678      send_(socket, makecmd("ZREMRANGEBYRANK") << key << start << end);
1679      return recv_int_reply_(socket);
1680    }
1681    
1682    int_type zremrangebyscore( const string_type& key, double min, double max )
1683    {
1684      int socket = get_socket(key);
1685      send_(socket, makecmd("ZREMRANGEBYSCORE") << key << min << max);
1686      return recv_int_reply_(socket);
1687    }
1688    
1689    int_type zcard( const string_type & key )
1690    {
1691      int socket = get_socket(key);
1692      send_(socket, makecmd("ZCARD") << key);
1693      return recv_int_reply_(socket);
1694    }
1695    
1696    double zscore( const string_type& key, const string_type& element )
1697    {
1698      int socket = get_socket(key);
1699      send_(socket, makecmd("ZSCORE") << key << element);
1700      return boost::lexical_cast<double>( recv_bulk_reply_(socket) );
1701    }
1702    
1703    int_type zunionstore( const string_type & dstkey, const string_vector & keys, const std::vector<double> & weights = std::vector<double>(), aggregate_type aggragate = aggregate_sum )
1704    {
1705      int dst_socket = get_socket(dstkey);
1706      int socket = get_socket(keys);
1707      if(socket != dst_socket)
1708        throw std::runtime_error("feature is not available in cluster mode");
1709      
1710      makecmd m("ZUNIONSTORE");
1711      m << dstkey << keys.size() << keys;
1712      
1713      if( weights.size() > 0 )
1714      {
1715        assert(keys.size() == weights.size());
1716        m << "WEIGHTS" << weights;
1717      }
1718      
1719      m << "AGGREGATE";
1720      switch(aggragate)
1721      {
1722        case aggregate_sum:
1723          m << "SUM";
1724          break;
1725        case aggregate_min:
1726          m << "MIN";
1727          break;
1728        case aggregate_max:
1729          m << "MAX";
1730          break;
1731        default:
1732          assert(false);
1733      }
1734      
1735      send_(socket, m);
1736      return re

Large files files are truncated, but you can click here to view the full file