1 module memcache.memcache;
2 import std.string;
3 import std.array;
4 import core.stdc.string;
5 import core.stdc.time;
6 import std.conv;
7 
8 public import memcache;
9 
10 pragma(inline,true)
11 string fromCString(const char* cstring)
12 {
13     return cast(string)(fromStringz(cstring));
14 }
15 
16 nothrow:
17 class Memcache
18 {
19 public:
20 
21     this()
22     {
23         memc_ = memcached(null, 0);
24     }
25 
26     this(string config)
27     {
28         memc_ = memcached(config.ptr, config.length);
29     }
30 
31     this(string hostname, in_port_t port)
32     {
33         memc_ = memcached(null, 0);
34         if (memc_)
35         {
36             hostname ~= "\0";
37             memcached_server_add(memc_, hostname.ptr, port);
38         }
39     }
40 
41     this(memcached_st* clone)
42     {
43         memc_ = memcached_clone(null, clone);
44     }
45 
46     ~this()
47     {
48         memcached_free(memc_);
49     }
50 
51     /**
52 	* Get the internal memcached_st *
53 	*/
54     
55     memcached_st * getImpl() 
56     {
57             return memc_;
58     }
59 
60     /**
61 	* Return an error string for the given return structure.
62 	*
63 	* @param[in] rc a memcached_return_t structure
64 	* @return error string corresponding to given return code in the library.
65 	*/
66     const string getError(memcached_return_t rc)
67     {
68         /* first parameter to strerror is unused */
69 
70         return fromCString(memcached_strerror(null, rc));
71     }
72 
73     bool error(out string error_message) const
74     {
75         if (memcached_failed(memcached_last_error(memc_)))
76         {
77             error_message = fromCString(memcached_last_error_message(memc_));
78             return true;
79         }
80 
81         return false;
82     }
83 
84     bool error() const
85     {
86         if (memcached_failed(memcached_last_error(memc_)))
87         {
88             return true;
89         }
90 
91         return false;
92     }
93 
94     bool error(out memcached_return_t arg) const
95     {
96         arg = memcached_last_error(memc_);
97         return memcached_failed(arg);
98     }
99 
100     bool setBehavior(memcached_behavior_t flag, ulong data)
101     {
102         return (memcached_success(memcached_behavior_set(memc_, flag, data)));
103     }
104 
105     ulong getBehavior(memcached_behavior_t flag)
106     {
107         return memcached_behavior_get(memc_, flag);
108     }
109 
110     /**
111 	* Configure the memcache object
112 	*
113 	* @param[in] in_config configuration
114 	* @return true on success; false otherwise
115 	*/
116     bool configure(string configuration)
117     {
118         memcached_st* new_memc = memcached(configuration.ptr, configuration.length);
119 
120         if (new_memc)
121         {
122             memcached_free(memc_);
123             memc_ = new_memc;
124 
125             return true;
126         }
127 
128         return false;
129     }
130 
131     /**
132 	* Add a server to the list of memcached servers to use.
133 	*
134 	* @param[in] server_name name of the server to add
135 	* @param[in] port port number of server to add
136 	* @return true on success; false otherwise
137 	*/
138     bool addServer(string server_name, in_port_t port)
139     {
140         server_name ~= "\0";
141         return memcached_success(memcached_server_add(memc_, server_name.ptr, port));
142     }
143 
144     /**
145 	* Remove a server from the list of memcached servers to use.
146 	*
147 	* @param[in] server_name name of the server to remove
148 	* @param[in] port port number of server to remove
149 	* @return true on success; false otherwise
150     */
151     /*
152 	bool removeServer(const string server_name, in_port_t port)
153 	{
154 		string str = "," ~ server_name ~":" ~ to!string(port);
155 
156 		memcached_return_t rc= memcached_server_remove(toStringz(str));
157 
158 		return  memcached_success(memcached_return_t);
159 	}
160 	*/
161     /**
162 	* Fetches an individual value from the server. mget() must always
163 	* be called before using this method.
164 	*
165 	* @param[in] key key of object to fetch
166 	* @param[out] ret_val store returned object in this vector
167 	* @return a memcached return structure
168 	*/
169     memcached_return_t fetch(string key, out byte[] ret_val, out uint flags, out ulong cas_value)
170     {
171         memcached_return_t rc;
172 
173         memcached_result_st* result;
174         result = memcached_fetch_result(memc_, null, &rc);
175         if (result)
176         {
177             // Key
178             const char* ckey = memcached_result_key_value(result);
179             size_t len = memcached_result_key_length(result);
180             if (len > 0)
181             {
182                 key = ckey[0 .. len].dup;
183             }
184             else
185             {
186                 key = "";
187             }
188             // Actual value, null terminated
189             const char* rev = memcached_result_value(result);
190             len = memcached_result_length(result);
191             ret_val = cast(byte[]) rev[0 .. len].dup;
192             // Misc
193             flags = memcached_result_flags(result);
194             cas_value = memcached_result_cas(result);
195         }
196         memcached_result_free(result);
197 
198         return rc;
199     }
200 
201     memcached_return_t fetch(string key, out byte[] ret_val,)
202     {
203         uint flags = 0;
204         ulong cas_value = 0;
205 
206         return fetch(key, ret_val, flags, cas_value);
207     }
208 
209     /**
210 	* Fetches an individual value from the server.
211 	*
212 	* @param[in] key key of object whose value to get
213 	* @param[out] ret_val object that is retrieved is stored in
214 	*                     this vector
215 	* @return true on success; false otherwise
216 	*/
217     T get(T = string)(string key, lazy T v= T.init)
218     {
219         import core.stdc.stdlib;
220 
221         uint flags = 0;
222         memcached_return_t rc;
223         size_t value_length = 0;
224 
225         char* value = memcached_get(memc_, cast(const char*) key.ptr,
226             key.length, &value_length, &flags, &rc);
227         char[] ret_val;
228         if (value != null)
229         {
230             ret_val = value[0 .. value_length].dup;
231             return to!T(ret_val);
232         }
233         else
234         {
235             return v;
236         }
237         
238     }
239 
240     /**
241 	* Fetches an individual from a server which is specified by
242 	* the master_key parameter that is used for determining which
243 	* server an object was stored in if key partitioning was
244 	* used for storage.
245 	*
246 	* @param[in] master_key key that specifies server object is stored on
247 	* @param[in] key key of object whose value to get
248 	* @param[out] ret_val object that is retrieved is stored in
249 	*                     this vector
250 	* @return true on success; false otherwise
251 	*/
252     T getByKey(T = string)(string master_key, string key, lazy T v= T.init)
253     {
254         import core.stdc.stdlib;
255 
256         uint flags = 0;
257         memcached_return_t rc;
258         size_t value_length = 0;
259 
260         char* value = memcached_get_by_key(memc_,
261             cast(const char*) master_key.ptr, master_key.length,
262             cast(const char*) key.ptr, key.length, &value_length, &flags, &rc);
263         char[] ret_val;
264         if (value)
265         {
266 
267             ret_val = value[0 .. value_length].dup;
268             free(value);
269             return to!T(ret_val);
270         }
271         else
272         {
273             return v;
274         }
275         
276     }
277 
278     /**
279 	* Selects multiple keys at once. This method always
280 	* works asynchronously.
281 	*
282 	* @param[in] keys vector of keys to select
283 	* @return true if all keys are found
284 	
285 	bool mget(const std::vector<std::string> & keys)
286 	{
287     std::vector<const char *> real_keys;
288     std::vector<size_t> key_len;
289 		/*
290 		* Construct an array which will contain the length
291 		* of each of the strings in the input vector. Also, to
292 		* interface with the memcached C API, we need to convert
293 		* the vector of std::string's to a vector of char *.
294 		*/
295     /*
296 		real_keys.reserve(keys.length);
297 		key_len.reserve(keys.length);
298 
299     std::vector<std::string>::const_iterator it= keys.begin();
300 
301 		while (it != keys.end())
302 		{
303 			real_keys.push_back(const_cast<char *>((*it).ptr));
304 			key_len.push_back((*it).length);
305 			++it;
306 		}
307 
308 		/*
309 		* If the std::vector of keys is empty then we cannot
310 		* call memcached_mget as we will get undefined behavior.
311 		*/
312     /*
313 		if (not real_keys.empty())
314 		{
315 			return memcached_success(memcached_mget(memc_, &real_keys[0], &key_len[0], real_keys.length));
316 		}
317 
318 		return false;
319 	}
320 */
321     /**
322 	* Writes an object to the server. If the object already exists, it will
323 	* overwrite the existing object. This method always returns true
324 	* when using non-blocking mode unless a network error occurs.
325 	*
326 	* @param[in] key key of object to write to server
327 	* @param[in] value value of object to write to server
328 	* @param[in] expiration time to keep the object stored in the server for
329 	* @param[in] flags flags to store with the object
330 	* @return true on succcess; false otherwise
331 	*/
332     bool set(T = string)(string key, T value, int expiration = 0, uint flags = 0)
333     {
334         auto mvalue = to!string(value);
335         memcached_return_t rc = memcached_set(memc_,
336             cast(const char*) cast(const char*) key.ptr, key.length,
337             cast(const char*) mvalue.ptr, mvalue.length, cast(time_t) expiration, flags);
338         return memcached_success(rc);
339     }
340     /*
341 	bool set(ref string key,
342 			 const char* value, const size_t value_length,
343 			  int expiration,
344 			 uint flags)
345 	{
346 		memcached_return_t rc= memcached_set(memc_,
347 											 cast(const char *)key.ptr, key.length,
348 											 value, value_length,
349 											 expiration, flags);
350 		return memcached_success(rc);
351 	}
352 */
353     /**
354 	* Writes an object to a server specified by the master_key parameter.
355 	* If the object already exists, it will overwrite the existing object.
356 	*
357 	* @param[in] master_key key that specifies server to write to
358 	* @param[in] key key of object to write to server
359 	* @param[in] value value of object to write to server
360 	* @param[in] expiration time to keep the object stored in the server for
361 	* @param[in] flags flags to store with the object
362 	* @return true on succcess; false otherwise
363 	*/
364     bool setByKey(T = string)(string master_key, string key, T value,
365         int expiration = 0, uint flags = 0)
366     {
367         string mvalue = to!string(value);
368         return memcached_success(memcached_set_by_key(memc_,
369             cast(const char*) master_key.ptr, master_key.length,
370             cast(const char*) key.ptr, key.length, cast(const char*) mvalue.ptr,
371             mvalue.length, cast(time_t) expiration, flags));
372     }
373 
374     /**
375 	* Writes a list of objects to the server. Objects are specified by
376 	* 2 vectors - 1 vector of keys and 1 vector of values.
377 	*
378 	* @param[in] keys vector of keys of objects to write to server
379 	* @param[in] values vector of values of objects to write to server
380 	* @param[in] expiration time to keep the objects stored in server for
381 	* @param[in] flags flags to store with the objects
382 	* @return true on success; false otherwise
383 	*/
384     /*
385 	bool setAll(string[] keys,
386 				byte[][] values,
387 				int expiration,
388 				uint flags)
389 	{
390 		bool retval= true;
391 		for (int i = 0; i < keys.length; ++i) {
392 			retval= set(keys[i], values[i], expiration, flags);
393 		}
394     std::vector<std::string>::const_iterator key_it= keys.begin();
395     std::vector< std::vector<char> *>::const_iterator val_it= values.begin();
396 		while (key_it != keys.end())
397 		{
398 			retval= set((*key_it), *(*val_it), expiration, flags);
399 			if (retval == false)
400 			{
401 				return retval;
402 			}
403 			++key_it;
404 			++val_it;
405 		}
406 		return retval;
407 	}
408 */
409     /**
410 	* Writes a list of objects to the server. Objects are specified by
411 	* a map of keys to values.
412 	*
413 	* @param[in] key_value_map map of keys and values to store in server
414 	* @param[in] expiration time to keep the objects stored in server for
415 	* @param[in] flags flags to store with the objects
416 	* @return true on success; false otherwise
417 	*/
418     /*
419 	bool setAll(const std::map<const std::string, std::vector<char> >& key_value_map,
420 				int expiration,
421 				uint flags)
422 	{
423 		bool retval= true;
424     std::map<const std::string, std::vector<char> >::const_iterator it= key_value_map.begin();
425 
426 		while (it != key_value_map.end())
427 		{
428 			retval= set(it->first, it->second, expiration, flags);
429 			if (retval == false)
430 			{
431 				// We should tell the user what the key that failed was
432 				return false;
433 			}
434 			++it;
435 		}
436 
437 		return true;
438 	}
439 */
440     /**
441 	* Increment the value of the object associated with the specified
442 	* key by the offset given. The resulting value is saved in the value
443 	* parameter.
444 	*
445 	* @param[in] key key of object in server whose value to increment
446 	* @param[in] offset amount to increment object's value by
447 	* @param[out] value store the result of the increment here
448 	* @return true on success; false otherwise
449 	*/
450     bool increment(string key, uint offset, ulong* value)
451     {
452         return memcached_success(memcached_increment(memc_,
453             cast(const char*) key.ptr, key.length, offset, value));
454     }
455 
456     /**
457 	* Decrement the value of the object associated with the specified
458 	* key by the offset given. The resulting value is saved in the value
459 	* parameter.
460 	*
461 	* @param[in] key key of object in server whose value to decrement
462 	* @param[in] offset amount to increment object's value by
463 	* @param[out] value store the result of the decrement here
464 	* @return true on success; false otherwise
465 	*/
466     bool decrement(string key, uint offset, ulong* value)
467     {
468         return memcached_success(memcached_decrement(memc_,
469             cast(const char*) key.ptr, key.length, offset, value));
470     }
471 
472     /**
473 	* Add an object with the specified key and value to the server. This
474 	* function returns false if the object already exists on the server.
475 	*
476 	* @param[in] key key of object to add
477 	* @param[in] value of object to add
478 	* @return true on success; false otherwise
479 	*/
480     bool add(T = string)(string key, T tvalue)
481     {
482         auto value = to!string(tvalue);
483         return memcached_success(memcached_add(memc_, cast(const char*) key.ptr,
484             key.length, cast(const char*) value.ptr, value.length, cast(time_t) 0,
485             cast(uint) 0));
486     }
487 
488     /**
489 	* Add an object with the specified key and value to the server. This
490 	* function returns false if the object already exists on the server. The
491 	* server to add the object to is specified by the master_key parameter.
492 	*
493 	* @param[in[ master_key key of server to add object to
494 	* @param[in] key key of object to add
495 	* @param[in] value of object to add
496 	* @return true on success; false otherwise
497 	*/
498     bool addByKey(T = string)(string master_key, string key, T tvalue)
499     {
500         auto value = to!string(tvalue);
501         return memcached_success(memcached_add_by_key(memc_,
502             cast(const char*) master_key.ptr, master_key.length,
503             cast(const char*) key.ptr, key.length, cast(const char*) value.ptr,
504             value.length, cast(time_t) 0, cast(uint) 0));
505     }
506 
507     /**
508 	* Replaces an object on the server. This method only succeeds
509 	* if the object is already present on the server.
510 	*
511 	* @param[in] key key of object to replace
512 	* @param[in[ value value to replace object with
513 	* @return true on success; false otherwise
514 	*/
515     bool replace(T = string)(string key, T tvalue)
516     {
517         auto value = to!string(tvalue);
518         return memcached_success(memcached_replace(memc_,
519             cast(const char*) key.ptr, key.length, cast(const char*) value.ptr,
520             value.length, cast(time_t) 0, cast(uint) 0));
521     }
522 
523     /**
524 	* Replaces an object on the server. This method only succeeds
525 	* if the object is already present on the server. The server
526 	* to replace the object on is specified by the master_key param.
527 	*
528 	* @param[in] master_key key of server to replace object on
529 	* @param[in] key key of object to replace
530 	* @param[in[ value value to replace object with
531 	* @return true on success; false otherwise
532 	*/
533     bool replaceByKey(T = string)(string master_key, string key, T tvalue)
534     {
535         auto value = to!string(tvalue);
536         return memcached_success(memcached_replace_by_key(memc_,
537             cast(const char*) master_key.ptr, master_key.length,
538             cast(const char*) key.ptr, key.length, cast(const char*) value.ptr,
539             value.length, cast(time_t) 0, cast(uint) 0));
540     }
541 
542     /**
543 	* Places a segment of data before the last piece of data stored.
544 	*
545 	* @param[in] key key of object whose value we will prepend data to
546 	* @param[in] value data to prepend to object's value
547 	* @return true on success; false otherwise
548 	*/
549     bool prepend(T = string)(string key, T tvalue)
550     {
551         auto value = to!string(tvalue);
552         return memcached_success(memcached_prepend(memc_,
553             cast(const char*) key.ptr, key.length, cast(const char*) value.ptr,
554             value.length, cast(time_t) 0, cast(uint) 0));
555     }
556 
557     /**
558 	* Places a segment of data before the last piece of data stored. The
559 	* server on which the object where we will be prepending data is stored
560 	* on is specified by the master_key parameter.
561 	*
562 	* @param[in] master_key key of server where object is stored
563 	* @param[in] key key of object whose value we will prepend data to
564 	* @param[in] value data to prepend to object's value
565 	* @return true on success; false otherwise
566 	*/
567     bool prependByKey(T = string)(string master_key, string key, T tvalue)
568     {
569         auto value = to!string(tvalue);
570         return memcached_success(memcached_prepend_by_key(memc_,
571             cast(const char*) master_key.ptr, master_key.length,
572             cast(const char*) key.ptr, key.length, cast(const char*) value.ptr,
573             value.length, cast(time_t) 0, cast(uint) 0));
574     }
575 
576     /**
577 	* Places a segment of data at the end of the last piece of data stored.
578 	*
579 	* @param[in] key key of object whose value we will append data to
580 	* @param[in] value data to append to object's value
581 	* @return true on success; false otherwise
582 	*/
583     bool append(T = string)(string key, T tvalue)
584     {
585         auto value = to!string(tvalue);
586         return memcached_success(memcached_append(memc_,
587             cast(const char*) key.ptr, key.length, cast(const char*) value.ptr,
588             value.length, cast(time_t) 0, cast(uint) 0));
589     }
590 
591     /**
592 	* Places a segment of data at the end of the last piece of data stored. The
593 	* server on which the object where we will be appending data is stored
594 	* on is specified by the master_key parameter.
595 	*
596 	* @param[in] master_key key of server where object is stored
597 	* @param[in] key key of object whose value we will append data to
598 	* @param[in] value data to append to object's value
599 	* @return true on success; false otherwise
600 	*/
601     bool appendByKey(T = string)(string master_key, string key, T tvalue)
602     {
603         auto value = to!string(tvalue);
604         return memcached_success(memcached_append_by_key(memc_,
605             cast(const char*) master_key.ptr, master_key.length,
606             cast(const char*) key.ptr, key.length, cast(const char*) value.ptr,
607             value.length, cast(time_t) 0, cast(uint) 0));
608     }
609 
610     /**
611 	* Overwrite data in the server as long as the cas_arg value
612 	* is still the same in the server.
613 	*
614 	* @param[in] key key of object in server
615 	* @param[in] value value to store for object in server
616 	* @param[in] cas_arg "cas" value
617 	*/
618     bool cas(T = string)(string key, T tvalue, ulong cas_arg)
619     {
620         auto value = to!string(tvalue);
621         return memcached_success(memcached_cas(memc_, cast(const char*) key.ptr,
622             key.length, cast(const char*) value.ptr, value.length,
623             cast(time_t) 0, cast(uint) 0, cas_arg));
624     }
625 
626     /**
627 	* Overwrite data in the server as long as the cas_arg value
628 	* is still the same in the server. The server to use is
629 	* specified by the master_key parameter.
630 	*
631 	* @param[in] master_key specifies server to operate on
632 	* @param[in] key key of object in server
633 	* @param[in] value value to store for object in server
634 	* @param[in] cas_arg "cas" value
635 	*/
636     bool casByKey(T = string)(string master_key, string key, T tvalue, ulong cas_arg)
637     {
638         auto value = to!string(tvalue);
639         return memcached_success(memcached_cas_by_key(memc_,
640             cast(const char*) master_key.ptr, master_key.length,
641             cast(const char*) key.ptr, key.length, cast(const char*) value.ptr,
642             value.length, cast(time_t) 0, cast(uint) 0, cas_arg));
643     }
644 
645     /**
646 	* Delete an object from the server specified by the key given.
647 	*
648 	* @param[in] key key of object to delete
649 	* @param[in] expiration time to delete the object after
650 	* @return true on success; false otherwise
651 	*/
652     bool del(string key, int expiration = 0)
653     {
654         return memcached_success(memcached_delete(memc_,
655             cast(const char*) key.ptr, key.length, cast(time_t) expiration));
656     }
657 
658     /**
659 	* Delete an object from the server specified by the key given.
660 	*
661 	* @param[in] master_key specifies server to remove object from
662 	* @param[in] key key of object to delete
663 	* @param[in] expiration time to delete the object after
664 	* @return true on success; false otherwise
665 	*/
666     bool delByKey(string master_key, string key, int expiration = 0)
667     {
668         return memcached_success(memcached_delete_by_key(memc_,
669             cast(const char*) master_key.ptr, master_key.length,
670             cast(const char*) key.ptr, key.length, cast(time_t) expiration));
671     }
672 
673     /**
674 	* Wipe the contents of memcached servers.
675 	*
676 	* @param[in] expiration time to wait until wiping contents of
677 	*                       memcached servers
678 	* @return true on success; false otherwise
679 	*/
680     bool flush(int expiration = 0)
681     {
682         return memcached_success(memcached_flush(memc_, cast(time_t) expiration));
683     }
684 
685     /**
686 	* Get the library version string.
687 	* @return std::string containing a copy of the library version string.
688 	*/
689     const string libVersion()
690     {
691         const char* ver = memcached_lib_version();
692         return fromCString(ver);
693     }
694 
695     /**
696 	* Retrieve memcached statistics. Populate a std::map with the retrieved
697 	* stats. Each server will map to another std::map of the key:value stats.
698 	*
699 	* @param[out] stats_map a std::map to be populated with the memcached
700 	*                       stats
701 	* @return true on success; false otherwise
702 	*/
703     /*
704 	bool getStats(std::map< std::string, std::map<std::string, std::string> >& stats_map)
705 	{
706 		memcached_return_t rc;
707 		memcached_stat_st *stats= memcached_stat(memc_, null, &rc);
708 
709 		if (rc != MEMCACHED_SUCCESS &&
710 			rc != MEMCACHED_SOME_ERRORS)
711 		{
712 			return false;
713 		}
714 
715 		uint server_count= memcached_server_count(memc_);
716 
717 		/*
718 		* For each memcached server, construct a std::map for its stats and add
719 		* it to the std::map of overall stats.
720 		*/
721     /*
722 		for (uint x= 0; x < server_count; x++)
723 		{
724 			const memcached_instance_st * instance= memcached_server_instance_by_position(memc_, x);
725 		std::ostringstream strstm;
726 		std::string server_name(memcached_server_name(instance));
727 			server_name.append(":");
728 			strstm << memcached_server_port(instance);
729 			server_name.append(strstm.str());
730 
731 		std::map<std::string, std::string> server_stats;
732 			char **list= memcached_stat_get_keys(memc_, &stats[x], &rc);
733 			for (char** ptr= list; *ptr; ptr++)
734 			{
735 				char *value= memcached_stat_get_value(memc_, &stats[x], *ptr, &rc);
736 				server_stats[*ptr]= value;
737 				free(value);
738 			}
739 
740 			stats_map[server_name]= server_stats;
741 			free(list);
742 		}
743 
744 		memcached_stat_free(memc_, stats);
745 		return true;
746 	}
747 */
748 private:
749     memcached_st* memc_;
750 };