Commit c6dbba89b067ac3e6fcf691f3c8a856ac3ef1b5c

Authored by Hayk Martirosyan
1 parent 3b431e0e

Binary value support for last word in command

Implemented limited but useful binary data support without breaking the
API. If the last character of the command is a ", look for the first ",
and everything in between the quotes treat as binary data. Takes care of
setting a key with a binary value. Not useful if there need to be
multiple binary entries in one command, or for binary keys. Also need to
be careful if the last character of the value actually needs to be a
quote, in which case we need to quote the value.
CMakeLists.txt
@@ -84,4 +84,7 @@ if (examples) @@ -84,4 +84,7 @@ if (examples)
84 add_executable(multi_client examples/multi-client.cpp ${SRC_ALL}) 84 add_executable(multi_client examples/multi-client.cpp ${SRC_ALL})
85 target_link_libraries(multi_client ${LIB_REDIS}) 85 target_link_libraries(multi_client ${LIB_REDIS})
86 86
  87 + add_executable(binary_data examples/binary_data.cpp ${SRC_ALL})
  88 + target_link_libraries(binary_data ${LIB_REDIS})
  89 +
87 endif() 90 endif()
README.md
1 redox 1 redox
2 ====== 2 ======
3 3
4 -Modern, asynchronous, and wicked fast C++11 bindings for Redis 4 +Modern, asynchronous, and wicked fast C++11 client for Redis
5 5
6 ## Overview 6 ## Overview
7 7
examples/binary_data.cpp 0 → 100644
  1 +/**
  2 +* Basic use of Redox to set and get binary data.
  3 +*/
  4 +
  5 +#include <iostream>
  6 +#include <algorithm>
  7 +#include <random>
  8 +#include "../src/redox.hpp"
  9 +
  10 +using namespace std;
  11 +
  12 +/**
  13 +* Random string generator.
  14 +*/
  15 +std::string random_string(size_t length) {
  16 + std::string str(length, 0);
  17 + std::generate_n(str.begin(), length, []{ return (unsigned char)rand(); });
  18 + return str;
  19 +}
  20 +
  21 +int main(int argc, char* argv[]) {
  22 +
  23 + redox::Redox rdx = {"localhost", 6379}; // Initialize Redox
  24 + if(!rdx.start()) return 1; // Start the event loop
  25 +
  26 + rdx.del("binary");
  27 +
  28 + string binary_data = random_string(10000);
  29 +
  30 + auto c = rdx.command_blocking<string>("SET binary \"" + binary_data + "\"");
  31 + if(c->ok()) cout << "Reply: " << c->reply() << endl;
  32 + else cerr << "Failed to set key! Status: " << c->status() << endl;
  33 + c->free();
  34 +
  35 + c = rdx.command_blocking<string>("GET binary");
  36 + if(c->ok()) {
  37 + if(c->reply() == binary_data) cout << "Binary data matches!" << endl;
  38 + else cerr << "Binary data differs!" << endl;
  39 + }
  40 + else cerr << "Failed to get key! Status: " << c->status() << endl;
  41 + c->free();
  42 +
  43 + rdx.stop(); // Shut down the event loop
  44 +}
