Annotation of parser3/src/types/pa_vmemcached.C, revision 1.29

1.1       moko        1: /** @file
                      2:        Parser: memcached class.
                      3: 
1.26      moko        4:        Copyright (c) 2001-2026 Art. Lebedev Studio (https://www.artlebedev.com)
1.1       moko        5:        Authors:
                      6:                Ivan Poluyanov <ivan-poluyanov@yandex.ru>
                      7:                Artem Stepanov <timofei1394@thesecure.in>
                      8: */
                      9: 
                     10: #include "pa_vmemcached.h"
                     11: #include "pa_value.h"
                     12: #include "pa_vstring.h"
1.2       moko       13: #include "pa_vhash.h"
1.1       moko       14: #include "pa_vvoid.h"
                     15: 
1.29    ! moko       16: volatile const char * IDENT_PA_VMEMCACHED_C="$Id: pa_vmemcached.C,v 1.28 2026/10/09 22:05:41 moko Exp $" IDENT_PA_VMEMCACHED_H;
1.1       moko       17: 
1.14      moko       18: const char *memcached_library="libmemcached" LT_MODULE_EXT;
1.1       moko       19: 
1.8       moko       20: // support functions
                     21: 
                     22: static void error(const char *step, memcached_st* m, memcached_return rc) {
1.1       moko       23:        const char* str=f_memcached_strerror(m, rc);
                     24:        throw Exception("memcached", 0, "%s error: %s (%d)", step, str ? str : "<unknown>", rc);
                     25: }
                     26: 
1.2       moko       27: inline void check(const char *action, memcached_st* m, memcached_return rc) {
1.1       moko       28:        if(rc==MEMCACHED_SUCCESS)
                     29:                return;
1.2       moko       30:        error(action, m, rc);
1.1       moko       31: }
                     32: 
1.12      moko       33: inline void check(const char *action, memcached_st* m, memcached_return rc, memcached_return ok) {
                     34:        if(rc==MEMCACHED_SUCCESS || rc==ok)
                     35:                return;
                     36:        error(action, m, rc);
                     37: }
                     38: 
1.6       moko       39: inline void check_key(const String& akey) {
                     40:        if(akey.is_empty())
                     41:                throw Exception("memcached", 0, "key must not be empty");
                     42:        if(akey.length() > MEMCACHED_MAX_KEY)
                     43:                throw Exception("memcached", &akey, "key length %d exceeds limit (%d bytes)", akey.length(), MEMCACHED_MAX_KEY);
                     44: }
                     45: 
1.8       moko       46: // serialization helpers
                     47: 
                     48: #define SERIALIZED_STRING 256
                     49: 
                     50: struct Serialization_data{
                     51:     unsigned int flags;
                     52:     const char *ptr;
                     53:     size_t length;
                     54: 
                     55:     Serialization_data() : flags(0), ptr(0), length(0){}
                     56:     Serialization_data(unsigned int aflags) : flags(aflags), ptr(0), length(0){}
                     57:     Serialization_data(unsigned int aflags, const char *aptr, size_t alength) : flags(aflags), ptr(aptr), length(alength){}
                     58: };
                     59: 
                     60: static void serialize_string(const String &str, Serialization_data &data){
                     61:        if(str.is_empty()){
                     62:                data = Serialization_data(SERIALIZED_STRING);
                     63:                return;
                     64:        }
                     65:        
                     66:        if (str.is_not_just_lang()){
                     67:                String::Cm cm = str.serialize(0);
                     68:                data = Serialization_data(SERIALIZED_STRING, cm.str, cm.length);
                     69:        } else {
                     70:                data = Serialization_data(SERIALIZED_STRING + (unsigned int)str.just_lang(), str.cstr(), str.length());
                     71:        }
                     72: }
                     73: 
                     74: static VString *deserialize_string(Serialization_data &data){
                     75:        String *result;
                     76: 
                     77:        if(data.flags==SERIALIZED_STRING){
                     78:                result = new String();
                     79:                if (data.length>0 && !result->deserialize(0, (void *)data.ptr, data.length))
                     80:                        return NULL;
                     81:        } else {
                     82:                // we can't use length from memcached as there can be '\0' inside
                     83:                String::Language lang=(String::Language)(data.flags-SERIALIZED_STRING);
                     84:                result = new String(data.ptr, lang);
                     85:        }
                     86: 
                     87:        return new VString(*result);
                     88: }
                     89: 
                     90: static Value &deserialize(Serialization_data &data){
                     91:        Value *result=NULL;
                     92:        
                     93:        if(data.flags>=SERIALIZED_STRING && data.flags<(SERIALIZED_STRING+256)){
                     94:                // String->deserialize uses passed string
1.9       moko       95:                if(data.length>0)
                     96:                        data.ptr=pa_strdup(data.ptr, data.length);
1.8       moko       97:                result=deserialize_string(data);
                     98:        }
                     99: 
                    100:        if (!result)
                    101:                throw Exception(PARSER_RUNTIME, 0, "unable to deserialize data id %d, size %d", data.flags, data.length);
                    102: 
                    103:        return *result;
                    104: }
                    105: 
                    106: // VMemcached
                    107: 
