root/lang/perl/Cache-Memcached-LibMemcached/trunk/perl-libmemcached.c @ 5242

Revision 5242, 22.4 kB (checked in by daisuke, 6 years ago)

lang/perl/Cache-Memcached-LibMemcached?; fix memory leak for get()/mget(). Pointed out by Time Bunce. Tweak cas() implementation

Line 
1/* $Id$
2 *
3 * Copyright (c) 2008 Daisuke Maki <daisuke@endeworks.jp>
4 * All rights reserved
5 */
6
7#ifndef __PERL_LIBMEMCACHED_C__
8#define __PERL_LIBMEMCACHED_C__
9#include "perl-libmemcached.h"
10
11#define ASSERT_CALLBACK(x) \
12    if (! SvOK(x) || ! SvROK(x) || SvTYPE(SvRV(x) ) != SVt_PVCV) { \
13        croak("provided argument is not a coderef!"); \
14    }
15
16#define CONSTSUBi(name, x) \
17    { \
18        SV *sv = newSViv(x); \
19        newCONSTSUB(stash, name, sv); \
20        av_push(export, newSVpv(name, PL_na)); \
21    }
22
23void
24Cache_LibMemcached_bootstrap()
25{
26    HV *stash;
27    AV *export;
28
29    stash = gv_stashpv("Cache::Memcached::LibMemcached::Constants", 1);
30    export = get_av("Cache::Memcached::LibMemcached::Constants::EXPORT_OK", 1);
31
32    CONSTSUBi( "F_STORABLE", F_STORABLE );
33    CONSTSUBi( "F_COMPRESS", F_COMPRESS );
34
35    /* There has to be an easier way, eh? */
36    CONSTSUBi( "MEMCACHED_SUCCESS", MEMCACHED_SUCCESS );
37    CONSTSUBi( "MEMCACHED_FAILURE", MEMCACHED_FAILURE );
38    CONSTSUBi( "MEMCACHED_HOST_LOOKUP_FAILURE", MEMCACHED_HOST_LOOKUP_FAILURE );
39    CONSTSUBi( "MEMCACHED_CONNECTION_BIND_FAILURE", MEMCACHED_CONNECTION_BIND_FAILURE );
40    CONSTSUBi( "MEMCACHED_WRITE_FAILURE", MEMCACHED_WRITE_FAILURE );
41    CONSTSUBi( "MEMCACHED_READ_FAILURE", MEMCACHED_READ_FAILURE );
42    CONSTSUBi( "MEMCACHED_UNKNOWN_READ_FAILURE", MEMCACHED_UNKNOWN_READ_FAILURE );
43    CONSTSUBi( "MEMCACHED_PROTOCOL_ERROR", MEMCACHED_PROTOCOL_ERROR );
44    CONSTSUBi( "MEMCACHED_CLIENT_ERROR", MEMCACHED_CLIENT_ERROR );
45    CONSTSUBi( "MEMCACHED_SERVER_ERROR", MEMCACHED_SERVER_ERROR );
46    CONSTSUBi( "MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE", MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE );
47    CONSTSUBi( "MEMCACHED_DATA_EXISTS", MEMCACHED_DATA_EXISTS );
48    CONSTSUBi( "MEMCACHED_DATA_DOES_NOT_EXIST", MEMCACHED_DATA_DOES_NOT_EXIST );
49    CONSTSUBi( "MEMCACHED_NOTSTORED", MEMCACHED_NOTSTORED );
50    CONSTSUBi( "MEMCACHED_STORED", MEMCACHED_STORED );
51    CONSTSUBi( "MEMCACHED_NOTFOUND", MEMCACHED_NOTFOUND );
52    CONSTSUBi( "MEMCACHED_MEMORY_ALLOCATION_FAILURE", MEMCACHED_MEMORY_ALLOCATION_FAILURE );
53    CONSTSUBi( "MEMCACHED_PARTIAL_READ", MEMCACHED_PARTIAL_READ );
54    CONSTSUBi( "MEMCACHED_SOME_ERRORS", MEMCACHED_SOME_ERRORS );
55    CONSTSUBi( "MEMCACHED_NO_SERVERS", MEMCACHED_NO_SERVERS );
56    CONSTSUBi( "MEMCACHED_END", MEMCACHED_END );
57    CONSTSUBi( "MEMCACHED_DELETED", MEMCACHED_DELETED );
58    CONSTSUBi( "MEMCACHED_VALUE", MEMCACHED_VALUE );
59    CONSTSUBi( "MEMCACHED_STAT", MEMCACHED_STAT );
60    CONSTSUBi( "MEMCACHED_ERRNO", MEMCACHED_ERRNO );
61    CONSTSUBi( "MEMCACHED_FAIL_UNIX_SOCKET", MEMCACHED_FAIL_UNIX_SOCKET );
62    CONSTSUBi( "MEMCACHED_NOT_SUPPORTED", MEMCACHED_NOT_SUPPORTED );
63    CONSTSUBi( "MEMCACHED_NO_KEY_PROVIDED", MEMCACHED_NO_KEY_PROVIDED );
64    CONSTSUBi( "MEMCACHED_FETCH_NOTFINISHED", MEMCACHED_FETCH_NOTFINISHED );
65    CONSTSUBi( "MEMCACHED_TIMEOUT", MEMCACHED_TIMEOUT );
66    CONSTSUBi( "MEMCACHED_MAXIMUM_RETURN", MEMCACHED_MAXIMUM_RETURN );
67
68/*
69    CONSTSUBi( "MEMCACHED_BEHAVIOR_NO_BLOCK", MEMCACHED_BEHAVIOR_NO_BLOCK );
70  MEMCACHED_BEHAVIOR_TCP_NODELAY,
71  MEMCACHED_BEHAVIOR_HASH,
72  MEMCACHED_BEHAVIOR_KETAMA,
73  MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE,
74  MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE,
75  MEMCACHED_BEHAVIOR_CACHE_LOOKUPS,
76  MEMCACHED_BEHAVIOR_SUPPORT_CAS,
77  MEMCACHED_BEHAVIOR_POLL_TIMEOUT,
78  MEMCACHED_BEHAVIOR_DISTRIBUTION,
79*/
80
81    CONSTSUBi( "MEMCACHED_DISTRIBUTION_MODULA", MEMCACHED_DISTRIBUTION_MODULA );
82    CONSTSUBi( "MEMCACHED_DISTRIBUTION_CONSISTENT", MEMCACHED_DISTRIBUTION_CONSISTENT );
83
84    CONSTSUBi( "MEMCACHED_HASH_DEFAULT", MEMCACHED_HASH_DEFAULT );
85    CONSTSUBi( "MEMCACHED_HASH_MD5", MEMCACHED_HASH_MD5 );
86    CONSTSUBi( "MEMCACHED_HASH_CRC", MEMCACHED_HASH_CRC );
87    CONSTSUBi( "MEMCACHED_HASH_FNV1_64", MEMCACHED_HASH_FNV1_64 );
88    CONSTSUBi( "MEMCACHED_HASH_FNV1A_64", MEMCACHED_HASH_FNV1A_64 );
89    CONSTSUBi( "MEMCACHED_HASH_FNV1_32", MEMCACHED_HASH_FNV1_32 );
90    CONSTSUBi( "MEMCACHED_HASH_FNV1A_32", MEMCACHED_HASH_FNV1A_32 );
91    CONSTSUBi( "MEMCACHED_HASH_KETAMA", MEMCACHED_HASH_KETAMA );
92    CONSTSUBi( "MEMCACHED_HASH_HSIEH", MEMCACHED_HASH_HSIEH );
93}
94
95/* The next functions (compress, uncompress, freeze, thaw) are implemented
96 * as simple static method calls emulating a Perl method calls to
97 * Compress::Zlib and Storable, respectively
98 */
99
100inline static SV *
101Cache_LibMemcached_uncompress(cache, raw_value, value_len, flags)
102        Cache_LibMemcached *cache;
103        char *raw_value;
104        size_t value_len;
105        uint16_t flags;
106{
107    SV *sv;
108
109    if (! MEMCACHED_HAVE_ZLIB(cache) || ! (flags & F_COMPRESS)) {
110        /* no compression, just create an sv */
111        sv = newSVpv(raw_value, value_len);
112    } else {
113        dSP;
114
115        ENTER;
116        SAVETMPS;
117
118        PUSHMARK(SP);
119        EXTEND(SP, 1);
120        PUSHs(sv_2mortal(newSVpv(raw_value, value_len)));
121        PUTBACK;
122
123        if (call_sv(MEMCACHED_UNCOMPRESS_METHOD(cache), G_SCALAR) <= 0) {
124            croak("uncompress_method did not return a proper value");
125        }
126        SPAGAIN;
127
128        sv = POPs;
129        SvREFCNT_inc(sv);
130
131        FREETMPS;
132        LEAVE;
133    }
134    return sv;
135}
136
137inline static void
138Cache_LibMemcached_thaw(cache, sv, flags)
139        Cache_LibMemcached *cache;
140        SV *sv;
141        uint16_t flags;
142{
143    if (flags & F_STORABLE) {
144        SV *rv;
145        dSP;
146        ENTER;
147        SAVETMPS;
148
149        PUSHMARK(SP);
150        EXTEND(SP, 1);
151        PUSHs(sv);
152        PUTBACK;
153
154        if (call_sv(MEMCACHED_DESERIALIZE_METHOD(cache), G_SCALAR) <= 0) {
155            croak("deserialize_method did not return a proper value");
156        }
157        SPAGAIN;
158
159        rv = POPs;
160       
161        SvSetSV(sv, rv);
162
163        FREETMPS;
164        LEAVE;
165    }
166}
167
168inline static void
169Cache_LibMemcached_freeze(cache, sv, flags)
170        Cache_LibMemcached *cache;
171        SV *sv;
172        uint16_t *flags;
173{
174    if (SvROK(sv)) {
175        SV *rv;
176        dSP;
177        ENTER;
178        SAVETMPS;
179
180        PUSHMARK(SP);
181        EXTEND(SP, 1);
182        PUSHs(sv);
183        PUTBACK;
184
185        if (call_sv(MEMCACHED_SERIALIZE_METHOD(cache), G_SCALAR) <= 0) {
186            croak("serialize_method did not return a proper value");
187        }
188        SPAGAIN;
189
190        rv = POPs;
191       
192        SvSetSV(sv, rv);
193
194        FREETMPS;
195        LEAVE;
196
197        *flags |= F_STORABLE;
198    }
199}
200
201inline static void
202Cache_LibMemcached_compress(cache, sv, flags)
203        Cache_LibMemcached *cache;
204        SV *sv;
205        uint16_t *flags;
206{
207    size_t compress_threshold;
208    bool   compress_enabled;
209    size_t sv_length = sv_len(sv);
210
211    compress_threshold = MEMCACHED_COMPRESS_THRESHOLD(cache);
212    if (MEMCACHED_HAVE_ZLIB(cache) &&
213        MEMCACHED_COMPRESS_ENABLED(cache) &&
214        compress_threshold > 0 &&
215        sv_length > compress_threshold
216    ) {
217        SV *rv;
218        size_t rv_length;
219        dSP;
220
221        ENTER;
222        SAVETMPS;
223
224        PUSHMARK(SP);
225        EXTEND(SP, 1);
226        PUSHs(sv);
227        PUTBACK;
228
229        if (call_sv(MEMCACHED_COMPRESS_METHOD(cache), G_SCALAR) <= 0) {
230            croak("compress_method did not return a proper value");
231        }
232        SPAGAIN;
233
234        rv = POPs;
235        rv_length = SvCUR(rv);
236
237        /* We won't reset the contents of the original SV unless
238         * the savings are actually greater than the savings threshold
239         */
240        if (rv_length < sv_length * ( 1 - MEMCACHED_COMPRESS_SAVINGS(cache) ) ) {
241            SvSetSV(sv, rv);
242            *flags |= F_COMPRESS;
243        }
244        FREETMPS;
245        LEAVE;
246
247    }
248}
249
250SV *
251Cache_LibMemcached_create(pkg, have_zlib, compress_enabled, compress_threshold, compress_savings, compress_method, uncompress_method, serialize_method, deserialize_method)
252        char *pkg;
253        bool have_zlib;
254        bool compress_enabled;
255        size_t compress_threshold;
256        float compress_savings;
257        SV *compress_method;
258        SV *uncompress_method;
259        SV *serialize_method;
260        SV *deserialize_method;
261{
262    SV *sv;
263    Cache_LibMemcached *xs;
264    memcached_st *cache;
265
266    Newxz(xs, 1, Cache_LibMemcached);
267
268    MEMCACHED_CACHE(xs)              = memcached_create(NULL);
269    MEMCACHED_HAVE_ZLIB(xs)          = have_zlib;
270    MEMCACHED_COMPRESS_ENABLED(xs)   = compress_enabled;
271    MEMCACHED_COMPRESS_THRESHOLD(xs) = compress_threshold;
272    MEMCACHED_COMPRESS_SAVINGS(xs)   = compress_savings;
273    MEMCACHED_COMPRESS_METHOD(xs)    = compress_method;
274    MEMCACHED_UNCOMPRESS_METHOD(xs)  = uncompress_method;
275    MEMCACHED_SERIALIZE_METHOD(xs)   = serialize_method;
276    MEMCACHED_DESERIALIZE_METHOD(xs) = deserialize_method;
277
278    SvREFCNT_inc(compress_method);
279    SvREFCNT_inc(uncompress_method);
280    SvREFCNT_inc(serialize_method);
281    SvREFCNT_inc(deserialize_method);
282    ASSERT_CALLBACK( MEMCACHED_SERIALIZE_METHOD(xs) );
283
284    XS_STRUCT2OBJ(sv, "Cache::Memcached::LibMemcached", xs);
285
286    return sv;
287}
288
289void
290Cache_LibMemcached_DESTROY(cache)
291        Cache_LibMemcached *cache;
292{
293    memcached_quit(MEMCACHED_CACHE(cache));
294    memcached_free(MEMCACHED_CACHE(cache));
295    SvREFCNT_dec(MEMCACHED_COMPRESS_METHOD(cache));
296    SvREFCNT_dec(MEMCACHED_UNCOMPRESS_METHOD(cache));
297    SvREFCNT_dec(MEMCACHED_DESERIALIZE_METHOD(cache));
298    SvREFCNT_dec(MEMCACHED_SERIALIZE_METHOD(cache));
299}
300
301Cache_LibMemcached_rc
302Cache_LibMemcached_server_add(cache, hostname, port)
303        Cache_LibMemcached *cache;
304        char *hostname;
305        unsigned int port;
306{
307    return memcached_server_add(MEMCACHED_CACHE(cache), hostname, port);
308}
309
310Cache_LibMemcached_rc
311Cache_LibMemcached_server_add_unix_socket(cache, filename)
312        Cache_LibMemcached *cache;
313        char *filename;
314{
315    return memcached_server_add_unix_socket(MEMCACHED_CACHE(cache), filename);
316}
317
318unsigned int
319Cache_LibMemcached_server_list_count(cache)
320        Cache_LibMemcached *cache;
321{
322    return memcached_server_list_count( MEMCACHED_CACHE(cache)->hosts );
323}
324
325void
326Cache_LibMemcached_server_list_free(cache)
327        Cache_LibMemcached *cache;
328{
329    memcached_server_list_free( MEMCACHED_CACHE(cache)->hosts );
330}
331
332SV *
333Cache_LibMemcached_get(cache, key)
334        Cache_LibMemcached *cache;
335        SV *key;
336{
337    STRLEN key_len;
338    size_t value_len = 0;
339    char *key_char, *value;
340    Cache_LibMemcached_rc rc;
341    uint16_t flags;
342    SV *sv;
343
344    key_char = SvPVbyte(key, key_len);
345   
346    value = memcached_get(MEMCACHED_CACHE(cache), key_char, key_len, &value_len, &flags, &rc);
347    if (!value || rc != MEMCACHED_SUCCESS) {
348        return &PL_sv_undef;
349    }
350
351    sv = Cache_LibMemcached_uncompress( cache, value, value_len, flags );
352    Cache_LibMemcached_thaw( cache, sv, flags );
353    free(value);
354
355    return sv;
356}
357
358Cache_LibMemcached_rc
359Cache_LibMemcached_set(cache, key, value, expires)
360        Cache_LibMemcached *cache;
361        SV *key;
362        SV *value;
363        time_t expires;
364{
365    STRLEN key_len, value_len;
366    char *key_char;
367    char *value_char;
368    uint16_t flags = 0;
369    SV *value_copy;
370
371    value_copy = newSVsv(value);
372
373    Cache_LibMemcached_freeze(cache, value_copy, &flags);
374    Cache_LibMemcached_compress(cache, value_copy, &flags);
375
376    key_char = SvPVbyte(key, key_len);
377    value_char = SvPVbyte(value_copy, value_len);
378
379    return memcached_set(MEMCACHED_CACHE(cache), key_char, key_len, value_char, value_len, expires, flags);
380}
381
382Cache_LibMemcached_rc
383Cache_LibMemcached_cas(cache, key, cas, value, expires)
384        Cache_LibMemcached *cache;
385        SV *key;
386        UV cas;
387        SV *value;
388        time_t expires;
389{
390    STRLEN key_len, value_len;
391    char *key_char;
392    char *value_char;
393    uint16_t flags = 0;
394    SV *value_copy;
395
396    value_copy = newSVsv(value);
397
398    Cache_LibMemcached_freeze(cache, value_copy, &flags);
399    Cache_LibMemcached_compress(cache, value_copy, &flags);
400
401    key_char = SvPVbyte(key, key_len);
402    value_char = SvPVbyte(value_copy, value_len);
403
404    return memcached_cas(MEMCACHED_CACHE(cache), key_char, key_len, value_char, value_len, expires, flags, (uint64_t) cas);
405}
406
407Cache_LibMemcached_rc
408Cache_LibMemcached_add(cache, key, value, expires)
409        Cache_LibMemcached *cache;
410        SV *key;
411        SV *value;
412        time_t expires;
413{
414    STRLEN key_len, value_len;
415    char *key_char;
416    char *value_char;
417    uint16_t flags = 0;
418    SV *value_copy;
419
420    value_copy = newSVsv(value);
421
422    Cache_LibMemcached_freeze(cache, value_copy, &flags);
423    Cache_LibMemcached_compress(cache, value_copy, &flags);
424
425    key_char = SvPVbyte(key, key_len);
426    value_char = SvPVbyte(value_copy, value_len);
427
428    return memcached_add(MEMCACHED_CACHE(cache), key_char, key_len, value_char, value_len, expires, flags);
429}
430
431Cache_LibMemcached_rc
432Cache_LibMemcached_replace(cache, key, value, expires)
433        Cache_LibMemcached *cache;
434        SV *key;
435        SV *value;
436        time_t expires;
437{
438    STRLEN key_len, value_len;
439    char *key_char;
440    char *value_char;
441    uint16_t flags = 0;
442    SV *value_copy;
443
444    value_copy = newSVsv(value);
445
446    Cache_LibMemcached_freeze(cache, value_copy, &flags);
447    Cache_LibMemcached_compress(cache, value_copy, &flags);
448
449    key_char = SvPVbyte(key, key_len);
450    value_char = SvPVbyte(value_copy, value_len);
451
452    return memcached_replace(MEMCACHED_CACHE(cache), key_char, key_len, value_char, value_len, expires, flags);
453}
454
455/* XXX - Notes from memcached spec.
456 *
457 * "append" means "add this data to an existing key after existing data".
458 *
459 * "prepend" means "add this data to an existing key before existing data".
460 *
461 * The append and prepend commands do not accept flags or exptime.
462 * They update existing data portions, and ignore new flag and exptime
463 * settings.
464 */
465Cache_LibMemcached_rc
466Cache_LibMemcached_prepend(cache, key_sv, value_sv)
467        Cache_LibMemcached *cache;
468        SV *key_sv;
469        SV *value_sv;
470{
471    char *key, *value;
472    STRLEN key_length, value_length;
473
474    key = SvPV(key_sv, key_length);
475    value = SvPV(value_sv, value_length);
476
477    /* While libmemcached's API supports expiration and flags, the
478     * operation as defined in the spec doesn't, so we're sending
479     * 0 for these
480     */
481    return memcached_prepend(
482        MEMCACHED_CACHE(cache), key, key_length, value, value_length,
483        (time_t) 0, (uint16_t) 0);
484}
485
486Cache_LibMemcached_rc
487Cache_LibMemcached_append(cache, key_sv, value_sv)
488        Cache_LibMemcached *cache;
489        SV *key_sv;
490        SV *value_sv;
491{
492    char *key, *value;
493    STRLEN key_length, value_length;
494
495    key = SvPV(key_sv, key_length);
496    value = SvPV(value_sv, value_length);
497
498    /* While libmemcached's API supports expiration and flags, the
499     * operation as defined in the spec doesn't, so we're sending
500     * 0 for these
501     */
502    return memcached_append(
503        MEMCACHED_CACHE(cache), key, key_length, value, value_length,
504        (time_t) 0, (uint16_t) 0);
505}
506
507SV *
508Cache_LibMemcached_get_cas_multi(cache, keys, key_len_list, keys_len)
509        Cache_LibMemcached *cache;
510        char **keys;
511        size_t *key_len_list;
512        unsigned int keys_len;
513{
514    Cache_LibMemcached_rc rc;
515    memcached_result_st *result;
516    HV *hv;
517    unsigned int i;
518
519    rc = memcached_mget(MEMCACHED_CACHE(cache), keys, key_len_list, keys_len);
520    if (rc != MEMCACHED_SUCCESS) {
521        memcached_quit(MEMCACHED_CACHE(cache));
522        croak("memcached_mget failed :(");
523    }
524
525    hv = newHV();
526    result = memcached_result_create(MEMCACHED_CACHE(cache), NULL);
527    for(i = 0; i < keys_len; i++) {
528        SV *key, *value;
529
530        memcached_fetch_result(MEMCACHED_CACHE(cache), result, &rc);
531        if (rc != MEMCACHED_SUCCESS) {
532            break;
533        }
534
535        key = newSVpv(
536            memcached_result_key_value(result),
537            memcached_result_key_length(result)
538        );
539        value = newSVnv( memcached_result_cas(result) );
540        SvREFCNT_inc(value);
541        if (hv_store_ent(hv, key, value, 0) == NULL) {
542            croak("Failed to insert into hash");
543        }
544    }
545    memcached_result_free(result);
546    return newRV_noinc((SV *) hv);
547}
548
549SV *
550Cache_LibMemcached_mget(cache, keys, key_len_list, keys_len)
551        Cache_LibMemcached *cache;
552        char **keys;
553        size_t *key_len_list;
554        unsigned int keys_len;
555{
556    Cache_LibMemcached_rc rc;
557    HV *hv;
558    unsigned int i;
559
560    rc = memcached_mget(MEMCACHED_CACHE(cache), keys, key_len_list, keys_len);
561    if (rc != MEMCACHED_SUCCESS) {
562        memcached_quit(MEMCACHED_CACHE(cache));
563        croak("memcached_mget failed :(");
564    }
565
566    hv = newHV();
567    for(i = 0; i < keys_len; i++) {
568        char *value = NULL;
569        char key[MEMCACHED_MAX_KEY];
570        size_t key_length = 0;
571        size_t value_length = 0;
572        uint16_t flags = 0;
573        Cache_LibMemcached_rc rc;
574        SV *sv;
575
576        value = memcached_fetch(MEMCACHED_CACHE(cache), key, &key_length, &value_length, &flags, &rc);
577        if (value == NULL) {
578            break;
579        }
580
581        sv = Cache_LibMemcached_uncompress( cache, value, value_length, flags );
582        Cache_LibMemcached_thaw( cache, sv, flags );
583        free(value);
584
585        SvREFCNT_inc(sv);
586        if (hv_store_ent(hv, newSVpv(key, key_length), sv, 0) == NULL) {
587            croak("Failed to insert into hash");
588        }
589    }
590
591    return newRV_noinc((SV *) hv);
592}
593
594SV *
595Cache_LibMemcached_delete(cache, key_sv, expiration)
596        Cache_LibMemcached *cache;
597        SV *key_sv;
598        time_t expiration;
599{
600    char *key;
601    size_t key_len;
602    Cache_LibMemcached_rc rc;
603
604    key = SvPVbyte(key_sv, key_len);
605
606    rc = memcached_delete(MEMCACHED_CACHE(cache), key, key_len, expiration);
607    return (rc == MEMCACHED_SUCCESS) ?
608        &PL_sv_yes : &PL_sv_no
609    ;
610}
611
612SV *
613Cache_LibMemcached_incr(cache, key_sv, offset)
614        Cache_LibMemcached *cache;
615        SV *key_sv;
616        unsigned int offset;
617{
618    char *key;
619    size_t key_len;
620    uint64_t value;
621    Cache_LibMemcached_rc rc;
622
623    key = SvPVbyte(key_sv, key_len);
624
625    rc = memcached_increment(MEMCACHED_CACHE(cache), key, key_len, offset, &value);
626
627    if (rc != MEMCACHED_SUCCESS) {
628        return &PL_sv_undef;
629    }
630
631    return newSViv(value);
632}
633
634SV *
635Cache_LibMemcached_decr(cache, key_sv, offset)
636        Cache_LibMemcached *cache;
637        SV *key_sv;
638        unsigned int offset;
639{
640    char *key;
641    size_t key_len;
642    uint64_t value;
643    Cache_LibMemcached_rc rc;
644
645    key = SvPVbyte(key_sv, key_len);
646
647    rc = memcached_decrement(MEMCACHED_CACHE(cache), key, key_len, offset, &value);
648
649    if (rc != MEMCACHED_SUCCESS) {
650        return &PL_sv_undef;
651    }
652
653    return newSViv(value);
654}
655
656Cache_LibMemcached_rc
657Cache_LibMemcached_flush_all(cache, expiration)
658        Cache_LibMemcached *cache;
659        time_t expiration;
660{
661    return memcached_flush( MEMCACHED_CACHE(cache), expiration );
662}
663
664void
665Cache_LibMemcached_set_support_cas(cache, value)
666        Cache_LibMemcached *cache;
667        IV value;
668{
669    memcached_behavior_set( MEMCACHED_CACHE(cache),
670        MEMCACHED_BEHAVIOR_SUPPORT_CAS, (void *) value );
671}
672
673void
674Cache_LibMemcached_set_no_block(cache, value)
675        Cache_LibMemcached *cache;
676        IV value;
677{
678    memcached_behavior_set( MEMCACHED_CACHE(cache),
679        MEMCACHED_BEHAVIOR_NO_BLOCK, (void *) value );
680}
681
682IV
683Cache_LibMemcached_is_no_block(cache)
684        Cache_LibMemcached *cache;
685{
686    return memcached_behavior_get( MEMCACHED_CACHE(cache),
687        MEMCACHED_BEHAVIOR_NO_BLOCK );
688}
689
690void
691Cache_LibMemcached_set_distribution_method(cache, value)
692        Cache_LibMemcached *cache;
693        IV value;
694{
695    memcached_behavior_set( MEMCACHED_CACHE(cache),
696        MEMCACHED_BEHAVIOR_DISTRIBUTION, &value );
697}
698
699IV
700Cache_LibMemcached_get_distribution_method(cache)
701        Cache_LibMemcached *cache;
702{
703    return memcached_behavior_get( MEMCACHED_CACHE(cache),
704        MEMCACHED_BEHAVIOR_DISTRIBUTION );
705}
706
707void
708Cache_LibMemcached_set_hashing_algorithm(cache, value)
709        Cache_LibMemcached *cache;
710        IV value;
711{
712    memcached_behavior_set( MEMCACHED_CACHE(cache),
713        MEMCACHED_BEHAVIOR_HASH, &value );
714}
715
716IV
717Cache_LibMemcached_get_hashing_algorithm(cache)
718        Cache_LibMemcached *cache;
719{
720    return memcached_behavior_get( MEMCACHED_CACHE(cache),
721        MEMCACHED_BEHAVIOR_HASH );
722}
723
724Cache_LibMemcached_rc
725Cache_LibMemcached_populate_stathv(cache, hv, stat)
726        Cache_LibMemcached *cache;
727        HV *hv;
728        Cache_LibMemcached_stat stat;
729{
730    Cache_LibMemcached_rc rc;
731    char **list;
732    char **ptr;
733
734    list = memcached_stat_get_keys(MEMCACHED_CACHE(cache), &stat, &rc);
735    if (rc != MEMCACHED_SUCCESS)
736        return rc;
737
738    for(ptr = list; *ptr; ptr++) {
739        SV *key, *sv;
740        char *value;
741
742        value = memcached_stat_get_value(
743            MEMCACHED_CACHE(cache),
744            &stat,
745            *ptr,
746            &rc
747        );
748        if (rc != MEMCACHED_SUCCESS) {
749            free(value);
750            break;
751        }
752        key = newSVpvf("%s", *ptr);
753        sv  = newSVpvf("%s", value);
754        if (hv_store_ent(hv, key, sv, 0) == NULL) {
755            /* croak("Failed to insert into hash"); */
756            free(value);
757            break;
758        }
759        free(value);
760    }
761    free(list);
762
763    return rc;
764}
765       
766
767/* This is so half-baked, it's not even funny */
768SV *
769Cache_LibMemcached_stats(cache)
770        Cache_LibMemcached *cache;
771{
772    Cache_LibMemcached_rc   rc;
773    Cache_LibMemcached_stat *stat;
774    memcached_server_st *server_list;
775    unsigned int i;
776    unsigned int server_count;
777    HV *hv, *hosts_hv, *single_host_hv;
778
779    stat = memcached_stat(MEMCACHED_CACHE(cache), NULL, &rc);
780    if (rc != MEMCACHED_SUCCESS)
781        croak("memcached_stat failed: %s", memcached_strerror(MEMCACHED_CACHE(cache), rc));
782
783    /* need a structure like
784     *   { hosts => {
785     *      "server:port" => {
786     *          key => value
787     *      }
788     *    }
789     */
790
791    hosts_hv = newHV();
792    server_list  = memcached_server_list(MEMCACHED_CACHE(cache));
793    server_count = memcached_server_count(MEMCACHED_CACHE(cache));
794    for (i = 0; i < server_count; i++) {
795        SV *key;
796        single_host_hv = newHV();
797
798        rc = Cache_LibMemcached_populate_stathv(cache, single_host_hv, stat[i]);
799        if (rc != MEMCACHED_SUCCESS)
800            break;
801
802        key = newSVpvf("%s:%u",
803            memcached_server_name(cache, server_list[i]),
804            memcached_server_port(cache, server_list[i])
805        );
806        if (hv_store_ent(hosts_hv, key, newRV_inc((SV *) single_host_hv), 0) == NULL) {
807            /* croak("Failed to insert into hash"); */
808            break;
809        }
810    }
811    free(stat);
812   
813    hv = newHV(); /* top level hash */
814    if (hv_store_ent(hv, newSVpv("hosts", 5), newRV_inc((SV *) hosts_hv), 0) == NULL) {
815        croak("Failed insert into hash");
816    }
817    return newRV_inc((SV *) hv);
818}
819
820void
821Cache_LibMemcached_disconnect_all(cache)
822        Cache_LibMemcached *cache;
823{
824    memcached_quit(MEMCACHED_CACHE(cache));
825}
826
827#endif /* __PERL_LIBMEMCACHED_C__ */
Note: See TracBrowser for help on using the browser.