src/command.cpp
@@ -46,7 +46,7 @@ void Command&lt;std::string&gt;::invoke_callback() { @@ -46,7 +46,7 @@ void Command&lt;std::string&gt;::invoke_callback() {
46 invoke_error(REDOX_WRONG_TYPE); 46 invoke_error(REDOX_WRONG_TYPE);
47 47
48 } else { 48 } else {
49 - std::string s = reply_obj->str; 49 + std::string s(reply_obj->str, reply_obj->len);
50 invoke(s); 50 invoke(s);
51 } 51 }
52 } 52 }
@@ -129,7 +129,7 @@ void Command&lt;std::vector&lt;std::string&gt;&gt;::invoke_callback() { @@ -129,7 +129,7 @@ void Command&lt;std::vector&lt;std::string&gt;&gt;::invoke_callback() {
129 std::cerr << "[ERROR] " << cmd << ": Received non-array reply." << std::endl; 129 std::cerr << "[ERROR] " << cmd << ": Received non-array reply." << std::endl;
130 invoke_error(REDOX_WRONG_TYPE); 130 invoke_error(REDOX_WRONG_TYPE);
131 } 131 }
132 - v.push_back(r->str); 132 + v.emplace_back(r->str, r->len);
133 } 133 }
134 invoke(v); 134 invoke(v);
135 } 135 }
@@ -153,7 +153,7 @@ void Command&lt;std::unordered_set&lt;std::string&gt;&gt;::invoke_callback() { @@ -153,7 +153,7 @@ void Command&lt;std::unordered_set&lt;std::string&gt;&gt;::invoke_callback() {
153 std::cerr << "[ERROR] " << cmd << ": Received non-array reply." << std::endl; 153 std::cerr << "[ERROR] " << cmd << ": Received non-array reply." << std::endl;
154 invoke_error(REDOX_WRONG_TYPE); 154 invoke_error(REDOX_WRONG_TYPE);
155 } 155 }
156 - v.insert(r->str); 156 + v.emplace(r->str, r->len);
157 } 157 }
158 invoke(v); 158 invoke(v);
159 } 159 }
@@ -177,7 +177,7 @@ void Command&lt;std::set&lt;std::string&gt;&gt;::invoke_callback() { @@ -177,7 +177,7 @@ void Command&lt;std::set&lt;std::string&gt;&gt;::invoke_callback() {
177 std::cerr << "[ERROR] " << cmd << ": Received non-array reply." << std::endl; 177 std::cerr << "[ERROR] " << cmd << ": Received non-array reply." << std::endl;
178 invoke_error(REDOX_WRONG_TYPE); 178 invoke_error(REDOX_WRONG_TYPE);
179 } 179 }
180 - v.insert(r->str); 180 + v.emplace(r->str, r->len);
181 } 181 }
182 invoke(v); 182 invoke(v);
183 } 183 }
src/redox.cpp
@@ -235,6 +235,30 @@ void Redox::command_callback(redisAsyncContext *ctx, void *r, void *privdata) { @@ -235,6 +235,30 @@ void Redox::command_callback(redisAsyncContext *ctx, void *r, void *privdata) {
235 template<class ReplyT> 235 template<class ReplyT>
236 bool Redox::submit_to_server(Command<ReplyT>* c) { 236 bool Redox::submit_to_server(Command<ReplyT>* c) {
237 c->pending++; 237 c->pending++;
  238 +
  239 + // Process binary data if trailing quotation. This is a limited implementation
  240 + // to allow binary data between the first and the last quotes of the command string,
  241 + // if the very last character of the command is a quote ('"').
  242 + if(c->cmd[c->cmd.size()-1] == '"') {
  243 +
  244 + // Indices of the quotes
  245 + int first = c->cmd.find('"');
  246 + int last = c->cmd.size()-1;
  247 +
  248 + // Proceed only if the first and last quotes are different
  249 + if(first != last) {
  250 +
  251 + string format = c->cmd.substr(0, first) + "%b";
  252 + string value = c->cmd.substr(first+1, last-first-1);
  253 + if (redisAsyncCommand(c->rdx->ctx, command_callback<ReplyT>, (void*)c->id, format.c_str(), value.c_str(), value.size()) != REDIS_OK) {
  254 + cerr << "[ERROR] Could not send \"" << c->cmd << "\": " << c->rdx->ctx->errstr << endl;
  255 + c->invoke_error(REDOX_SEND_ERROR);
  256 + return false;
  257 + }
  258 + return true;
  259 + }
  260 + }
  261 +
238 if (redisAsyncCommand(c->rdx->ctx, command_callback<ReplyT>, (void*)c->id, c->cmd.c_str()) != REDIS_OK) { 262 if (redisAsyncCommand(c->rdx->ctx, command_callback<ReplyT>, (void*)c->id, c->cmd.c_str()) != REDIS_OK) {
239 cerr << "[ERROR] Could not send \"" << c->cmd << "\": " << c->rdx->ctx->errstr << endl; 263 cerr << "[ERROR] Could not send \"" << c->cmd << "\": " << c->rdx->ctx->errstr << endl;
240 c->invoke_error(REDOX_SEND_ERROR); 264 c->invoke_error(REDOX_SEND_ERROR);