File Coverage

XS.xs
Criterion Covered Total %
statement 102 110 92.7
branch 35 54 64.8
condition n/a
subroutine n/a
pod n/a
total 137 164 83.5


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             };
35              
36 52           static SV *createReply(pTHX_ SV *sv, int type)
37             {
38 52           char reply_type = redisTypes[type];
39 52           HV *reply = newHV();
40              
41 52           hv_stores(reply, "type", newSVpvn(&reply_type, sizeof reply_type));
42 52           hv_stores(reply, "data", sv);
43 52           return newRV_noinc((SV*)reply);
44             }
45              
46 0           static void freeReplyObjectSV(void *reply) {
47             dTHX;
48 0           SV* r = reply;
49 0           sv_2mortal(r);
50 0           }
51              
52 52           static inline void storeParent(pTHX_ const redisReadTask *task, SV *reply)
53             {
54 52 100         if (task->parent) {
55 18           SV *const obj = task->parent->obj;
56 18           HV *const parent = (HV*)SvRV(obj);
57 18           SV **const data = hv_fetchs(parent, "data", FALSE);
58             assert(data && SvTYPE(SvRV(*data)) == SVt_PVAV);
59 18           av_store((AV*)SvRV(*data), task->idx, reply);
60             }
61 52           }
62              
63 35           static void *createStringObjectSV(const redisReadTask *task, char *str,
64             size_t len)
65             {
66             dTHXREDIS(task);
67              
68 35           SV *const reply = createReply(aTHX_ newSVpvn(str, len), task->type);
69 35           storeParent(aTHX_ task, reply);
70 35           return reply;
71             }
72              
73 9           static void *createArrayObjectSV(const redisReadTask *task, int elements)
74             {
75             dTHXREDIS(task);
76              
77 9           AV *av = newAV();
78 9           SV *const reply = createReply(aTHX_ newRV_noinc((SV*)av), task->type);
79 9           av_extend(av, elements);
80 9           storeParent(aTHX_ task, reply);
81 9           return reply;
82             }
83              
84 3           static void *createIntegerObjectSV(const redisReadTask *task, long long value)
85             {
86             dTHXREDIS(task);
87             /* Not pretty, but perl doesn't always have a sane way to store long long in
88             * a SV.
89             */
90             #if defined(LONGLONGSIZE) && LONGLONGSIZE == IVSIZE
91 3           SV *sv = newSViv(value);
92             #else
93             SV *sv = newSVnv(value);
94             #endif
95              
96 3           SV *reply = createReply(aTHX_ sv, task->type);
97 3           storeParent(aTHX_ task, reply);
98 3           return reply;
99             }
100              
101 5           static void *createNilObjectSV(const redisReadTask *task)
102             {
103             dTHXREDIS(task);
104              
105 5           SV *reply = createReply(aTHX_ &PL_sv_undef, task->type);
106 5           storeParent(aTHX_ task, reply);
107 5           return reply;
108             }
109              
110             /* Declarations below are used in the XS section */
111              
112             static redisReplyObjectFunctions perlRedisFunctions = {
113             createStringObjectSV,
114             createArrayObjectSV,
115             createIntegerObjectSV,
116             createNilObjectSV,
117             freeReplyObjectSV
118             };
119              
120             static SV *encodeMessage(pTHX_ SV *message_p);
121              
122 3           static SV *encodeString(pTHX_ SV *message_p) {
123 3           HV *const message = (HV*)SvRV(message_p);
124 3           SV **const type_sv = hv_fetchs(message, "type", FALSE);
125 3           SV **const data_sv = hv_fetchs(message, "data", FALSE);
126              
127 3 50         char *type = SvPV_nolen(*type_sv);
128 3 50         char *data = SvPV_nolen(*data_sv);
129              
130 3           return newSVpvf("%s%s\r\n", type, data);
131             };
132              
133 12           static SV *encodeBulk(pTHX_ SV *message_p) {
134 12           HV *const message = (HV*)SvRV(message_p);
135 12           SV **const data_sv = hv_fetchs(message, "data", FALSE);
136              
137 12 100         if (!SvOK(*data_sv))
    50          
    50          
138 3           return newSVpvf("$-1\r\n");
139              
140             STRLEN msglen;
141              
142 9 50         char *data = SvPV(*data_sv, msglen);
143 9           const char term[] = "\r\n";
144             char initmsg[32];
145              
146 9           STRLEN initlen = sprintf( initmsg, "$%lu\r\n", msglen );
147              
148 9           SV* resp_sv = newSV(initlen + msglen + sizeof(term)-1);
149 9           SvPOK_on(resp_sv);
150 9           char *buildstr = SvPVX(resp_sv);
151              
152 9           Copy(initmsg, buildstr, initlen, char);
153 9           Copy(data, buildstr + initlen, msglen, char);
154 9           Copy(term, buildstr + initlen + msglen, sizeof(term)-1, char);
155              
156 9           SvCUR_set(resp_sv, initlen + msglen + sizeof(term)-1);
157              
158 12           return resp_sv;
159             };
160              
161 6           static SV *encodeMultiBulk (pTHX_ SV *message_p) {
162 6           HV *const message = (HV*)SvRV(message_p);
163 6           SV **const data_sv = hv_fetchs(message, "data", FALSE);
164              
165 6 100         if (!SvOK(*data_sv))
    50          
    50          
166 1           return newSVpv("*-1\r\n", 0);
167              
168 5           AV *const data = (AV*)SvRV(*data_sv);
169 5           I32 len = av_len(data);
170 5           SV *r = newSVpvf("*%d\r\n", len+1);
171              
172             I32 i;
173 12 100         for (i = 0; i <= len; i++) {
174 7           sv_catsv(r, encodeMessage(aTHX_ *av_fetch(data, i, FALSE)));
175             };
176              
177 5           return r;
178             }
179              
180 21           static SV *encodeMessage(pTHX_ SV *message_p) {
181 21           HV *const message = (HV*)SvRV(message_p);
182 21           SV **const type_sv = hv_fetchs(message, "type", FALSE);
183              
184 21 50         char *type = SvPV_nolen(*type_sv);
185 21           const char op = type[0];
186              
187 21 50         if (1 != strlen(type) || NULL == strchr("+-:$*", op))
    50          
188 0           croak("Unknown message type: \"%s\"", type);
189              
190 21           switch (op) {
191             case '+':
192             case '-':
193             case ':':
194 3           return encodeString(aTHX_ message_p);
195             case '$':
196 12           return encodeBulk(aTHX_ message_p);
197             case '*':
198 6           return encodeMultiBulk(aTHX_ message_p);
199             }
200 0           }
201              
202             MODULE = Protocol::Redis::XS PACKAGE = Protocol::Redis::XS
203             PROTOTYPES: ENABLE
204              
205             void
206             _create(SV *self)
207             PREINIT:
208             redisReader *r;
209             CODE:
210 2           r = redisReaderCreate();
211 2           r->fn = &perlRedisFunctions;
212             SET_THX_REDIS(r);
213 2           xs_object_magic_attach_struct(aTHX_ SvRV(self), r);
214              
215             void
216             DESTROY(redisReader *r)
217             CODE:
218 2           redisReaderFree(r);
219              
220             void
221             parse(SV *self, SV *data)
222             PREINIT:
223             redisReader *r;
224             SV **callback;
225             CODE:
226 39           r = xs_object_magic_get_struct(aTHX_ SvRV(self));
227 39           redisReaderFeed(r, SvPVX(data), SvCUR(data));
228              
229 39           callback = hv_fetchs((HV*)SvRV(self), "_on_message_cb", FALSE);
230 39 50         if (callback && SvOK(*callback)) {
    100          
    50          
    50          
231             /* There's a callback, do parsing now. */
232             SV *reply;
233             do {
234 8 50         if(redisReaderGetReply(r, (void**)&reply) == REDIS_ERR) {
235 0           croak("%s", r->errstr);
236             }
237              
238 8 100         if (reply) {
239             /* Call the callback */
240 5           dSP;
241 5           ENTER;
242 5           SAVETMPS;
243 5 50         PUSHMARK(SP);
244 5 50         XPUSHs(self);
245 5 50         XPUSHs(reply);
246 5           PUTBACK;
247              
248 5           call_sv(*callback, G_DISCARD);
249 4           sv_2mortal(reply);
250              
251             /* May free reply; we still use the presence of a pointer in the loop
252             * condition below though.
253             */
254 4 50         FREETMPS;
255 4           LEAVE;
256             }
257 7 100         } while(reply != NULL);
258             }
259              
260             SV*
261             get_message(redisReader *r)
262             CODE:
263 30 50         if(redisReaderGetReply(r, (void**)&RETVAL) == REDIS_ERR) {
264 0           croak("%s", r->errstr);
265             }
266 30 100         if(!RETVAL)
267 1           RETVAL = &PL_sv_undef;
268              
269             OUTPUT:
270             RETVAL
271              
272             SV*
273             encode(SV *self, SV *message)
274             CODE:
275 14           RETVAL = encodeMessage(aTHX_ message);
276             OUTPUT:
277             RETVAL