1.10      moko      108: static void load_memcached(const char *library){
1.1       moko      109:        const char *memcached_status = memcached_load(library);
                    110: 
                    111:        if(memcached_status)
                    112:                throw Exception("memcached", 0, "failed to load memcached library %s: %s", library, memcached_status);
1.10      moko      113: }
                    114: 
1.16      moko      115: void VMemcached::open(const String& options_string, time_t attl, bool connect){
1.10      moko      116:        load_memcached(memcached_library);
                    117:        
                    118:        if(f_memcached==NULL)
                    119:                throw Exception("memcached", 0, "options hash requires libmemcached version 0.49 or later");
                    120:        
                    121:        if(options_string.is_empty())
                    122:                throw Exception("memcached", 0, "options hash must not be empty");
                    123:        
                    124:        fttl=attl;
1.27      moko      125:        if(fm) f_memcached_free(fm); // ^m.open[] of an opened object
1.10      moko      126:        fm=f_memcached(options_string.cstr(), options_string.length());
1.16      moko      127:        if (connect)
                    128:                check("connect", fm, f_memcached_version(fm), MEMCACHED_NOT_SUPPORTED);
1.10      moko      129: }
1.1       moko      130: 
1.10      moko      131: void VMemcached::open_parse(const String& connect_string, time_t attl){
                    132:        load_memcached(memcached_library);
                    133:        
1.1       moko      134:        if(connect_string.is_empty())
1.10      moko      135:                throw Exception("memcached", 0, "connect string must not be empty");
                    136:        
1.2       moko      137:        fttl=attl;
1.27      moko      138:        if(fm) f_memcached_free(fm); // ^m.open[] of an opened object
1.1       moko      139:        fm=f_memcached_create(NULL);
                    140:        memcached_server_st* fservers = f_memcached_servers_parse(connect_string.cstr());
1.28      moko      141:        memcached_return rc=f_memcached_server_push(fm, fservers);
                    142:        f_memcached_server_list_free(fservers); // pushed as a copy
                    143:        check("server_push", fm, rc);
1.12      moko      144:        check("connect", fm, f_memcached_version(fm), MEMCACHED_NOT_SUPPORTED);
1.1       moko      145: }
                    146: 
1.6       moko      147: void VMemcached::flush(time_t attl) {
1.2       moko      148:        check("flush", fm, f_memcached_flush(fm, attl));
                    149: }
                    150: 
1.13      moko      151: void VMemcached::quit() {
                    152:        f_memcached_quit(fm);
                    153: }
                    154: 
1.6       moko      155: void VMemcached::remove(const String& aname) {
                    156:        check_key(aname);
1.12      moko      157:        check("delete", fm, f_memcached_delete(fm, aname.cstr(), aname.length(), (time_t)0), MEMCACHED_NOTFOUND);
1.1       moko      158: }
                    159: 
                    160: Value* VMemcached::get_element(const String& aname) {
1.29    ! moko      161:        MAYBE_GET_SPECIAL_OR_METHOD(VStateless_object::get_element(aname));
1.1       moko      162: 
1.6       moko      163:        check_key(aname);
1.1       moko      164: 
1.28      moko      165:        // mget used as memcached_get returns a value malloc-ed by the library, which we can not free:
                    166:        // free is disabled in parser; under windows the library may have another C runtime
                    167:        const char *key=aname.cstr();
                    168:        size_t key_length=aname.length();
                    169:        check("get", fm, f_memcached_mget(fm, &key, &key_length, 1));
                    170: 
                    171:        Value *result=VVoid::get();
                    172:        memcached_result_st *results=0;
1.1       moko      173:        memcached_return rc;
                    174: 
1.28      moko      175:        // fetched until the end, which frees the result
                    176:        while((results=f_memcached_fetch_result(fm, results, &rc)) && (rc == MEMCACHED_SUCCESS)){
                    177:                Serialization_data data(f_memcached_result_flags(results), f_memcached_result_value(results), f_memcached_result_length(results));
                    178:                result=&deserialize(data);
1.5       moko      179:        }
1.1       moko      180: 
1.28      moko      181:        if (rc != MEMCACHED_END && rc != MEMCACHED_NOTFOUND)
                    182:                error("get", fm, rc);
                    183: 
                    184:        return result;
1.1       moko      185: }
                    186: 
