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: