memcached-northscale 0.19.5.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,31 @@
1
+ require 'echoe'
2
+
3
+ Echoe.new("memcached-northscale") do |p|
4
+ p.author = "Sean Lynch"
5
+ p.project = "northscale"
6
+ p.summary = "Test gem. Do not use unless you know what you're doing."
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=full --show-reachable=no --num-callers=15 --track-fds=yes --workaround-gcc296-bugs=yes --max-stackframe=7304328 --dsymutil=yes --track-origins=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
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
@@ -0,0 +1,95 @@
1
+ require 'mkmf'
2
+ require 'rbconfig'
3
+
4
+ HERE = File.expand_path(File.dirname(__FILE__))
5
+ BUNDLE = Dir.glob("libmemcached-*.tar.gz").first
6
+ BUNDLE_PATH = BUNDLE.sub(".tar.gz", "")
7
+
8
+ SOLARIS_32 = RbConfig::CONFIG['target'] == "i386-pc-solaris2.10"
9
+
10
+ $CFLAGS = "#{RbConfig::CONFIG['CFLAGS']} #{$CFLAGS}".gsub("$(cflags)", "").gsub("-fno-common", "")
11
+ $CFLAGS << " -std=gnu99" if SOLARIS_32
12
+ $EXTRA_CONF = " --disable-64bit" if SOLARIS_32
13
+ $LDFLAGS = "#{RbConfig::CONFIG['LDFLAGS']} #{$LDFLAGS}".gsub("$(ldflags)", "").gsub("-fno-common", "")
14
+ $CXXFLAGS = " -std=gnu++98 #{$CFLAGS}"
15
+ $CPPFLAGS = $ARCH_FLAG = $DLDFLAGS = ""
16
+
17
+ if ENV['DEBUG']
18
+ puts "Setting debug flags."
19
+ $CFLAGS << " -O0 -ggdb -DHAVE_DEBUG"
20
+ $EXTRA_CONF = ""
21
+ end
22
+
23
+ def check_libmemcached
24
+ return if ENV["EXTERNAL_LIB"]
25
+
26
+ $includes = " -I#{HERE}/include"
27
+ $defines = " -DLIBMEMCACHED_WITH_SASL_SUPPORT"
28
+ $libraries = " -L#{HERE}/lib"
29
+ $CFLAGS = "#{$includes} #{$libraries} #{$CFLAGS}"
30
+ $LDFLAGS = "#{$libraries} #{$LDFLAGS}"
31
+ $LIBPATH = ["#{HERE}/lib"]
32
+ $DEFLIBPATH = [] unless SOLARIS_32
33
+
34
+ Dir.chdir(HERE) do
35
+ if File.exist?("lib")
36
+ puts "Libmemcached already built; run 'rake clean' first if you need to rebuild."
37
+ else
38
+ tar = SOLARIS_32 ? 'gtar' : 'tar'
39
+ patch = SOLARIS_32 ? 'gpatch' : 'patch'
40
+
41
+ # have_sasl check may fail on OSX, skip it
42
+ # unless RUBY_PLATFORM =~ /darwin/ or have_library('sasl2')
43
+ # raise "SASL2 not found. You need the libsasl2-dev library, which should be provided through your system's package manager."
44
+ # end
45
+
46
+ puts "Building libmemcached."
47
+ puts(cmd = "#{tar} xzf #{BUNDLE} 2>&1")
48
+ raise "'#{cmd}' failed" unless system(cmd)
49
+
50
+ puts "Patching libmemcached source."
51
+ puts(cmd = "#{patch} -p1 -Z < libmemcached.patch")
52
+ raise "'#{cmd}' failed" unless system(cmd)
53
+
54
+ puts "Patching libmemcached with SASL support."
55
+ puts(cmd = "#{patch} -p1 -Z < sasl.patch")
56
+ raise "'#{cmd}' failed" unless system(cmd)
57
+
58
+ puts "Touching aclocal.m4 in libmemcached."
59
+ puts(cmd = "touch -r #{BUNDLE_PATH}/m4/visibility.m4 #{BUNDLE_PATH}/configure.ac #{BUNDLE_PATH}/m4/pandora_have_sasl.m4")
60
+ raise "'#{cmd}' failed" unless system(cmd)
61
+
62
+ Dir.chdir(BUNDLE_PATH) do
63
+ puts(cmd = "env CFLAGS='-fPIC #{$CFLAGS}' LDFLAGS='-fPIC #{$LDFLAGS}' ./configure --prefix=#{HERE} --without-memcached --disable-shared --disable-utils --disable-dependency-tracking #{$EXTRA_CONF} 2>&1")
64
+ raise "'#{cmd}' failed" unless system(cmd)
65
+
66
+ puts(cmd = "make CXXFLAGS='#{$CXXFLAGS}' || true 2>&1")
67
+ raise "'#{cmd}' failed" unless system(cmd)
68
+
69
+ puts(cmd = "make install || true 2>&1")
70
+ raise "'#{cmd}' failed" unless system(cmd)
71
+ end
72
+
73
+ system("rm -rf #{BUNDLE_PATH}") unless ENV['DEBUG'] or ENV['DEV']
74
+ end
75
+ end
76
+
77
+ # Absolutely prevent the linker from picking up any other libmemcached
78
+ Dir.chdir("#{HERE}/lib") do
79
+ system("cp -f libmemcached.a libmemcached_gem.a")
80
+ system("cp -f libmemcached.la libmemcached_gem.la")
81
+ end
82
+ $LIBS << " -lmemcached_gem -lsasl2"
83
+ end
84
+
85
+ if ENV['SWIG']
86
+ puts "Running SWIG."
87
+ puts(cmd = "swig #{$defines} #{$includes} -ruby -autorename rlibmemcached.i")
88
+ raise "'#{cmd}' failed" unless system(cmd)
89
+ puts(cmd = "sed -i 's/STR2CSTR/StringValuePtr/' rlibmemcached_wrap.c")
90
+ raise "'#{cmd}' failed" unless system(cmd)
91
+ end
92
+
93
+ check_libmemcached
94
+
95
+ 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,230 @@
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
+ if ( (size_t) RSTRING_LEN($input) == 0) {
68
+ $1 = NULL;
69
+ } else {
70
+ $1 = STR2CSTR($input);
71
+ }
72
+ };
73
+
74
+ %apply (const char *str, size_t len) {
75
+ (const char *namespace, size_t namespace_length),
76
+ (const char *key, size_t key_length),
77
+ (const char *value, size_t value_length)
78
+ };
79
+
80
+ // Key strings with same master key
81
+ // This will have to go if people actually want to set the master key separately
82
+ %typemap(in) (const char *master_key, size_t master_key_length, const char *key, size_t key_length) {
83
+ $3 = $1 = STR2CSTR($input);
84
+ $4 = $2 = (size_t) RSTRING_LEN($input);
85
+ };
86
+
87
+
88
+ //// Output maps
89
+
90
+ %apply unsigned short *OUTPUT {memcached_return *error}
91
+ %apply unsigned int *OUTPUT {uint32_t *flags}
92
+ %apply size_t *OUTPUT {size_t *value_length}
93
+ %apply unsigned long long *OUTPUT {uint64_t *value}
94
+
95
+ // Uint64
96
+ %typemap(out) (uint64_t) {
97
+ $result = ULL2NUM($1);
98
+ };
99
+
100
+ // Uint32
101
+ %typemap(out) (uint32_t) {
102
+ $result = UINT2NUM($1);
103
+ };
104
+
105
+ // Void type strings without lengths for prefix_key callback
106
+ // Currently not used by the gem
107
+ %typemap(out) (void *) {
108
+ $result = rb_str_new2($1);
109
+ };
110
+
111
+ // String for memcached_fetch
112
+ %typemap(in, numinputs=0) (char *key, size_t *key_length) {
113
+ char string[256];
114
+ size_t length = 0;
115
+ $1 = string;
116
+ $2 = &length;
117
+ };
118
+ %typemap(argout) (char *key, size_t *key_length) {
119
+ // Pushes an empty string when *key_length == 0
120
+ rb_ary_push($result, rb_str_new($1, *$2));
121
+ }
122
+
123
+ // Array of strings
124
+
125
+ %typemap(out) (char **) {
126
+ int i;
127
+ VALUE ary = rb_ary_new();
128
+ $result = rb_ary_new();
129
+
130
+ for(i=0; $1[i] != NULL; i++) {
131
+ rb_ary_store(ary, i, rb_str_new2($1[i]));
132
+ }
133
+ rb_ary_push($result, ary);
134
+ free($1);
135
+ };
136
+
137
+ //// SWIG includes, for functions, constants, and structs
138
+
139
+ %include "libmemcached/visibility.h"
140
+ %include "libmemcached/memcached.h"
141
+ %include "libmemcached/memcached_constants.h"
142
+ %include "libmemcached/memcached_get.h"
143
+ %include "libmemcached/memcached_storage.h"
144
+ %include "libmemcached/memcached_result.h"
145
+ %include "libmemcached/memcached_server.h"
146
+ %include "libmemcached/memcached_sasl.h"
147
+
148
+ //// Custom C functions
149
+
150
+ //// Manual wrappers
151
+
152
+ // Single get. SWIG likes to use SWIG_FromCharPtr instead of SWIG_FromCharPtrAndSize because
153
+ // of the retval/argout split, so it truncates return values with \0 in them.
154
+ VALUE memcached_get_rvalue(memcached_st *ptr, const char *key, size_t key_length, uint32_t *flags, memcached_return *error);
155
+ %{
156
+ VALUE memcached_get_rvalue(memcached_st *ptr, const char *key, size_t key_length, uint32_t *flags, memcached_return *error) {
157
+ VALUE ret;
158
+ size_t value_length;
159
+ char *value = memcached_get(ptr, key, key_length, &value_length, flags, error);
160
+ ret = rb_str_new(value, value_length);
161
+ free(value);
162
+ return ret;
163
+ };
164
+ %}
165
+
166
+ // Multi get
167
+ VALUE memcached_fetch_rvalue(memcached_st *ptr, char *key, size_t *key_length, uint32_t *flags, memcached_return *error);
168
+ %{
169
+ VALUE memcached_fetch_rvalue(memcached_st *ptr, char *key, size_t *key_length, uint32_t *flags, memcached_return *error) {
170
+ size_t value_length;
171
+ VALUE result = rb_ary_new();
172
+
173
+ char *value = memcached_fetch(ptr, key, key_length, &value_length, flags, error);
174
+ if (value == NULL) {
175
+ rb_ary_push(result, Qnil);
176
+ } else {
177
+ VALUE ret = rb_str_new(value, value_length);
178
+ rb_ary_push(result, ret);
179
+ free(value);
180
+ }
181
+ return result;
182
+ };
183
+ %}
184
+
185
+ // We need to wrap this so it doesn't leak memory. SWIG doesn't want to automatically free. We could
186
+ // maybe use a 'ret' %typemap, but this is ok.
187
+ VALUE memcached_stat_get_rvalue(memcached_st *ptr, memcached_stat_st *stat, char *key, memcached_return *error);
188
+ %{
189
+ VALUE memcached_stat_get_rvalue(memcached_st *ptr, memcached_stat_st *stat, char *key, memcached_return *error) {
190
+ char *str;
191
+ VALUE ret;
192
+ str = memcached_stat_get_value(ptr, stat, key, error);
193
+ ret = rb_str_new2(str);
194
+ free(str);
195
+ return ret;
196
+ };
197
+ %}
198
+
199
+ // Ruby isn't aware that the pointer is an array... there is probably a better way to do this
200
+ memcached_server_st *memcached_select_server_at(memcached_st *in_ptr, int index);
201
+ %{
202
+ memcached_server_st *memcached_select_server_at(memcached_st *in_ptr, int index) {
203
+ return &(in_ptr->hosts[index]);
204
+ };
205
+ %}
206
+
207
+ // Same, but for stats
208
+ memcached_stat_st *memcached_select_stat_at(memcached_st *in_ptr, memcached_stat_st *stat_ptr, int index);
209
+ %{
210
+ memcached_stat_st *memcached_select_stat_at(memcached_st *in_ptr, memcached_stat_st *stat_ptr, int index) {
211
+ return &(stat_ptr[index]);
212
+ };
213
+ %}
214
+
215
+ // Wrap only hash function
216
+ // Uint32
217
+ VALUE memcached_generate_hash_rvalue(const char *key, size_t key_length, memcached_hash hash_algorithm);
218
+ %{
219
+ VALUE memcached_generate_hash_rvalue(const char *key, size_t key_length,memcached_hash hash_algorithm) {
220
+ return UINT2NUM(memcached_generate_hash_value(key, key_length, hash_algorithm));
221
+ };
222
+ %}
223
+
224
+ // Initialization for SASL
225
+ %init %{
226
+ if (sasl_client_init(NULL) != SASL_OK) {
227
+ fprintf(stderr, "Failed to initialized SASL.\n");
228
+ }
229
+ %}
230
+