1.2       moko      187: Value &VMemcached::mget(ArrayString& akeys) {
                    188:        VHash &hresult = *new VHash();
                    189:        
                    190:        size_t kl = akeys.count();
                    191:        
                    192:        if(kl==0)
                    193:                return hresult;
                    194:        
1.20      moko      195:        const char **keys = new(PointerGC) const char *[kl];
                    196:        size_t *key_lengths = new(PointerFreeGC) size_t[kl];
1.2       moko      197:        
1.6       moko      198:        for(size_t i=0; i<kl; i++){
                    199:                const String &skey = *(akeys[i]);
                    200:                check_key(skey);
                    201:                keys[i] = skey.cstr();
                    202:                key_lengths[i] = skey.length();
1.2       moko      203:        }
                    204:        
1.3       moko      205:        check("mget", fm, f_memcached_mget(fm, keys, key_lengths, kl));
1.2       moko      206:        
1.7       moko      207:        // memcached_fetch_result calls memcached_result_create and memcached_result_free, we don't need to do this.
                    208:        memcached_result_st *results=0;
1.4       moko      209:        memcached_return rc;
                    210:        
1.7       moko      211:        while((results=f_memcached_fetch_result(fm, results, &rc)) && (rc == MEMCACHED_SUCCESS)){
1.4       moko      212:                const char *hkey = pa_strdup(f_memcached_result_key_value(results), f_memcached_result_key_length(results));
1.8       moko      213:                Serialization_data data(f_memcached_result_flags(results), f_memcached_result_value(results), f_memcached_result_length(results));
1.5       moko      214: 
1.8       moko      215:                hresult.hash().put(hkey, &deserialize(data));
1.2       moko      216:        }
1.4       moko      217: 
                    218:        if (rc != MEMCACHED_END && rc != MEMCACHED_NOTFOUND)
                    219:                error("mget", fm, rc);
1.2       moko      220:        
1.20      moko      221:        operator delete[](keys);
                    222:        operator delete[](key_lengths);
1.3       moko      223:        
1.2       moko      224:        return hresult;
                    225: }
1.1       moko      226: 
1.11      moko      227: static inline time_t serialize_value(time_t ttl, const String& aname, Value* avalue, Serialization_data &data){
1.1       moko      228: 
                    229:        if(HashStringValue* hash=avalue->get_hash()) {
1.11      moko      230:                int valid_options=1;
                    231:                if(Value* ttl_value=hash->get(expires_name)){
1.1       moko      232:                        ttl=ttl_value->as_int();
1.11      moko      233:                        valid_options++;
                    234:                }
                    235:                if(avalue=hash->get(value_name)){
                    236:                        if(avalue->get_junction())
1.1       moko      237:                                throw Exception("memcached", 0, VALUE_NAME " must not be code");
                    238:                } else
1.2       moko      239:                        throw Exception("memcached", &aname, "value hash must contain ." VALUE_NAME);
1.11      moko      240:                        
                    241:                if(valid_options!=hash->count())
                    242:                        throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
1.1       moko      243:        }
                    244: 
1.8       moko      245:        if(avalue->is_string()){
                    246:                serialize_string(*avalue->get_string(), data);
                    247:        } else {
1.23      moko      248:                throw Exception("memcached", &aname, "%s serialization is not supported yet", avalue->type());
1.8       moko      249:        }
1.1       moko      250: 
1.11      moko      251:        return ttl;
                    252: }
                    253: 
                    254: bool VMemcached::add(const String& aname, Value* avalue){
                    255:        check_key(aname);
                    256: 
                    257:        Serialization_data data;
                    258:        time_t ttl=serialize_value(fttl, aname, avalue, data);
                    259: 
                    260:        memcached_return rc=f_memcached_add(fm, aname.cstr(), aname.length(), data.ptr, data.length, ttl, data.flags);
                    261: 
                    262:        if(rc == MEMCACHED_NOTSTORED)
                    263:                return false;
                    264: 
                    265:        if(rc != MEMCACHED_SUCCESS)
                    266:                error("add", fm, rc);
                    267: 
                    268:        return true;
                    269: }
                    270: 
1.15      moko      271: const VJunction* VMemcached::put_element(const String& aname, Value* avalue){
1.11      moko      272:        check_key(aname);
                    273: 
                    274:        Serialization_data data;
                    275:        time_t ttl=serialize_value(fttl, aname, avalue, data);
                    276: 
                    277:        check("set", fm, f_memcached_set(fm, aname.cstr(), aname.length(), data.ptr, data.length, ttl, data.flags));
1.1       moko      278: 
1.21      moko      279:        return 0;
1.1       moko      280: }
                    281: 

E-mail: