Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
eos
QClient
Compare Revisions
26cb9ff78b7945c55860dc10a0173288447ebbf8...868e222d8a55454eee3ebda1add2b57bc6637dd4
Commits (2)
Add Formatting class, to be used for redis-encoding various types
· f39c8373
Georgios Bitzes
authored
Mar 15, 2019
f39c8373
Implement convenience function to redis-serialize maps
· 868e222d
Georgios Bitzes
authored
Mar 15, 2019
868e222d
Hide whitespace changes
Inline
Side-by-side
include/qclient/Formatting.hh
0 → 100644
View file @
868e222d
//------------------------------------------------------------------------------
// File: Formatting.hh
// Author: Georgios Bitzes - CERN
//------------------------------------------------------------------------------
/************************************************************************
* qclient - A simple redis C++ client with support for redirects *
* Copyright (C) 2019 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 <http://www.gnu.org/licenses/>.*
************************************************************************/
#ifndef QCLIENT_FORMATTING_HH
#define QCLIENT_FORMATTING_HH
#include <string>
#include <vector>
#include <map>
#include <sstream>
#define SSTR(message) static_cast<std::ostringstream&>(std::ostringstream().flush() << message).str()
namespace
qclient
{
//------------------------------------------------------------------------------
// A class to help redis-serialize any given data structures
// NOTE: When in doubt, this class will serialize into string-type messages,
// not status-type messages. (status messages are not binary-safe)
//------------------------------------------------------------------------------
class
Formatting
{
public:
//----------------------------------------------------------------------------
// External API: Serialize onto a plain string.
//----------------------------------------------------------------------------
template
<
typename
T
>
static
std
::
string
serialize
(
T
&&
arg
)
{
std
::
ostringstream
ss
;
serializeInternal
(
ss
,
std
::
forward
<
T
>
(
arg
));
return
ss
.
str
();
}
//----------------------------------------------------------------------------
// Serialize a vector of arbitrary types
//----------------------------------------------------------------------------
template
<
typename
...
Args
>
static
std
::
string
serializeVector
(
Args
&&
...
args
)
{
const
size_t
n
=
sizeof
...(
args
);
std
::
ostringstream
ss
;
ss
<<
"*"
<<
n
<<
"
\r\n
"
;
serializeMulti
(
ss
,
std
::
forward
<
Args
>
(
args
)...);
return
ss
.
str
();
}
private:
//----------------------------------------------------------------------------
// Internal API: Serialize onto a given std::ostringstream.
// We need this to support efficient serialization of arrays.
//----------------------------------------------------------------------------
static
void
serializeInternal
(
std
::
ostringstream
&
ss
,
const
std
::
string
&
str
);
static
void
serializeInternal
(
std
::
ostringstream
&
ss
,
int64_t
num
);
//----------------------------------------------------------------------------
// Serialize any kind of vector
//----------------------------------------------------------------------------
template
<
typename
T
>
static
void
serializeInternal
(
std
::
ostringstream
&
ss
,
const
std
::
vector
<
T
>
&
vec
)
{
ss
<<
"*"
<<
vec
.
size
()
<<
"
\r\n
"
;
for
(
size_t
i
=
0
;
i
<
vec
.
size
();
i
++
)
{
serializeInternal
(
ss
,
vec
[
i
]);
}
}
//----------------------------------------------------------------------------
// Serialize any kind of map
//----------------------------------------------------------------------------
template
<
typename
K
,
typename
V
>
static
void
serializeInternal
(
std
::
ostringstream
&
ss
,
const
std
::
map
<
K
,
V
>
&
map
)
{
ss
<<
"*"
<<
2
*
map
.
size
()
<<
"
\r\n
"
;
for
(
auto
it
=
map
.
begin
();
it
!=
map
.
end
();
it
++
)
{
serializeInternal
(
ss
,
it
->
first
);
serializeInternal
(
ss
,
it
->
second
);
}
}
//----------------------------------------------------------------------------
// Recursively serialize each type in the vector
//----------------------------------------------------------------------------
template
<
class
none
=
void
>
static
void
serializeMulti
(
std
::
ostringstream
&
ss
)
{}
// base case for recursion
template
<
typename
Head
,
typename
...
Tail
>
static
void
serializeMulti
(
std
::
ostringstream
&
ss
,
Head
&&
head
,
Tail
&&
...
tail
)
{
serializeInternal
(
ss
,
std
::
forward
<
Head
>
(
head
));
serializeMulti
<
Tail
...
>
(
ss
,
std
::
forward
<
Tail
>
(
tail
)...);
}
};
}
#endif
include/qclient/ResponseBuilder.hh
View file @
868e222d
...
...
@@ -53,6 +53,10 @@ public:
static
redisReplyPtr
makeArr
(
const
std
::
string
&
str1
,
const
std
::
string
&
str2
,
int
num
);
static
redisReplyPtr
makeStatus
(
const
std
::
string
&
msg
);
// Convenience function to quickly parse a redis-encoded string into redisReplyPtr
static
redisReplyPtr
parseRedisEncodedString
(
const
std
::
string
&
str
);
private:
struct
Deleter
{
void
operator
()(
redisReader
*
reader
)
{
redisReaderFree
(
reader
);
}
...
...
src/Formatting.cc
View file @
868e222d
...
...
@@ -22,6 +22,7 @@
************************************************************************/
#include "qclient/Reply.hh"
#include "qclient/Formatting.hh"
#include <hiredis/hiredis.h>
#include <sstream>
#include <memory>
...
...
@@ -112,4 +113,17 @@ std::string describeRedisReply(const redisReply *const redisReply, const std::st
std
::
string
describeRedisReply
(
const
redisReplyPtr
&
redisReply
)
{
return
describeRedisReply
(
redisReply
.
get
(),
""
);
}
//------------------------------------------------------------------------------
// Internal API: Serialize onto a given std::ostringstream.
// We need this to support efficient serialization of arrays.
//------------------------------------------------------------------------------
void
Formatting
::
serializeInternal
(
std
::
ostringstream
&
ss
,
const
std
::
string
&
str
)
{
ss
<<
"$"
<<
str
.
size
()
<<
"
\r\n
"
<<
str
<<
"
\r\n
"
;
}
void
Formatting
::
serializeInternal
(
std
::
ostringstream
&
ss
,
int64_t
num
)
{
ss
<<
":"
<<
num
<<
"
\r\n
"
;
}
}
src/ResponseBuilder.cc
View file @
868e222d
...
...
@@ -125,4 +125,14 @@ redisReplyPtr ResponseBuilder::makeStatus(const std::string &msg) {
return
ans
;
}
redisReplyPtr
ResponseBuilder
::
parseRedisEncodedString
(
const
std
::
string
&
str
)
{
ResponseBuilder
builder
;
builder
.
feed
(
str
);
redisReplyPtr
ans
;
builder
.
pull
(
ans
);
return
ans
;
}
}
test/formatting.cc
View file @
868e222d
...
...
@@ -23,6 +23,7 @@
#include "gtest/gtest.h"
#include "qclient/QClient.hh"
#include "qclient/Formatting.hh"
using
namespace
qclient
;
void
setStr
(
redisReplyPtr
reply
,
const
std
::
string
&
str
)
{
...
...
@@ -93,3 +94,54 @@ TEST(DescribeRedisReply, BasicSanity1) {
reply
->
type
=
999
;
ASSERT_EQ
(
describeRedisReply
(
reply
),
"!!! unknown reply type !!!"
);
}
TEST
(
Formatting
,
SerializeString
)
{
ASSERT_EQ
(
Formatting
::
serialize
(
"asdf"
),
"$4
\r\n
asdf
\r\n
"
);
}
TEST
(
Formatting
,
SerializeVector
)
{
ASSERT_EQ
(
Formatting
::
serializeVector
(
"asdf"
,
"bbb"
,
"aaaa"
),
"*3
\r\n
"
"$4
\r\n
asdf
\r\n
"
"$3
\r\n
bbb
\r\n
"
"$4
\r\n
aaaa
\r\n
"
);
ASSERT_EQ
(
Formatting
::
serializeVector
(
"asdf"
,
1234
),
"*2
\r\n
"
"$4
\r\n
asdf
\r\n
"
":1234
\r\n
"
);
}
TEST
(
Formatting
,
SerializeIntVector
)
{
std
::
vector
<
int64_t
>
vec
=
{
4
,
9
,
8
};
ASSERT_EQ
(
Formatting
::
serialize
(
vec
),
"*3
\r\n
"
":4
\r\n
"
":9
\r\n
"
":8
\r\n
"
);
redisReplyPtr
reply
=
ResponseBuilder
::
parseRedisEncodedString
(
Formatting
::
serialize
(
vec
));
ASSERT_EQ
(
describeRedisReply
(
reply
),
"1) (integer) 4
\n
"
"2) (integer) 9
\n
"
"3) (integer) 8
\n
"
);
}
TEST
(
Formatting
,
SerializeStringMap
)
{
std
::
map
<
std
::
string
,
std
::
string
>
map
;
map
[
"i like"
]
=
"pickles"
;
map
[
"asdf"
]
=
"1234"
;
ASSERT_EQ
(
Formatting
::
serialize
(
map
),
"*4
\r\n
"
"$4
\r\n
asdf
\r\n
"
"$4
\r\n
1234
\r\n
"
"$6
\r\n
i like
\r\n
"
"$7
\r\n
pickles
\r\n
"
);
}