//------------------------------------------------------------------------------
// File: EncodedRequest.cc
// Author: Georgios Bitzes - CERN
//------------------------------------------------------------------------------
/************************************************************************
* qclient - A simple redis C++ client with support for redirects *
* Copyright (C) 2016 CERN/Switzerland *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see .*
************************************************************************/
#include "qclient/EncodedRequest.hh"
#include
#include "fmt/format.h"
#include
#include
namespace qclient {
void EncodedRequest::initFromChunks(size_t nchunks, const char** chunks, const size_t* sizes) {
// First, format all integers we're going to need.. fmt::format_int
// keeps its buffers on the stack.
fmt::format_int nchunksFormatted(nchunks);
// Abuse a stack memory region to store fmt::format_int's. I found no better
// way to do this, while making sure all variables are kept on the stack.
// We use placement new to construct the objects directly in a custom memory
// region.
char memoryRegion[sizeof(fmt::format_int) * nchunks];
for(size_t i = 0; i < nchunks; i++) {
new (memoryRegion + (i*sizeof(fmt::format_int))) fmt::format_int(sizes[i]);
}
// Calculate the required size of our buffer.
length = 0;
for(size_t i = 0; i < nchunks; i++) {
length += sizes[i] + (((fmt::format_int*) memoryRegion)[i]).size();
length += 1 + 2 + 2;
}
length += nchunksFormatted.size() + 3;
char* buff = (char*) malloc(length);
buff[0] = '*';
memcpy(buff+1, nchunksFormatted.data(), nchunksFormatted.size());
size_t pos = 1 + nchunksFormatted.size();
buff[pos++] = '\r';
buff[pos++] = '\n';
for(size_t i = 0; i < nchunks; i++) {
buff[pos++] = '$';
fmt::format_int *formatted = reinterpret_cast(&memoryRegion[sizeof(fmt::format_int)*i]);
memcpy(buff+pos, formatted->data(), formatted->size());
pos += formatted->size();
buff[pos++] = '\r';
buff[pos++] = '\n';
memcpy(buff+pos, chunks[i], sizes[i]);
pos += sizes[i];
buff[pos++] = '\r';
buff[pos++] = '\n';
}
buffer.reset(buff);
}
EncodedRequest::EncodedRequest(size_t nchunks, const char** chunks, const size_t* sizes) {
initFromChunks(nchunks, chunks, sizes);
}
EncodedRequest EncodedRequest::fuseIntoBlock(const std::deque &block) {
size_t fusedSize = 0u;
for(size_t i = 0; i < block.size(); i++) {
fusedSize += block[i].getLen();
}
char* buff = (char*) malloc(fusedSize);
size_t pos = 0;
for(size_t i = 0; i < block.size(); i++) {
size_t localSize = block[i].getLen();
memcpy(buff+pos, block[i].getBuffer(), localSize);
pos += localSize;
}
return EncodedRequest(buff, fusedSize);
}
EncodedRequest EncodedRequest::fuseIntoBlockAndSurround(std::deque &&block) {
block.emplace_front(EncodedRequest::make("MULTI"));
block.emplace_back(EncodedRequest::make("EXEC"));
return EncodedRequest::fuseIntoBlock(block);
}
namespace {
std::string escapeNonPrintable(const std::string &str) {
std::stringstream ss;
for(size_t i = 0; i < str.size(); i++) {
if(isprint(str[i])) {
ss << str[i];
}
else if(str[i] == '\0') {
ss << "\\x00";
}
else {
char buff[16];
snprintf(buff, 16, "\\x%02X", (unsigned char) str[i]);
ss << buff;
}
}
return ss.str();
}
}
std::string EncodedRequest::toPrintableString() const {
if(!buffer) {
return "!!!uninitialized!!!";
}
return escapeNonPrintable(std::string(buffer.get(), length));
}
}