Arcane  v3.14.10.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
HiredisAdapter.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2024 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* HiredisAdapter.cc (C) 2000-2024 */
9/* */
10/* Adapteur pour 'hiredis'. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/FatalErrorException.h"
15#include "arcane/utils/UniqueArray.h"
16#include "arcane/utils/TraceAccessor.h"
17
19#include "arcane/std/internal/IRedisContext.h"
20
21#include "arcane_packages.h"
22
23#include <string.h>
24
25#ifdef ARCANE_HAS_PACKAGE_HIREDIS
26
27#include <hiredis/hiredis.h>
28
29/*---------------------------------------------------------------------------*/
30/*---------------------------------------------------------------------------*/
31
32namespace Arcane::Redis
33{
34
35/*---------------------------------------------------------------------------*/
36/*---------------------------------------------------------------------------*/
37
38class HiredisCommand
39{
40 public:
41
42 ~HiredisCommand()
43 {
44 _checkDestroy();
45 }
46
47 void sendCommand(redisContext* c, const char* format, ...)
48 {
49 _checkDestroy();
50 {
51 va_list arguments;
52 va_start(arguments, format);
53 m_reply = reinterpret_cast<redisReply*>(redisvCommand(c, format, arguments));
54 va_end(arguments);
55 }
56 if (m_reply == nullptr)
57 ARCANE_FATAL("Null reply");
58 if (c->err != 0)
59 ARCANE_FATAL("Error during redis connection: %s\n", c->errstr);
60 }
61 bool isReplyString() const
62 {
63 ARCANE_CHECK_POINTER(m_reply);
64 return m_reply->type == REDIS_REPLY_STRING;
65 }
66 size_t replyLength() const
67 {
68 ARCANE_CHECK_POINTER(m_reply);
69 return m_reply->len;
70 }
71 const char* replyData() const
72 {
73 ARCANE_CHECK_POINTER(m_reply);
74 return m_reply->str;
75 }
76
77 private:
78
79 ::redisReply* m_reply = nullptr;
80
81 void _checkDestroy()
82 {
83 if (m_reply) {
84 freeReplyObject(m_reply);
85 m_reply = nullptr;
86 }
87 }
88};
89
90/*---------------------------------------------------------------------------*/
91/*---------------------------------------------------------------------------*/
92
93class HiredisContext
94: public TraceAccessor
95, public IRedisContext
96{
97 public:
98
99 HiredisContext(ITraceMng* tm)
100 : TraceAccessor(tm)
101 {}
102 ~HiredisContext()
103 {
104 if (m_redis_context)
105 ::redisFree(m_redis_context);
106 }
107
108 public:
109
110 void open(const String& machine, Int32 port) override
111 {
112 redisContext* c = ::redisConnect(machine.localstr(), port);
113 m_redis_context = c;
114 if (!c)
115 ARCANE_FATAL("Error during redis connection: can not allocate redis context");
116 if (c->err != 0)
117 ARCANE_FATAL("Error during redis connection: {0}", c->errstr);
118 }
119
120 public:
121
122 void sendBuffer(const String& key, Span<const std::byte> bytes) override
123 {
124 _checkContext();
125 auto key_bytes = key.bytes();
126 size_t key_len = key_bytes.size();
127 size_t buf_size = bytes.size();
128 HiredisCommand command;
129 command.sendCommand(m_redis_context, "SET %b %b", key_bytes.data(), key_len, bytes.data(), buf_size);
130 }
131
132 void getBuffer(const String& key, Array<std::byte>& bytes) override
133 {
134 _checkContext();
135 auto key_bytes = key.bytes();
136 size_t key_len = key_bytes.size();
137 HiredisCommand command;
138 command.sendCommand(m_redis_context, "GET %b", key_bytes.data(), key_len);
139 if (!command.isReplyString())
140 ARCANE_FATAL("Reply is not a string");
141 Int64 reply_length = command.replyLength();
142 auto* data_as_bytes = reinterpret_cast<const std::byte*>(command.replyData());
143 bytes.copy(Span<const std::byte>(data_as_bytes, reply_length));
144 }
145
146 public:
147
148 ::redisContext* m_redis_context = nullptr;
149
150 private:
151
152 void _checkContext()
153 {
154 if (!m_redis_context)
155 ARCANE_FATAL("No redis context. You have to call open() to create a context");
156 }
157};
158
159/*---------------------------------------------------------------------------*/
160/*---------------------------------------------------------------------------*/
161
162class HiredisAdapter
163: public TraceAccessor
164{
165 public:
166
167 HiredisAdapter(ITraceMng* tm)
168 : TraceAccessor(tm)
169 , m_context(tm)
170 {}
171
172 public:
173
174 void test();
175
176 private:
177
178 HiredisContext m_context;
179};
180
181/*---------------------------------------------------------------------------*/
182/*---------------------------------------------------------------------------*/
183
184void HiredisAdapter::
185test()
186{
187 m_context.open("127.0.0.1", 6379);
188
189 redisContext* c = m_context.m_redis_context;
190 char *s, *e;
191
192 HiredisCommand command;
193
194 constexpr const char* REDIS_VERSION_FIELD = "redis_version:";
195 command.sendCommand(c, "INFO");
196 if (!command.isReplyString())
197 ARCANE_FATAL("Can not get INFO");
198 if ((s = strstr((char*)command.replyData(), REDIS_VERSION_FIELD)) == NULL)
199 ARCANE_FATAL("No INFO");
200
201 s += strlen(REDIS_VERSION_FIELD);
202
203 /* We need a field terminator and at least 'x.y.z' (5) bytes of data */
204 if ((e = strstr(s, "\r\n")) == NULL || (e - s) < 5)
205 ARCANE_FATAL("Invalid version number");
206 info() << "VERSION=" << command.replyData();
207
208 UniqueArray<Int64> my_buffer(10000);
209 for (Int64 i = 0, n = my_buffer.size(); i < n; ++i) {
210 my_buffer[i] = i + 1;
211 }
212 Span<const std::byte> send_buf(asBytes(my_buffer));
213 Int64 send_size = send_buf.size();
214 info() << "SEND_SIZE=" << send_size;
215 //command.sendCommand(c, "SET mytest %b", my_buffer.data(), (size_t)send_size);
216 m_context.sendBuffer("mytest", send_buf);
217
218 UniqueArray<std::byte> out_bytes;
219 m_context.getBuffer("mytest", out_bytes);
220
221 Int64 reply_length = out_bytes.largeSize();
222 info() << "REPLY_SIZE = " << reply_length;
223 if (reply_length != send_size)
224 ARCANE_FATAL("Bad reply v={0} expected={1}", reply_length, send_size);
225
226 Span<const Int64> receive_buf = Arccore::asSpan<Int64>(out_bytes.span());
227 if (receive_buf != my_buffer)
228 ARCANE_FATAL("Bad value");
229 for (Int64 x = 0, n = receive_buf.size(); x < n; ++x)
230 if (receive_buf[x] != my_buffer[x])
231 ARCANE_FATAL("Bad value i={0} v={1} expected={2}", x, receive_buf[x], my_buffer[x]);
232}
233
234/*---------------------------------------------------------------------------*/
235/*---------------------------------------------------------------------------*/
236
237} // End namespace Arcane::Redis
238
239/*---------------------------------------------------------------------------*/
240/*---------------------------------------------------------------------------*/
241
242#endif //ARCANE_HAS_PACKAGE_HIREDIS
243
244/*---------------------------------------------------------------------------*/
245/*---------------------------------------------------------------------------*/
246
247namespace Arcane
248{
249
250/*---------------------------------------------------------------------------*/
251/*---------------------------------------------------------------------------*/
252
253extern "C++" ARCANE_STD_EXPORT void
254_testRedisAdapter([[maybe_unused]] ITraceMng* tm)
255{
256#ifdef ARCANE_HAS_PACKAGE_HIREDIS
257 Redis::HiredisAdapter h{ tm };
258 h.test();
259#endif
260}
261
262/*---------------------------------------------------------------------------*/
263/*---------------------------------------------------------------------------*/
264
265extern "C++" Ref<IRedisContext>
266createRedisContext([[maybe_unused]] ITraceMng* tm)
267{
268#ifdef ARCANE_HAS_PACKAGE_HIREDIS
269 return makeRef<IRedisContext>(new Redis::HiredisContext(tm));
270#else
271 ARCANE_FATAL("Can not create Redis context because Arcane is not "
272 "compiled with support for 'hiredis' library");
273#endif
274}
275
276/*---------------------------------------------------------------------------*/
277/*---------------------------------------------------------------------------*/
278
279} // End namespace Arcane
280
281/*---------------------------------------------------------------------------*/
282/*---------------------------------------------------------------------------*/
#define ARCANE_CHECK_POINTER(ptr)
Macro retournant le pointeur ptr s'il est non nul ou lancant une exception s'il est nul.
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
Déclarations des types généraux de Arcane.
Lecteur des fichiers de maillage via la bibliothèque LIMA.
Definition Lima.cc:120
TraceMessage info() const
Flot pour un message d'information.
-*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
detail::SpanTypeFromSize< conststd::byte, SizeType >::SpanType asBytes(const SpanImpl< DataType, SizeType, Extent > &s)
Converti la vue en un tableau d'octets non modifiables.
Definition Span.h:881