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