File Coverage

XS.xs
Criterion Covered Total %
statement 109 129 84.5
branch 29 42 69.0
condition n/a
subroutine n/a
pod n/a
total 138 171 80.7


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2              
3             #include "EXTERN.h"
4             #include "perl.h"
5             #include "XSUB.h"
6             #define NEED_newRV_noinc
7             #define NEED_sv_2pv_flags
8             #include "ppport.h"
9             #include
10             #include
11              
12             #ifdef PERL_IMPLICIT_CONTEXT
13              
14             #define dTHXREDIS(task) \
15             dTHXa(task->privdata);
16              
17             #define SET_THX_REDIS(r) \
18             do { r->privdata = aTHX; } while(0)
19              
20             #else
21              
22             #define dTHXREDIS(task)
23             #define SET_THX_REDIS(r)
24              
25             #endif
26              
27             static const char redisTypes[] = {
28             [REDIS_REPLY_STRING] = '$',
29             [REDIS_REPLY_ARRAY] = '*',
30             [REDIS_REPLY_INTEGER] = ':',
31             [REDIS_REPLY_NIL] = '$',
32             [REDIS_REPLY_STATUS] = '+',
33             [REDIS_REPLY_ERROR] = '-'
34             #if HIREDIS_MAJOR > 0
35             ,
36             [REDIS_REPLY_DOUBLE] = ',',
37             [REDIS_REPLY_BOOL] = '#'
38             #endif
39             };
40              
41 59           static SV *createReply(pTHX_ SV *sv, int type)
42             {
43 59           char reply_type = redisTypes[type];
44 59           HV *reply = newHV();
45              
46 59           hv_stores(reply, "type", newSVpvn(&reply_type, sizeof reply_type));
47 59           hv_stores(reply, "data", sv);
48 59           return newRV_noinc((SV*)reply);
49             }
50              
51 0           static void freeReplyObjectSV(void *reply) {
52             dTHX;
53 0           SV* r = reply;
54 0           sv_2mortal(r);
55 0           }
56              
57 59           static inline void storeParent(pTHX_ const redisReadTask *task, SV *reply)
58             {
59 59 100         if (task->parent) {
60 22           SV *const obj = task->parent->obj;
61 22           HV *const parent = (HV*)SvRV(obj);
62 22           SV **const data = hv_fetchs(parent, "data", FALSE);
63             assert(data && SvTYPE(SvRV(*data)) == SVt_PVAV);
64 22           av_store((AV*)SvRV(*data), task->idx, reply);
65             }
66 59           }
67              
68 43           static void *createStringObjectSV(const redisReadTask *task, char *str,
69             size_t len)
70             {
71             dTHXREDIS(task);
72              
73 43           SV *const reply = createReply(aTHX_ newSVpvn(str, len), task->type);
74 43           storeParent(aTHX_ task, reply);
75 43           return reply;
76             }
77              
78             #if HIREDIS_MAJOR > 0
79 11           static void *createArrayObjectSV(const redisReadTask *task, size_t elements)
80             #else
81             static void *createArrayObjectSV(const redisReadTask *task, int elements)
82             #endif
83             {
84             dTHXREDIS(task);
85              
86 11           AV *av = newAV();
87 11           SV *const reply = createReply(aTHX_ newRV_noinc((SV*)av), task->type);
88 11           av_extend(av, elements);
89 11           storeParent(aTHX_ task, reply);
90 11           return reply;
91             }
92              
93 3           static void *createIntegerObjectSV(const redisReadTask *task, long long value)
94             {
95             dTHXREDIS(task);
96             /* Not pretty, but perl doesn't always have a sane way to store long long in
97             * a SV.
98             */
99             #if defined(LONGLONGSIZE) && LONGLONGSIZE == IVSIZE
100 3           SV *sv = newSViv(value);
101             #else
102             SV *sv = newSVnv(value);
103             #endif
104              
105 3           SV *reply = createReply(aTHX_ sv, task->type);
106 3           storeParent(aTHX_ task, reply);
107 3           return reply;
108             }
109              
110             #if HIREDIS_MAJOR > 0
111 0           static void *createDoubleObjectSV(const redisReadTask *task, double value,
112             char* str, size_t len)
113             {
114             dTHXREDIS(task);
115              
116 0           SV *sv = newSVpvn(str, len);
117 0 0         SvUPGRADE(sv, SVt_PVNV);
118 0           SvNV_set(sv, value);
119 0           SvNOK_on(sv);
120 0           SV *const reply = createReply(aTHX_ sv, task->type);
121 0           storeParent(aTHX_ task, reply);
122 0           return reply;
123             }
124             #endif
125              
126 2           static void *createNilObjectSV(const redisReadTask *task)
127             {
128             dTHXREDIS(task);
129              
130 2           SV *reply = createReply(aTHX_ &PL_sv_undef, task->type);
131 2           storeParent(aTHX_ task, reply);
132 2           return reply;
133             }
134              
135             #if HIREDIS_MAJOR > 0
136 0           static void *createBoolObjectSV(const redisReadTask *task, int value)
137             {
138             dTHXREDIS(task);
139 0           SV *sv = newSViv(value);
140              
141 0           SV *reply = createReply(aTHX_ sv, task->type);
142 0           storeParent(aTHX_ task, reply);
143 0           return reply;
144             }
145             #endif
146              
147             /* Declarations below are used in the XS section */
148              
149             static redisReplyObjectFunctions perlRedisFunctions = {
150             createStringObjectSV,
151             createArrayObjectSV,
152             createIntegerObjectSV,
153             #if HIREDIS_MAJOR > 0
154             createDoubleObjectSV,
155             #endif
156             createNilObjectSV,
157             #if HIREDIS_MAJOR > 0
158             createBoolObjectSV,
159             #endif
160             freeReplyObjectSV
161             };
162              
163             static void encodeMessage(pTHX_ SV *target, SV *message_p);
164              
165 3           static void encodeString(pTHX_ SV *target, SV *message_p) {
166 3           HV *const message = (HV*)SvRV(message_p);
167 3           SV **const type_sv = hv_fetchs(message, "type", FALSE);
168 3           SV **const data_sv = hv_fetchs(message, "data", FALSE);
169              
170 3           char *type = SvPV_nolen(*type_sv);
171 3           char *data = SvPV_nolen(*data_sv);
172              
173 3           sv_catpvf(target, "%s%s\r\n", type, data);
174 3           }
175              
176 12           static void encodeBulk(pTHX_ SV *target, SV *message_p) {
177 12           HV *const message = (HV*)SvRV(message_p);
178 12           SV **const data_sv = hv_fetchs(message, "data", FALSE);
179              
180 12 100         if (!SvOK(*data_sv)) {
181 3           sv_catpv(target, "$-1\r\n");
182 3           return;
183             }
184              
185             STRLEN msglen;
186              
187 9           char *data = SvPV(*data_sv, msglen);
188 9           const char term[] = "\r\n";
189             char initmsg[32];
190              
191 9           STRLEN initlen = sprintf( initmsg, "$%lu\r\n", msglen );
192              
193 9           STRLEN targlen = sv_len(target);
194 9 50         SvGROW(target, targlen + initlen + msglen + sizeof(term)-1 + 1);
    100          
195              
196 9           sv_catpvn(target, initmsg, initlen);
197 9           sv_catpvn(target, data, msglen);
198 9           sv_catpvn(target, term, sizeof(term)-1);
199             }
200              
201 6           static void encodeMultiBulk (pTHX_ SV *target, SV *message_p) {
202 6           HV *const message = (HV*)SvRV(message_p);
203 6           SV **const data_sv = hv_fetchs(message, "data", FALSE);
204              
205 6 100         if (!SvOK(*data_sv)) {
206 1           sv_catpv(target, "*-1\r\n");
207 1           return;
208             }
209              
210 5           AV *const data = (AV*)SvRV(*data_sv);
211 5           I32 len = av_len(data);
212 5           sv_catpvf(target, "*%d\r\n", len+1);
213              
214             I32 i;
215 12 100         for (i = 0; i <= len; i++) {
216 7           encodeMessage(aTHX_ target, *av_fetch(data, i, FALSE));
217             }
218             }
219              
220 21           static void encodeMessage(pTHX_ SV *target, SV *message_p) {
221 21           HV *const message = (HV*)SvRV(message_p);
222 21           SV **const type_sv = hv_fetchs(message, "type", FALSE);
223              
224             STRLEN type_len;
225 21           char *type = SvPV(*type_sv, type_len);
226 21           const char op = type[0];
227              
228 21 50         if (1 != type_len || op == '\0' || NULL == strchr("+-:$*", op))
    50          
    50          
229 0           croak("Unknown message type: \"%s\"", type);
230              
231 21           switch (op) {
232 3           case '+':
233             case '-':
234             case ':':
235 3           encodeString(aTHX_ target, message_p);
236 21           return;
237 12           case '$':
238 12           encodeBulk(aTHX_ target, message_p);
239 12           return;
240 6           case '*':
241 6           encodeMultiBulk(aTHX_ target, message_p);
242 6           return;
243             }
244             }
245              
246             MODULE = Protocol::Redis::XS PACKAGE = Protocol::Redis::XS
247             PROTOTYPES: ENABLE
248              
249             void
250             _create(SV *self)
251             PREINIT:
252             redisReader *r;
253             CODE:
254 2           r = redisReaderCreate();
255 2           r->fn = &perlRedisFunctions;
256             SET_THX_REDIS(r);
257 2           xs_object_magic_attach_struct(aTHX_ SvRV(self), r);
258              
259             void
260             DESTROY(redisReader *r)
261             CODE:
262 2           redisReaderFree(r);
263              
264             void
265             parse(SV *self, SV *data)
266             PREINIT:
267             redisReader *r;
268             SV **callback;
269             CODE:
270 42           r = xs_object_magic_get_struct(aTHX_ SvRV(self));
271 42           redisReaderFeed(r, SvPVX(data), SvCUR(data));
272              
273 42           callback = hv_fetchs((HV*)SvRV(self), "_on_message_cb", FALSE);
274 42 50         if (callback && SvOK(*callback)) {
    100          
275             /* There's a callback, do parsing now. */
276             SV *reply;
277             do {
278 8 50         if(redisReaderGetReply(r, (void**)&reply) == REDIS_ERR) {
279 0           croak("%s", r->errstr);
280             }
281              
282 8 100         if (reply) {
283             /* Call the callback */
284 5           dSP;
285 5           ENTER;
286 5           SAVETMPS;
287 5 50         PUSHMARK(SP);
288 5 50         XPUSHs(self);
289 5 50         XPUSHs(reply);
290 5           PUTBACK;
291              
292 5           call_sv(*callback, G_DISCARD);
293 4           sv_2mortal(reply);
294              
295             /* May free reply; we still use the presence of a pointer in the loop
296             * condition below though.
297             */
298 4 50         FREETMPS;
299 4           LEAVE;
300             }
301 7 100         } while(reply != NULL);
302             }
303              
304             SV*
305             get_message(redisReader *r)
306             CODE:
307 33 50         if(redisReaderGetReply(r, (void**)&RETVAL) == REDIS_ERR) {
308 0           croak("%s", r->errstr);
309             }
310 33 100         if(!RETVAL)
311 1           RETVAL = &PL_sv_undef;
312              
313             OUTPUT:
314             RETVAL
315              
316             SV*
317             encode(SV *self, SV *message)
318             CODE:
319 14           RETVAL = sv_2mortal(newSVpvn("", 0));
320 14           encodeMessage(aTHX_ RETVAL, message);
321 14           SvREFCNT_inc(RETVAL);
322             OUTPUT:
323             RETVAL