arunthampi-memcached 0.17.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,45 @@
1
+ require 'echoe'
2
+
3
+ Echoe.new("memcached") do |p|
4
+ p.author = "Evan Weaver"
5
+ p.project = "fauna"
6
+ p.summary = "An interface to the libmemcached C client."
7
+ p.url = "http://blog.evanweaver.com/files/doc/fauna/memcached/"
8
+ p.docs_host = "blog.evanweaver.com:~/www/bax/public/files/doc/"
9
+ p.rdoc_pattern = /README|TODO|LICENSE|CHANGELOG|BENCH|COMPAT|exceptions|behaviors|rails.rb|memcached.rb/
10
+ p.clean_pattern += ["ext/lib", "ext/include", "ext/share", "ext/libmemcached-?.??", "ext/bin", "ext/conftest.dSYM"]
11
+ end
12
+
13
+ task :exceptions do
14
+ $LOAD_PATH << "lib"
15
+ require 'memcached'
16
+ Memcached.constants.sort.each do |const_name|
17
+ const = Memcached.send(:const_get, const_name)
18
+ next if const == Memcached::Success or const == Memcached::Stored
19
+ if const.is_a? Class and const < Memcached::Error
20
+ puts "* Memcached::#{const_name}"
21
+ end
22
+ end
23
+ end
24
+
25
+ task :valgrind do
26
+ exec("valgrind --tool=memcheck --leak-check=yes --show-reachable=no --num-callers=15 --track-fds=yes ruby #{File.dirname(__FILE__)}/test/profile/valgrind.rb")
27
+ end
28
+
29
+ task :profile do
30
+ exec("ruby #{File.dirname(__FILE__)}/test/profile/profile.rb")
31
+ end
32
+
33
+ begin
34
+ require 'jeweler'
35
+ Jeweler::Tasks.new do |gemspec|
36
+ gemspec.name = "arunthampi-memcached"
37
+ gemspec.summary = "Chu Yeow's fork of memcached which allows big-set and big-get"
38
+ gemspec.description = "Chu Yeow's fork of memcached which allows big-set and big-get"
39
+ gemspec.email = "chuyeow@gmail.com"
40
+ gemspec.homepage = "http://github.com/chuyeow/memcached"
41
+ gemspec.authors = ["Chu Yeow"]
42
+ end
43
+ rescue LoadError
44
+ puts "Jeweler not available. Install it with: sudo gem install jeweler"
45
+ end
data/TODO ADDED
@@ -0,0 +1,4 @@
1
+
2
+ * Verify that DNS lookups are getting cached
3
+ * Check for memory leaks in Valgrind after free callback change
4
+ * Re-run benchmarks
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.17.4
@@ -0,0 +1,106 @@
1
+ require 'mkmf'
2
+
3
+ HERE = File.expand_path(File.dirname(__FILE__))
4
+ BUNDLE = Dir.glob("libmemcached-*.tar.gz").first
5
+ BUNDLE_PATH = BUNDLE.sub(".tar.gz", "")
6
+
7
+ DARWIN = `uname -sp` == "Darwin i386\n"
8
+
9
+ # Is there a better way to do this?
10
+ archflags = if ENV['ARCHFLAGS']
11
+ ENV['ARCHFLAGS']
12
+ elsif Config::CONFIG['host_os'][0,8] == "darwin10"
13
+ "-arch i386 -arch x86_64"
14
+ elsif Config::CONFIG['host_os'][0,6] == "darwin"
15
+ "-arch i386 -arch ppc"
16
+ end
17
+
18
+ if !ENV["EXTERNAL_LIB"]
19
+ $includes = " -I#{HERE}/include"
20
+ $libraries = " -L#{HERE}/lib"
21
+
22
+ $CFLAGS = "#{$includes} #{$libraries} #{$CFLAGS}"
23
+ $LDFLAGS = "#{$libraries} #{$LDFLAGS}"
24
+ $CPPFLAGS = $ARCH_FLAG = $DLDFLAGS = ""
25
+
26
+ $LIBPATH = ["#{HERE}/lib"]
27
+ $DEFLIBPATH = []
28
+
29
+ Dir.chdir(HERE) do
30
+ if File.exist?("lib")
31
+ puts "Libmemcached already built; run 'rake clean' first if you need to rebuild."
32
+ else
33
+ puts "Building libmemcached."
34
+ puts(cmd = "tar xzf #{BUNDLE} 2>&1")
35
+ raise "'#{cmd}' failed" unless system(cmd)
36
+
37
+ puts "Patching libmemcached."
38
+ puts(cmd = "patch -p1 < libmemcached.patch")
39
+ raise "'#{cmd}' failed" unless system(cmd)
40
+
41
+ Dir.chdir(BUNDLE_PATH) do
42
+
43
+ cxxflags = cflags = ldflags = "-fPIC"
44
+ extraconf = ''
45
+
46
+ # again... is there a better way to do this?
47
+ if DARWIN
48
+ cflags = "#{cflags} #{archflags}"
49
+ cxxflags = "-std=gnu++98 #{cflags}"
50
+ ldflags = "#{ldflags} #{archflags}"
51
+ extraconf = '--enable-dtrace --disable-dependency-tracking'
52
+ end
53
+
54
+ if ENV['DEBUG']
55
+ puts "Setting debug flags for libmemcached."
56
+ cflags << " -O0 -ggdb -DHAVE_DEBUG"
57
+ extraconf << " --enable-debug"
58
+ else
59
+ cflags << " -Os"
60
+ end
61
+
62
+ puts(cmd = "env CFLAGS='#{cflags}' LDFLAGS='#{ldflags}' ./configure --prefix=#{HERE} --without-memcached --disable-shared --disable-utils #{extraconf} 2>&1")
63
+ raise "'#{cmd}' failed" unless system(cmd)
64
+ puts(cmd = "make CXXFLAGS='#{cxxflags}' || true 2>&1")
65
+ raise "'#{cmd}' failed" unless system(cmd)
66
+ puts(cmd = "make install || true 2>&1")
67
+ raise "'#{cmd}' failed" unless system(cmd)
68
+ end
69
+
70
+ unless ENV['DEBUG'] or ENV['DEV']
71
+ system("rm -rf #{BUNDLE_PATH}")
72
+ end
73
+ end
74
+ end
75
+
76
+ # Absolutely prevent the linker from picking up any other libmemcached
77
+ Dir.chdir("#{HERE}/lib") do
78
+ system("cp -f libmemcached.a libmemcached_gem.a")
79
+ system("cp -f libmemcached.la libmemcached_gem.la")
80
+ end
81
+ $LIBS << " -lmemcached_gem"
82
+ end
83
+
84
+ $CFLAGS.gsub! /-O\d/, ''
85
+
86
+ if ENV['DEBUG']
87
+ puts "Setting debug flags for gem."
88
+ $CFLAGS << " -O0 -ggdb -DHAVE_DEBUG"
89
+ else
90
+ $CFLAGS << " -Os"
91
+ end
92
+
93
+ if DARWIN
94
+ $CFLAGS.gsub! /-arch \S+/, ''
95
+ $CFLAGS << " #{archflags}"
96
+ $LDFLAGS.gsub! /-arch \S+/, ''
97
+ $LDFLAGS << " #{archflags}"
98
+ end
99
+
100
+ if ENV['SWIG']
101
+ puts "Running SWIG."
102
+ puts(cmd = "swig #{$includes} -ruby -autorename rlibmemcached.i")
103
+ raise "'#{cmd}' failed" unless system(cmd)
104
+ end
105
+
106
+ create_makefile 'rlibmemcached'
@@ -0,0 +1,270 @@
1
+
2
+ diff --git a/libmemcached-0.32/libmemcached/memcached_response.c b/libmemcached/libmemcached/memcached_response.c
3
+ --- a/libmemcached-0.32/libmemcached/memcached_response.c
4
+ +++ b/libmemcached/libmemcached/memcached_response.c
5
+ @@ -496,6 +496,8 @@
6
+ case PROTOCOL_BINARY_RESPONSE_E2BIG:
7
+ case PROTOCOL_BINARY_RESPONSE_EINVAL:
8
+ case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
9
+ + rc= MEMCACHED_NOTSTORED;
10
+ + break;
11
+ case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
12
+ case PROTOCOL_BINARY_RESPONSE_ENOMEM:
13
+ default:
14
+ diff --git a/libmemcached-0.32/libmemcached/memcached.c b/libmemcached/libmemcached/memcached.c
15
+ --- a/libmemcached-0.32/libmemcached/memcached.c
16
+ +++ b/libmemcached/libmemcached/memcached.c
17
+ @@ -50,6 +50,9 @@ void memcached_free(memcached_st *ptr)
18
+ if (ptr->continuum)
19
+ ptr->call_free(ptr, ptr->continuum);
20
+
21
+ + if (ptr->live_host_indices)
22
+ + ptr->call_free(ptr, ptr->live_host_indices);
23
+ +
24
+ if (ptr->is_allocated)
25
+ ptr->call_free(ptr, ptr);
26
+ else
27
+ diff --git a/libmemcached-0.32/libmemcached/memcached.h b/libmemcached/libmemcached/memcached.h
28
+ --- a/libmemcached-0.32/libmemcached/memcached.h
29
+ +++ b/libmemcached/libmemcached/memcached.h
30
+ @@ -112,6 +112,9 @@ struct memcached_st {
31
+ memcached_trigger_delete_key delete_trigger;
32
+ char prefix_key[MEMCACHED_PREFIX_KEY_MAX_SIZE];
33
+ uint32_t number_of_replicas;
34
+ + uint32_t number_of_live_hosts;
35
+ + uint32_t *live_host_indices;
36
+ + uint32_t live_host_indices_size;
37
+ };
38
+
39
+ LIBMEMCACHED_API
40
+ diff --git a/libmemcached-0.32/libmemcached/memcached_connect.c b/libmemcached/libmemcached/memcached_connect.c
41
+ --- a/libmemcached-0.32/libmemcached/memcached_connect.c
42
+ +++ b/libmemcached/libmemcached/memcached_connect.c
43
+ @@ -273,7 +272,6 @@ static memcached_return network_connect(memcached_server_st *ptr)
44
+ (void)fcntl(ptr->fd, F_SETFL, flags & ~O_NONBLOCK);
45
+
46
+ WATCHPOINT_ASSERT(ptr->cursor_active == 0);
47
+ - ptr->server_failure_counter= 0;
48
+ return MEMCACHED_SUCCESS;
49
+ }
50
+ use = use->ai_next;
51
+ @@ -282,21 +280,13 @@ static memcached_return network_connect(memcached_server_st *ptr)
52
+
53
+ if (ptr->fd == -1)
54
+ {
55
+ - /* Failed to connect. schedule next retry */
56
+ - if (ptr->root->retry_timeout)
57
+ - {
58
+ - struct timeval next_time;
59
+ -
60
+ - if (gettimeofday(&next_time, NULL) == 0)
61
+ - ptr->next_retry= next_time.tv_sec + ptr->root->retry_timeout;
62
+ - }
63
+ - ptr->server_failure_counter+= 1;
64
+ + ptr->server_failure_counter ++;
65
+ if (ptr->cached_errno == 0)
66
+ return MEMCACHED_TIMEOUT;
67
+ +
68
+ return MEMCACHED_ERRNO; /* The last error should be from connect() */
69
+ }
70
+
71
+ - ptr->server_failure_counter= 0;
72
+ return MEMCACHED_SUCCESS; /* The last error should be from connect() */
73
+ }
74
+
75
+ @@ -315,7 +305,7 @@ memcached_return memcached_connect(memcached_server_st *ptr)
76
+ gettimeofday(&next_time, NULL);
77
+
78
+ /* if we've had too many consecutive errors on this server, mark it dead. */
79
+ - if (ptr->server_failure_counter > ptr->root->server_failure_limit)
80
+ + if (ptr->server_failure_counter >= ptr->root->server_failure_limit)
81
+ {
82
+ ptr->next_retry= next_time.tv_sec + ptr->root->retry_timeout;
83
+ ptr->server_failure_counter= 0;
84
+ diff --git a/libmemcached-0.32/libmemcached/memcached_get.h b/libmemcached/libmemcached/memcached_get.h
85
+ diff --git a/libmemcached-0.32/libmemcached/memcached_hash.c b/libmemcached/libmemcached/memcached_hash.c
86
+ index 129d761..3a7fa55 100644
87
+ --- a/libmemcached-0.32/libmemcached/memcached_hash.c
88
+ +++ b/libmemcached/libmemcached/memcached_hash.c
89
+ @@ -11,6 +11,7 @@ static uint32_t FNV_32_PRIME= 16777619;
90
+ /* Prototypes */
91
+ static uint32_t internal_generate_hash(const char *key, size_t key_length);
92
+ static uint32_t internal_generate_md5(const char *key, size_t key_length);
93
+ +uint32_t memcached_live_host_index(memcached_st *ptr, uint32_t num);
94
+
95
+ uint32_t memcached_generate_hash_value(const char *key, size_t key_length, memcached_hash hash_algorithm)
96
+ {
97
+ @@ -146,10 +144,10 @@ static uint32_t dispatch_host(memcached_st *ptr, uint32_t hash)
98
+ right= begin;
99
+ return right->index;
100
+ }
101
+ - case MEMCACHED_DISTRIBUTION_MODULA:
102
+ - return hash % ptr->number_of_hosts;
103
+ + case MEMCACHED_DISTRIBUTION_MODULA:
104
+ + return memcached_live_host_index(ptr, hash);
105
+ case MEMCACHED_DISTRIBUTION_RANDOM:
106
+ - return (uint32_t) random() % ptr->number_of_hosts;
107
+ + return memcached_live_host_index(ptr, (uint32_t) random());
108
+ default:
109
+ WATCHPOINT_ASSERT(0); /* We have added a distribution without extending the logic */
110
+ return hash % ptr->number_of_hosts;
111
+ @@ -201,6 +199,19 @@ uint32_t memcached_generate_hash(memcached_st *ptr, const char *key, size_t key_
112
+ return dispatch_host(ptr, hash);
113
+ }
114
+
115
+ +uint32_t memcached_live_host_index(memcached_st *ptr, uint32_t num)
116
+ +{
117
+ + if (memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS)) {
118
+ + if (ptr->number_of_live_hosts > 0) {
119
+ + return ptr->live_host_indices[num % ptr->number_of_live_hosts];
120
+ + } else {
121
+ + return 0; /* FIXME: we should do something different if every server's dead, but I dunno what. */
122
+ + }
123
+ + } else {
124
+ + return num % ptr->number_of_hosts;
125
+ + }
126
+ +}
127
+ +
128
+ static uint32_t internal_generate_hash(const char *key, size_t key_length)
129
+ {
130
+ const char *ptr= key;
131
+ diff --git a/libmemcached-0.32/libmemcached/memcached_hosts.c b/libmemcached/libmemcached/memcached_hosts.c
132
+ --- a/libmemcached-0.32/libmemcached/memcached_hosts.c
133
+ +++ b/libmemcached/libmemcached/memcached_hosts.c
134
+ @@ -7,6 +7,7 @@ static memcached_return server_add(memcached_st *ptr, const char *hostname,
135
+ uint32_t weight,
136
+ memcached_connection type);
137
+ memcached_return update_continuum(memcached_st *ptr);
138
+ +memcached_return update_live_host_indices(memcached_st *ptr);
139
+
140
+ static int compare_servers(const void *p1, const void *p2)
141
+ {
142
+ @@ -44,8 +45,12 @@ memcached_return run_distribution(memcached_st *ptr)
143
+ case MEMCACHED_DISTRIBUTION_MODULA:
144
+ if (ptr->flags & MEM_USE_SORT_HOSTS)
145
+ sort_hosts(ptr);
146
+ + if (memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS))
147
+ + update_live_host_indices(ptr);
148
+ break;
149
+ case MEMCACHED_DISTRIBUTION_RANDOM:
150
+ + if (memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS))
151
+ + update_live_host_indices(ptr);
152
+ break;
153
+ default:
154
+ WATCHPOINT_ASSERT(0); /* We have added a distribution without extending the logic */
155
+ @@ -85,6 +90,46 @@ static uint32_t ketama_server_hash(const char *key, unsigned int key_length, int
156
+ | (results[0 + alignment * 4] & 0xFF);
157
+ }
158
+
159
+ +memcached_return update_live_host_indices(memcached_st *ptr)
160
+ +{
161
+ + uint32_t host_index;
162
+ + struct timeval now;
163
+ + uint32_t i = 0;
164
+ + memcached_server_st *list;
165
+ +
166
+ + if (gettimeofday(&now, NULL) != 0)
167
+ + {
168
+ + ptr->cached_errno = errno;
169
+ + return MEMCACHED_ERRNO;
170
+ + }
171
+ +
172
+ + if (ptr->live_host_indices == NULL) {
173
+ + ptr->live_host_indices = (uint32_t *)ptr->call_malloc(ptr, sizeof(uint32_t) * ptr->number_of_hosts);
174
+ + ptr->live_host_indices_size = ptr->number_of_live_hosts;
175
+ + }
176
+ +
177
+ + /* somehow we added some hosts. Shouldn't get here much, if at all. */
178
+ + if (ptr->live_host_indices_size < ptr->number_of_hosts ) {
179
+ + ptr->live_host_indices = (uint32_t *)ptr->call_realloc(ptr, ptr->live_host_indices, sizeof(uint32_t) * ptr->number_of_hosts);
180
+ + ptr->live_host_indices_size = ptr->number_of_live_hosts;
181
+ + }
182
+ +
183
+ + if (ptr->live_host_indices == NULL)
184
+ + return MEMCACHED_FAILURE;
185
+ +
186
+ + list = ptr->hosts;
187
+ + for (host_index= 0; host_index < ptr->number_of_hosts; ++host_index)
188
+ + {
189
+ + if (list[host_index].next_retry <= now.tv_sec) {
190
+ + ptr->live_host_indices[i++] = host_index;
191
+ + } else if (ptr->next_distribution_rebuild == 0 || list[host_index].next_retry < ptr->next_distribution_rebuild) {
192
+ + ptr->next_distribution_rebuild= list[host_index].next_retry;
193
+ + }
194
+ + }
195
+ + ptr->number_of_live_hosts = i;
196
+ + return MEMCACHED_SUCCESS;
197
+ +}
198
+ +
199
+ static int continuum_item_cmp(const void *t1, const void *t2)
200
+ {
201
+ memcached_continuum_item_st *ct1= (memcached_continuum_item_st *)t1;
202
+ @@ -111,8 +156,8 @@ memcached_return update_continuum(memcached_st *ptr)
203
+ uint32_t pointer_per_server= MEMCACHED_POINTS_PER_SERVER;
204
+ uint32_t pointer_per_hash= 1;
205
+ uint64_t total_weight= 0;
206
+ - uint64_t is_ketama_weighted= 0;
207
+ - uint64_t is_auto_ejecting= 0;
208
+ + uint32_t is_ketama_weighted= 0;
209
+ + uint32_t is_auto_ejecting= 0;
210
+ uint32_t points_per_server= 0;
211
+ uint32_t live_servers= 0;
212
+ struct timeval now;
213
+ diff --git a/libmemcached-0.32/libmemcached/memcached_storage.c b/libmemcached/libmemcached/memcached_storage.c
214
+ index ecefc56..ee79a7e 100644
215
+ --- a/libmemcached-0.32/libmemcached/memcached_storage.c
216
+ +++ b/libmemcached/libmemcached/memcached_storage.c
217
+ @@ -135,22 +135,16 @@ static inline memcached_return memcached_send(memcached_st *ptr,
218
+ }
219
+
220
+ if (write_length >= MEMCACHED_DEFAULT_COMMAND_SIZE)
221
+ - {
222
+ - rc= MEMCACHED_WRITE_FAILURE;
223
+ - goto error;
224
+ - }
225
+ + return MEMCACHED_WRITE_FAILURE;
226
+
227
+ /* Send command header */
228
+ rc= memcached_do(&ptr->hosts[server_key], buffer, write_length, 0);
229
+ if (rc != MEMCACHED_SUCCESS)
230
+ - goto error;
231
+ + return rc;
232
+
233
+ /* Send command body */
234
+ if ((sent_length= memcached_io_write(&ptr->hosts[server_key], value, value_length, 0)) == -1)
235
+ - {
236
+ - rc= MEMCACHED_WRITE_FAILURE;
237
+ - goto error;
238
+ - }
239
+ + return MEMCACHED_WRITE_FAILURE;
240
+
241
+ if ((ptr->flags & MEM_BUFFER_REQUESTS) && verb == SET_OP)
242
+ to_write= 0;
243
+ @@ -158,10 +152,7 @@ static inline memcached_return memcached_send(memcached_st *ptr,
244
+ to_write= 1;
245
+
246
+ if ((sent_length= memcached_io_write(&ptr->hosts[server_key], "\r\n", 2, to_write)) == -1)
247
+ - {
248
+ - rc= MEMCACHED_WRITE_FAILURE;
249
+ - goto error;
250
+ - }
251
+ + return MEMCACHED_WRITE_FAILURE;
252
+
253
+ if (ptr->flags & MEM_NOREPLY)
254
+ return (to_write == 0) ? MEMCACHED_BUFFERED : MEMCACHED_SUCCESS;
255
+ @@ -170,15 +161,8 @@ static inline memcached_return memcached_send(memcached_st *ptr,
256
+ return MEMCACHED_BUFFERED;
257
+
258
+ rc= memcached_response(&ptr->hosts[server_key], buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL);
259
+ -
260
+ if (rc == MEMCACHED_STORED)
261
+ - return MEMCACHED_SUCCESS;
262
+ - else
263
+ - return rc;
264
+ -
265
+ -error:
266
+ - memcached_io_reset(&ptr->hosts[server_key]);
267
+ -
268
+ + return MEMCACHED_SUCCESS;
269
+ return rc;
270
+ }
@@ -0,0 +1,212 @@
1
+ %module rlibmemcached
2
+ %{
3
+ #include <libmemcached/visibility.h>
4
+ #include <libmemcached/memcached.h>
5
+ %}
6
+
7
+ %warnfilter(SWIGWARN_RUBY_WRONG_NAME) memcached_st;
8
+ %warnfilter(SWIGWARN_RUBY_WRONG_NAME) memcached_server_st;
9
+ %warnfilter(SWIGWARN_RUBY_WRONG_NAME) memcached_stat_st;
10
+ %warnfilter(SWIGWARN_RUBY_WRONG_NAME) memcached_string_st;
11
+ %warnfilter(SWIGWARN_RUBY_WRONG_NAME) memcached_result_st;
12
+
13
+ %include "typemaps.i"
14
+ %include "libmemcached/visibility.h"
15
+
16
+ // Register libmemcached's struct free function to prevent memory leaks
17
+ %freefunc memcached_st "memcached_free";
18
+ %freefunc memcached_server_st "memcached_server_free";
19
+
20
+ // %trackobjects; // Doesn't fix any interesting leaks
21
+
22
+ //// Input maps
23
+
24
+ %apply unsigned short { uint8_t };
25
+ %apply unsigned int { uint16_t };
26
+ %apply unsigned long { uint32_t flags, uint32_t offset, uint32_t weight };
27
+ %apply unsigned long long { uint64_t data, uint64_t cas };
28
+
29
+ // Array of strings map for multiget
30
+ %typemap(in) (const char **keys, size_t *key_length, size_t number_of_keys) {
31
+ int i;
32
+ Check_Type($input, T_ARRAY);
33
+ $3 = (unsigned int) RARRAY_LEN($input);
34
+ $2 = (size_t *) malloc(($3+1)*sizeof(size_t));
35
+ $1 = (char **) malloc(($3+1)*sizeof(char *));
36
+ for(i = 0; i < $3; i ++) {
37
+ Check_Type(RARRAY_PTR($input)[i], T_STRING);
38
+ $2[i] = RSTRING_LEN(RARRAY_PTR($input)[i]);
39
+ $1[i] = StringValuePtr(RARRAY_PTR($input)[i]);
40
+ }
41
+ }
42
+ %typemap(in) (char **keys, size_t *key_length, unsigned int number_of_keys) {
43
+ int i;
44
+ Check_Type($input, T_ARRAY);
45
+ $3 = (unsigned int) RARRAY_LEN($input);
46
+ $2 = (size_t *) malloc(($3+1)*sizeof(size_t));
47
+ $1 = (char **) malloc(($3+1)*sizeof(char *));
48
+ for(i = 0; i < $3; i ++) {
49
+ Check_Type(RARRAY_PTR($input)[i], T_STRING);
50
+ $2[i] = RSTRING_LEN(RARRAY_PTR($input)[i]);
51
+ $1[i] = StringValuePtr(RARRAY_PTR($input)[i]);
52
+ }
53
+ }
54
+ %typemap(freearg) (char **keys, size_t *key_length, size_t number_of_keys) {
55
+ free($1);
56
+ free($2);
57
+ }
58
+
59
+ // Generic strings
60
+ %typemap(in) (const char *str, size_t len) {
61
+ $1 = STR2CSTR($input);
62
+ $2 = (size_t) RSTRING_LEN($input);
63
+ };
64
+
65
+ // Void type strings without lengths for prefix_key callback
66
+ %typemap(in) (void *data) {
67
+ $1 = STR2CSTR($input);
68
+ };
69
+
70
+ %apply (const char *str, size_t len) {
71
+ (const char *namespace, size_t namespace_length),
72
+ (const char *key, size_t key_length),
73
+ (const char *value, size_t value_length)
74
+ };
75
+
76
+ // Key strings with same master key
77
+ // This will have to go if people actually want to set the master key separately
78
+ %typemap(in) (const char *master_key, size_t master_key_length, const char *key, size_t key_length) {
79
+ $3 = $1 = STR2CSTR($input);
80
+ $4 = $2 = (size_t) RSTRING_LEN($input);
81
+ };
82
+
83
+
84
+ //// Output maps
85
+
86
+ %apply unsigned short *OUTPUT {memcached_return *error}
87
+ %apply unsigned int *OUTPUT {uint32_t *flags}
88
+ %apply size_t *OUTPUT {size_t *value_length}
89
+ %apply unsigned long long *OUTPUT {uint64_t *value}
90
+
91
+ // Uint64
92
+ %typemap(out) (uint64_t) {
93
+ $result = ULL2NUM($1);
94
+ };
95
+
96
+ // Uint32
97
+ %typemap(out) (uint32_t) {
98
+ $result = UINT2NUM($1);
99
+ };
100
+
101
+ // String for memcached_fetch
102
+ %typemap(in, numinputs=0) (char *key, size_t *key_length) {
103
+ char string[256];
104
+ size_t length = 0;
105
+ $1 = string;
106
+ $2 = &length;
107
+ };
108
+ %typemap(argout) (char *key, size_t *key_length) {
109
+ // Pushes an empty string when *key_length == 0
110
+ rb_ary_push($result, rb_str_new($1, *$2));
111
+ }
112
+
113
+ // Array of strings
114
+
115
+ %typemap(out) (char **) {
116
+ int i;
117
+ VALUE ary = rb_ary_new();
118
+ $result = rb_ary_new();
119
+
120
+ for(i=0; $1[i] != NULL; i++) {
121
+ rb_ary_store(ary, i, rb_str_new2($1[i]));
122
+ }
123
+ rb_ary_push($result, ary);
124
+ free($1);
125
+ };
126
+
127
+ //// SWIG includes, for functions, constants, and structs
128
+
129
+ %include "libmemcached/visibility.h"
130
+ %include "libmemcached/memcached.h"
131
+ %include "libmemcached/memcached_constants.h"
132
+ %include "libmemcached/memcached_get.h"
133
+ %include "libmemcached/memcached_storage.h"
134
+ %include "libmemcached/memcached_result.h"
135
+ %include "libmemcached/memcached_server.h"
136
+
137
+ //// Custom C functions
138
+
139
+ //// Manual wrappers
140
+
141
+ // Single get. SWIG likes to use SWIG_FromCharPtr instead of SWIG_FromCharPtrAndSize because
142
+ // of the retval/argout split, so it truncates return values with \0 in them.
143
+ VALUE memcached_get_rvalue(memcached_st *ptr, const char *key, size_t key_length, uint32_t *flags, memcached_return *error);
144
+ %{
145
+ VALUE memcached_get_rvalue(memcached_st *ptr, const char *key, size_t key_length, uint32_t *flags, memcached_return *error) {
146
+ VALUE ret;
147
+ size_t value_length;
148
+ char *value = memcached_get(ptr, key, key_length, &value_length, flags, error);
149
+ ret = rb_str_new(value, value_length);
150
+ free(value);
151
+ return ret;
152
+ };
153
+ %}
154
+
155
+ // Multi get
156
+ VALUE memcached_fetch_rvalue(memcached_st *ptr, char *key, size_t *key_length, uint32_t *flags, memcached_return *error);
157
+ %{
158
+ VALUE memcached_fetch_rvalue(memcached_st *ptr, char *key, size_t *key_length, uint32_t *flags, memcached_return *error) {
159
+ size_t value_length;
160
+ VALUE result = rb_ary_new();
161
+
162
+ char *value = memcached_fetch(ptr, key, key_length, &value_length, flags, error);
163
+ if (value == NULL) {
164
+ rb_ary_push(result, Qnil);
165
+ } else {
166
+ VALUE ret = rb_str_new(value, value_length);
167
+ rb_ary_push(result, ret);
168
+ free(value);
169
+ }
170
+ return result;
171
+ };
172
+ %}
173
+
174
+ // We need to wrap this so it doesn't leak memory. SWIG doesn't want to automatically free. We could
175
+ // maybe use a 'ret' %typemap, but this is ok.
176
+ VALUE memcached_stat_get_rvalue(memcached_st *ptr, memcached_stat_st *stat, char *key, memcached_return *error);
177
+ %{
178
+ VALUE memcached_stat_get_rvalue(memcached_st *ptr, memcached_stat_st *stat, char *key, memcached_return *error) {
179
+ char *str;
180
+ VALUE ret;
181
+ str = memcached_stat_get_value(ptr, stat, key, error);
182
+ ret = rb_str_new2(str);
183
+ free(str);
184
+ return ret;
185
+ };
186
+ %}
187
+
188
+ // Ruby isn't aware that the pointer is an array... there is probably a better way to do this
189
+ memcached_server_st *memcached_select_server_at(memcached_st *in_ptr, int index);
190
+ %{
191
+ memcached_server_st *memcached_select_server_at(memcached_st *in_ptr, int index) {
192
+ return &(in_ptr->hosts[index]);
193
+ };
194
+ %}
195
+
196
+ // Same, but for stats
197
+ memcached_stat_st *memcached_select_stat_at(memcached_st *in_ptr, memcached_stat_st *stat_ptr, int index);
198
+ %{
199
+ memcached_stat_st *memcached_select_stat_at(memcached_st *in_ptr, memcached_stat_st *stat_ptr, int index) {
200
+ return &(stat_ptr[index]);
201
+ };
202
+ %}
203
+
204
+ // Wrap only hash function
205
+ // Uint32
206
+ VALUE memcached_generate_hash_rvalue(const char *key, size_t key_length, memcached_hash hash_algorithm);
207
+ %{
208
+ VALUE memcached_generate_hash_rvalue(const char *key, size_t key_length,memcached_hash hash_algorithm) {
209
+ return UINT2NUM(memcached_generate_hash_value(key, key_length, hash_algorithm));
210
+ };
211
+ %}
212
+