xunch 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/lib/xunch.rb +25 -0
  3. data/lib/xunch/cache/cache.rb +88 -0
  4. data/lib/xunch/cache/cache_builder.rb +68 -0
  5. data/lib/xunch/cache/field_object_cache.rb +120 -0
  6. data/lib/xunch/cache/list_field_object_cache.rb +63 -0
  7. data/lib/xunch/cache/list_object_cache.rb +59 -0
  8. data/lib/xunch/cache/object_cache.rb +63 -0
  9. data/lib/xunch/codec/codec.rb +31 -0
  10. data/lib/xunch/codec/hash_codec.rb +98 -0
  11. data/lib/xunch/codec/json_codec.rb +81 -0
  12. data/lib/xunch/shard/redis.rb +270 -0
  13. data/lib/xunch/shard/shard_info.rb +37 -0
  14. data/lib/xunch/shard/shard_redis.rb +267 -0
  15. data/lib/xunch/shard/sharded.rb +50 -0
  16. data/lib/xunch/utils/exceptions.rb +11 -0
  17. data/lib/xunch/utils/nginx_cache_helper.rb +52 -0
  18. data/lib/xunch/utils/rb_tree.rb +634 -0
  19. data/lib/xunch/utils/rb_tree_node.rb +67 -0
  20. data/lib/xunch/utils/types.rb +8 -0
  21. data/lib/xunch/utils/utils.rb +24 -0
  22. data/test/benchmark_test.rb +68 -0
  23. data/test/cache_builder_test.rb +28 -0
  24. data/test/cache_object.rb +120 -0
  25. data/test/consistency_hash_test.rb +31 -0
  26. data/test/field_object_cache_test.rb +430 -0
  27. data/test/hash_codec_test.rb +57 -0
  28. data/test/json_codec_test.rb +57 -0
  29. data/test/list_field_object_cache_test.rb +211 -0
  30. data/test/list_object_cache_test.rb +211 -0
  31. data/test/nginx_cache_helper_test.rb +45 -0
  32. data/test/object_cache_test.rb +322 -0
  33. data/test/rb_tree_test.rb +48 -0
  34. data/test/redis_benchmark_test.rb +54 -0
  35. data/test/redis_test.rb +58 -0
  36. data/test/running_test.rb +212 -0
  37. data/test/test.rb +176 -0
  38. data/test/track_record_origin.rb +58 -0
  39. metadata +125 -0
@@ -0,0 +1,37 @@
1
+ module Xunch
2
+ class ShardInfo
3
+ attr_accessor :host, :port, :name, :password, :timeout, :database, :weight
4
+
5
+ DEFAULT_OPTIONS = {
6
+ :host => "127.0.0.1",
7
+ :port => 6379,
8
+ :name => nil,
9
+ :password => nil,
10
+ :timeout => nil,
11
+ :db => 0,
12
+ :weight => 1
13
+ }
14
+
15
+ def initialize(options)
16
+ raise "initialize ShardInfo error, options can not be nil." if options == nil
17
+ @options = DEFAULT_OPTIONS.merge(options)
18
+
19
+ @host = options[:host]
20
+ @port = options[:port]
21
+ @name = options[:name]
22
+ @password = options[:password]
23
+ @timeout = options[:timeout]
24
+ @db = options[:db]
25
+ @weight = options[:weight]
26
+ end
27
+
28
+ def create_resource
29
+ RedisClient.new(@options)
30
+ end
31
+
32
+ def to_s
33
+ "#{@host}:#{@port}::#{@db}*#{@weight}";
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,267 @@
1
+ module Xunch
2
+ class ShardRedis < Sharded
3
+
4
+ def initialize(regex,shard_infos)
5
+ super
6
+ end
7
+
8
+ def exists(key)
9
+ redis = get_shard(key)
10
+ redis.exists(key)
11
+ end
12
+
13
+ def del(key)
14
+ redis = get_shard(key)
15
+ redis.del(key)
16
+ end
17
+
18
+ def batch_del(keys)
19
+ shard_key_map = Hash.new
20
+ shard_index_map = Hash.new
21
+ for i in 0 .. keys.length - 1 do
22
+ redis = get_shard(keys[i])
23
+ if shard_key_map[redis] == nil
24
+ shard_key = Array.new
25
+ shard_key.push(keys[i])
26
+ shard_index = Array.new
27
+ shard_index.push(i)
28
+ shard_key_map[redis] = shard_key
29
+ shard_index_map[redis] = shard_index
30
+ else
31
+ shard_key_map[redis].push(keys[i])
32
+ shard_index_map[redis].push(i)
33
+ end
34
+ end
35
+
36
+ results = Array.new(keys.length)
37
+ shard_key_map.each { | redis, keys |
38
+ values = redis.del(keys)
39
+ shard_index = shard_index_map[redis]
40
+ for i in 0 .. shard_index.length - 1 do
41
+ results[shard_index[i]] = values[i]
42
+ end
43
+ }
44
+ results
45
+ end
46
+
47
+ def expire(key, ttl)
48
+ redis = get_shard(key)
49
+ redis.pexpire(key, ttl)
50
+ end
51
+
52
+ def get_expire(key)
53
+ redis = get_shard(key)
54
+ redis.pttl(key)
55
+ end
56
+
57
+ def get(key)
58
+ redis = get_shard(key)
59
+ redis.get(key)
60
+ end
61
+
62
+ def mget(keys)
63
+ shard_key_map = Hash.new
64
+ shard_index_map = Hash.new
65
+ for i in 0 .. keys.length - 1 do
66
+ redis = get_shard(keys[i])
67
+ if shard_key_map[redis] == nil
68
+ shard_key = Array.new
69
+ shard_key.push(keys[i])
70
+ shard_index = Array.new
71
+ shard_index.push(i)
72
+ shard_key_map[redis] = shard_key
73
+ shard_index_map[redis] = shard_index
74
+ else
75
+ shard_key_map[redis].push(keys[i])
76
+ shard_index_map[redis].push(i)
77
+ end
78
+ end
79
+
80
+ results = Array.new(keys.length)
81
+ shard_key_map.each { | redis, keys |
82
+ values = redis.mget(keys)
83
+ shard_index = shard_index_map[redis]
84
+ for i in 0 .. shard_index.length - 1 do
85
+ results[shard_index[i]] = values[i]
86
+ end
87
+ }
88
+ results
89
+ end
90
+
91
+ def set(key, value, ttl)
92
+ redis = get_shard(key)
93
+ redis.set(key,value,ttl)
94
+ end
95
+
96
+ def mset(key_value_pairs, ttl)
97
+ shard_map = Hash.new
98
+ key_value_pairs.each { | key, value |
99
+ redis = get_shard(key)
100
+ if shard_map[redis] == nil
101
+ shard = Hash.new
102
+ shard[key] = value
103
+ shard_map[redis] = shard
104
+ else
105
+ shard_map[redis][key] = value
106
+ end
107
+ }
108
+ result = []
109
+ shard_map.each { | redis, kvs |
110
+ result.push redis.mset(kvs, ttl)
111
+ }
112
+ return result.flatten!
113
+ end
114
+
115
+ def mapped_hget(key,fields)
116
+ redis = get_shard(key)
117
+ redis.hget(key,*fields)
118
+ end
119
+
120
+ def mapped_hmget(keys,fields)
121
+ shard_key_map = Hash.new
122
+ shard_index_map = Hash.new
123
+ for i in 0 .. keys.length - 1 do
124
+ redis = get_shard(keys[i])
125
+ if shard_key_map[redis] == nil
126
+ shard_key = Array.new
127
+ shard_key.push(keys[i])
128
+ shard_index = Array.new
129
+ shard_index.push(i)
130
+ shard_key_map[redis] = shard_key
131
+ shard_index_map[redis] = shard_index
132
+ else
133
+ shard_key_map[redis].push(keys[i])
134
+ shard_index_map[redis].push(i)
135
+ end
136
+ end
137
+
138
+ results = Array.new(keys.length)
139
+ shard_key_map.each { | redis, keys |
140
+ values = redis.hmget(keys,*fields)
141
+ shard_index = shard_index_map[redis]
142
+ for i in 0 .. shard_index.length - 1 do
143
+ results[shard_index[i]] = values[i]
144
+ end
145
+ }
146
+ results
147
+ end
148
+
149
+ def hgetall(key)
150
+ redis = get_shard(key)
151
+ redis.hgetall(key)
152
+ end
153
+
154
+ def hsetall(key, hash, ttl)
155
+ redis = get_shard(key)
156
+ redis.hsetall(key,hash,ttl)
157
+ end
158
+
159
+ def hmgetall(keys)
160
+ shard_key_map = Hash.new
161
+ shard_index_map = Hash.new
162
+ for i in 0 .. keys.length - 1 do
163
+ redis = get_shard(keys[i])
164
+ if shard_key_map[redis] == nil
165
+ shard_key = Array.new
166
+ shard_key.push(keys[i])
167
+ shard_index = Array.new
168
+ shard_index.push(i)
169
+ shard_key_map[redis] = shard_key
170
+ shard_index_map[redis] = shard_index
171
+ else
172
+ shard_key_map[redis].push(keys[i])
173
+ shard_index_map[redis].push(i)
174
+ end
175
+ end
176
+
177
+ results = Array.new(keys.length)
178
+ shard_key_map.each { | redis, keys |
179
+ values = redis.hmgetall(keys)
180
+ shard_index = shard_index_map[redis]
181
+ for i in 0 .. shard_index.length - 1 do
182
+ results[shard_index[i]] = values[i]
183
+ end
184
+ }
185
+ results
186
+ end
187
+
188
+ def hmsetall(key_value_pairs, ttl)
189
+ shard_map = Hash.new
190
+ key_value_pairs.each { | key, value |
191
+ redis = get_shard(key)
192
+ if shard_map[redis] == nil
193
+ shard = Hash.new
194
+ shard[key] = value
195
+ shard_map[redis] = shard
196
+ else
197
+ shard_map[redis][key] = value
198
+ end
199
+ }
200
+ result = []
201
+ shard_map.each { | redis, kvs |
202
+ result.push redis.hmset(kvs, ttl)
203
+ }
204
+ result.flatten!
205
+ end
206
+
207
+ def mapped_hmget(keys,fields)
208
+ shard_key_map = Hash.new
209
+ shard_index_map = Hash.new
210
+ for i in 0 .. keys.length - 1 do
211
+ redis = get_shard(keys[i])
212
+ if shard_key_map[redis] == nil
213
+ shard_key = Array.new
214
+ shard_key.push(keys[i])
215
+ shard_index = Array.new
216
+ shard_index.push(i)
217
+ shard_key_map[redis] = shard_key
218
+ shard_index_map[redis] = shard_index
219
+ else
220
+ shard_key_map[redis].push(keys[i])
221
+ shard_index_map[redis].push(i)
222
+ end
223
+ end
224
+
225
+ results = Array.new(keys.length)
226
+ shard_key_map.each { | redis, keys |
227
+ values = redis.hmget(keys,*fields)
228
+ shard_index = shard_index_map[redis]
229
+ for i in 0 .. shard_index.length - 1 do
230
+ results[shard_index[i]] = values[i]
231
+ end
232
+ }
233
+ results
234
+ end
235
+
236
+ def llen(key)
237
+ redis = get_shard(key)
238
+ redis.llen(key)
239
+ end
240
+
241
+ def lremove(key,value)
242
+ redis = get_shard(key)
243
+ redis.lrem(key)
244
+ end
245
+
246
+ def lset(temp_key, new_key, sub_keys, ttl)
247
+ redis = get_shard(new_key)
248
+ redis.lset(temp_key,new_key,sub_keys,ttl)
249
+ end
250
+
251
+ def lrange(key, start, stop)
252
+ redis = get_shard(key)
253
+ redis.lrange(key,start,stop)
254
+ end
255
+
256
+ def rename(old_key, new_key)
257
+ redis = get_shard(key)
258
+ redis.rename(old_key,new_key)
259
+ end
260
+
261
+ def type(key)
262
+ redis = get_shard(key)
263
+ redis.type(key)
264
+ end
265
+
266
+ end
267
+ end
@@ -0,0 +1,50 @@
1
+ module Xunch
2
+ # maintain a consistency hash ring
3
+ # this ring must be same as other language xunch sharded
4
+ class Sharded
5
+ include Murmurhash
6
+
7
+ def initialize(regex,shard_infos)
8
+ @regexp = Regexp.new(regex)
9
+ @nodes = RBTree.new
10
+ @resources = Hash.new
11
+ for i in 0 .. shard_infos.length - 1 do
12
+ if (shard_infos[i].name == nil)
13
+ for n in 0 .. 160 * shard_infos[i].weight do
14
+ @nodes.put(Murmurhash.hash2A("SHARD-#{i}-NODE-#{n}"), shard_infos[i]);
15
+ end
16
+ else
17
+ for n in 0 .. 160 * shard_infos[i].weight do
18
+ @nodes.put(Murmurhash.hash2A("#{shard_infos[i].name}-NODE-#{n}"), shard_infos[i]);
19
+ end
20
+ end
21
+ @resources.store(shard_infos[i], shard_infos[i].create_resource);
22
+ end
23
+ end
24
+
25
+ def destroy
26
+ @resources.each { |shard_info, redis_client|
27
+ redis_client.destroy
28
+ }
29
+ @nodes.clear
30
+ @resources.clear
31
+ end
32
+
33
+ protected
34
+ def get_shard(key)
35
+ shard_info = @nodes.ceiling_value(Murmurhash.hash2A(get_keytag(key)))
36
+ if(shard_info == nil)
37
+ shard_info = @nodes.first_value
38
+ end
39
+ shard_redis = @resources[shard_info]
40
+ return shard_redis
41
+ end
42
+
43
+ def get_keytag(key)
44
+ if @regexp != nil
45
+ key = @regexp.match(key)[0]
46
+ end
47
+ return key
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,11 @@
1
+ module Xunch
2
+ class XunchError < StandardError
3
+ end
4
+
5
+ class XunchCodecError < XunchError
6
+ end
7
+
8
+ class XunchConfigError < XunchError
9
+ end
10
+
11
+ end
@@ -0,0 +1,52 @@
1
+ require 'net/http'
2
+
3
+ module Xunch
4
+ class NginxCacheHelper
5
+
6
+ def initialize(domain,port = nil,timeout = nil)
7
+ if domain == nil || domain.strip.empty?
8
+ raise ArgumentError.new("domain can't be nil or empty string.")
9
+ end
10
+ if port == nil
11
+ port = 80
12
+ else
13
+ port = port.to_i
14
+ end
15
+ @http = Net::HTTP.new(domain, port)
16
+ if timeout != nil
17
+ @http.read_timeout = timeout.to_i
18
+ else
19
+ @http.read_timeout = 30
20
+ end
21
+ end
22
+
23
+ def evict(path)
24
+ respose = nil
25
+ begin
26
+ respose = @http.get("/purge" + path)
27
+ rescue SocketError => e
28
+ raise e
29
+ end
30
+ if respose == nil || respose.code != "200"
31
+ return false
32
+ else
33
+ return true
34
+ end
35
+ end
36
+
37
+ def cache(path)
38
+ respose = nil
39
+ begin
40
+ respose = @http.get(path)
41
+ rescue SocketError => e
42
+ raise e
43
+ end
44
+ if respose == nil || respose.code != "200"
45
+ return false
46
+ else
47
+ return true
48
+ end
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,634 @@
1
+ module Xunch
2
+ class RBTree
3
+
4
+
5
+ # The tree's root node.
6
+ attr_reader :root
7
+
8
+ # The number of nodes in the tree.
9
+ attr_reader :size
10
+
11
+ # The key compare proc
12
+ attr_reader :compare_proc
13
+
14
+ # Creates a new tree.
15
+ def initialize(&compare_proc)
16
+ @size = 0
17
+ @root = nil
18
+ @compare_proc = compare_proc
19
+ end
20
+
21
+ def put(key,value)
22
+ tmp_node = @root
23
+ if(tmp_node == nil)
24
+ compare_key(key,key)
25
+ @root = RBTree::Node.new(key,value,nil)
26
+ @size = 1
27
+ return value
28
+ end
29
+ cmp = 0
30
+ parent = nil
31
+
32
+ begin
33
+ parent = tmp_node
34
+ cmp = compare_key(key,parent.key)
35
+ if cmp < 0
36
+ tmp_node = tmp_node.left;
37
+ elsif cmp > 0
38
+ tmp_node = tmp_node.right;
39
+ else
40
+ return tmp_node.value = value;
41
+ end
42
+ end while tmp_node != nil
43
+
44
+ node = RBTree::Node.new(key,value,parent)
45
+
46
+ if cmp < 0
47
+ parent.left = node
48
+ else
49
+ parent.right = node
50
+ end
51
+
52
+ fixafterinsertion(node)
53
+ @size+=1
54
+
55
+ return value
56
+
57
+ end
58
+
59
+ def remove(key)
60
+ p = getnode(key)
61
+ if (p == nil)
62
+ return nil
63
+ end
64
+
65
+ oldValue = p.value
66
+ deletenode(p)
67
+ return oldValue
68
+ end
69
+
70
+ def get(key)
71
+ p = getnode(key);
72
+ return (p == nil ? nil : p.value)
73
+ end
74
+
75
+ def clear
76
+ @size = 0
77
+ @root = nil
78
+ end
79
+
80
+ def size
81
+ @size
82
+ end
83
+
84
+ def first_node
85
+ p = @root;
86
+ if p != nil
87
+ while p.left != nil
88
+ p = p.left
89
+ end
90
+ end
91
+ RBTree::ImmutableNode.new(p.key,p.value)
92
+ end
93
+
94
+ def first_key
95
+ p = @root;
96
+ if p != nil
97
+ while p.left != nil
98
+ p = p.left
99
+ end
100
+ end
101
+ p.key
102
+ end
103
+
104
+ def first_value
105
+ p = @root;
106
+ if p != nil
107
+ while p.left != nil
108
+ p = p.left
109
+ end
110
+ end
111
+ p.value
112
+ end
113
+
114
+ def last_node
115
+ p = @root;
116
+ if p != nil
117
+ while p.right != nil
118
+ p = p.right
119
+ end
120
+ end
121
+ RBTree::ImmutableNode.new(p.key,p.value)
122
+ end
123
+
124
+ def last_key
125
+ p = @root;
126
+ if p != nil
127
+ while p.right != nil
128
+ p = p.right
129
+ end
130
+ end
131
+ p.key
132
+ end
133
+
134
+ def last_value
135
+ p = @root;
136
+ if p != nil
137
+ while p.right != nil
138
+ p = p.right
139
+ end
140
+ end
141
+ p.value
142
+ end
143
+
144
+ def floor_node(key)
145
+ p = @root;
146
+ while p != nil
147
+ cmp = compare_key(key, p.key)
148
+ if cmp > 0
149
+ if p.right != nil
150
+ p = p.right
151
+ else
152
+ if p == nil
153
+ return nil
154
+ else
155
+ return RBTree::ImmutableNode.new(p.key,p.value)
156
+ end
157
+ end
158
+ elsif cmp < 0
159
+ if p.left != nil
160
+ p = p.left
161
+ else
162
+ parent = p.parent
163
+ ch = p
164
+ while parent != nil && ch == parent.left
165
+ ch = parent
166
+ parent = parent.parent;
167
+ end
168
+ if parent == nil
169
+ return nil
170
+ else
171
+ return RBTree::ImmutableNode.new(parent.key,parent.value)
172
+ end
173
+ end
174
+ else
175
+ if p == nil
176
+ return nil
177
+ else
178
+ return RBTree::ImmutableNode.new(p.key,p.value)
179
+ end
180
+ end
181
+ end
182
+
183
+ return nil
184
+ end
185
+
186
+ def floor_key(key)
187
+ node = floor_node(key)
188
+ if node == nil
189
+ return nil
190
+ else
191
+ return node.key
192
+ end
193
+ end
194
+
195
+ def floor_value(key)
196
+ node = floor_node(key)
197
+ if node == nil
198
+ return nil
199
+ else
200
+ return node.value
201
+ end
202
+ end
203
+
204
+ def lower_node(key)
205
+ p = @root;
206
+ while p != nil
207
+ cmp = compare_key(key, p.key)
208
+ if cmp > 0
209
+ if p.right != nil
210
+ p = p.right
211
+ else
212
+ if p == nil
213
+ return nil
214
+ else
215
+ return RBTree::ImmutableNode.new(p.key,p.value)
216
+ end
217
+ end
218
+ else
219
+ if p.left != nil
220
+ p = p.left
221
+ else
222
+ parent = p.parent
223
+ ch = p
224
+ while parent != nil && ch == parent.left
225
+ ch = parent
226
+ parent = parent.parent;
227
+ end
228
+ if parent == nil
229
+ return nil
230
+ else
231
+ return RBTree::ImmutableNode.new(parent.key,parent.value)
232
+ end
233
+ end
234
+ end
235
+ end
236
+ return nil
237
+ end
238
+
239
+ def lower_key(key)
240
+ node = lower_node(key)
241
+ if node == nil
242
+ return nil
243
+ else
244
+ return node.key
245
+ end
246
+ end
247
+
248
+ def lower_value(key)
249
+ node = lower_node(key)
250
+ if node == nil
251
+ return nil
252
+ else
253
+ return node.value
254
+ end
255
+ end
256
+
257
+ def ceiling_node(key)
258
+ p = @root;
259
+ while p != nil
260
+ cmp = compare_key(key, p.key)
261
+ if cmp < 0
262
+ if p.left != nil
263
+ p = p.left
264
+ else
265
+ if p == nil
266
+ return nil
267
+ else
268
+ return RBTree::ImmutableNode.new(p.key,p.value)
269
+ end
270
+ end
271
+ elsif cmp > 0
272
+ if p.right != nil
273
+ p = p.right
274
+ else
275
+ parent = p.parent
276
+ ch = p
277
+ while parent != nil && ch == parent.right
278
+ ch = parent
279
+ parent = parent.parent;
280
+ end
281
+ if parent == nil
282
+ return nil
283
+ else
284
+ return RBTree::ImmutableNode.new(parent.key,parent.value)
285
+ end
286
+ end
287
+ else
288
+ if p == nil
289
+ return nil
290
+ else
291
+ return RBTree::ImmutableNode.new(p.key,p.value)
292
+ end
293
+ end
294
+ end
295
+
296
+ return nil
297
+ end
298
+
299
+ def ceiling_key(key)
300
+ node = ceiling_node(key)
301
+ if node == nil
302
+ return nil
303
+ else
304
+ return node.key
305
+ end
306
+ end
307
+
308
+ def ceiling_value(key)
309
+ node = ceiling_node(key)
310
+ if node == nil
311
+ return nil
312
+ else
313
+ return node.value
314
+ end
315
+ end
316
+
317
+ def higher_node(key)
318
+ p = @root;
319
+ while p != nil
320
+ cmp = compare_key(key, p.key)
321
+ if cmp < 0
322
+ if p.left != nil
323
+ p = p.left
324
+ else
325
+ if p == nil
326
+ return nil
327
+ else
328
+ return RBTree::ImmutableNode.new(p.key,p.value)
329
+ end
330
+ end
331
+ else
332
+ if p.right != nil
333
+ p = p.right
334
+ else
335
+ parent = p.parent
336
+ ch = p
337
+ while parent != nil && ch == parent.right
338
+ ch = parent
339
+ parent = parent.parent;
340
+ end
341
+ if parent == nil
342
+ return nil
343
+ else
344
+ return RBTree::ImmutableNode.new(parent.key,parent.value)
345
+ end
346
+ end
347
+ end
348
+ end
349
+
350
+ return nil
351
+ end
352
+
353
+ def higher_key(key)
354
+ node = higher_node(key)
355
+ if node == nil
356
+ return nil
357
+ else
358
+ return node.key
359
+ end
360
+ end
361
+
362
+ def higher_value(key)
363
+ node = higher_node(key)
364
+ if node == nil
365
+ return nil
366
+ else
367
+ return node.value
368
+ end
369
+ end
370
+
371
+ def successor(t)
372
+ if t == nil
373
+ return nil
374
+ elsif t.right != nil
375
+ p = t.right
376
+ while p.left != nil
377
+ p = p.left
378
+ end
379
+ return p
380
+ else
381
+ p = t.parent
382
+ ch = t
383
+ while p != nil && ch == p.right
384
+ ch = p
385
+ p = p.parent
386
+ end
387
+ return p
388
+ end
389
+ end
390
+
391
+ def deletenode(p)
392
+ @size-=1
393
+
394
+ if p.left != nil && p.right != nil
395
+ s = successor(p)
396
+ p.key = s.key
397
+ p.value = s.value
398
+ p = s
399
+ end
400
+
401
+ replacement = (p.left != nil ? p.left : p.right)
402
+
403
+ if replacement != nil
404
+ replacement.parent = p.parent;
405
+ if p.parent == nil
406
+ @root = replacement
407
+ elsif p == p.parent.left
408
+ p.parent.left = replacement
409
+ else
410
+ p.parent.right = replacement
411
+ end
412
+
413
+ p.left = p.right = p.parent = nil
414
+
415
+ if p.color == :black
416
+ fixafterdeletion(replacement)
417
+ end
418
+ elsif p.parent == nil
419
+ @root = nil
420
+ else
421
+ if p.color == :black
422
+ fixdfterdeletion(p)
423
+ end
424
+ if p.parent != nil
425
+ if p == p.parent.left
426
+ p.parent.left = nil
427
+ elsif p == p.parent.right
428
+ p.parent.right = nil
429
+ end
430
+ p.parent = nil
431
+ end
432
+ end
433
+ end
434
+
435
+ def getnode(key)
436
+ raise "key is nil." unless key != nil
437
+ p = @root;
438
+ while p != nil
439
+ cmp = compare_key(key, p.key)
440
+ if cmp < 0
441
+ p = p.left
442
+ elsif cmp > 0
443
+ p = p.right
444
+ else
445
+ return p
446
+ end
447
+ end
448
+ return nil
449
+ end
450
+
451
+ def fixafterdeletion(x)
452
+ while x != @root && colorof(x) == :black
453
+ if x == leftof(parentof(x))
454
+ sib = rightof(parentof(x))
455
+
456
+ if colorof(sib) == :red
457
+ setcolor(sib, :black)
458
+ setcolor(parentof(x), :red)
459
+ rotateleft(parentof(x))
460
+ sib = rightof(parentof(x))
461
+ end
462
+
463
+ if colorof(leftof(sib)) == :black && colorof(rightof(sib)) == :black
464
+ setcolor(sib, :red)
465
+ x = parentof(x)
466
+ else
467
+ if colorof(rightof(sib)) == :black
468
+ setcolor(leftof(sib), :black)
469
+ setcolor(sib, :red)
470
+ rotateright(sib)
471
+ sib = rightof(parentof(x))
472
+ end
473
+ setcolor(sib, colorof(parentof(x)))
474
+ setcolor(parentof(x), :black)
475
+ setcolor(rightof(sib), :black)
476
+ rotateleft(parentof(x))
477
+ x = @root
478
+ end
479
+ else
480
+ sib = leftof(parentof(x))
481
+
482
+ if colorof(sib) == :red
483
+ setcolor(sib, :black)
484
+ setcolor(parentof(x), :red)
485
+ rotateright(parentof(x))
486
+ sib = leftof(parentof(x))
487
+ end
488
+
489
+ if colorof(rightof(sib)) == :black && colorof(leftof(sib)) == :black
490
+ setcolor(sib, :red)
491
+ x = parentof(x)
492
+ else
493
+ if colorof(leftof(sib)) == :black
494
+ setcolor(rightof(sib), :black)
495
+ setcolor(sib, :red)
496
+ rotateleft(sib)
497
+ sib = leftof(parentof(x))
498
+ end
499
+ setcolor(sib, colorof(parentof(x)))
500
+ setcolor(parentof(x), :black)
501
+ setcolor(leftof(sib), :black)
502
+ rotateright(parentof(x))
503
+ x = @root;
504
+ end
505
+ end
506
+ end
507
+ setcolor(x, :black);
508
+ end
509
+
510
+ def fixafterinsertion(x)
511
+ x.color = :red
512
+
513
+ while x != nil && x != @root && x.parent.color == :red
514
+ if parentof(x) == leftof(parentof(parentof(x)))
515
+ y = rightof(parentof(parentof(x)))
516
+ if colorof(y) == :red
517
+ setcolor(parentof(x), :black)
518
+ setcolor(y, :black)
519
+ setcolor(parentof(parentof(x)), :red)
520
+ x = parentof(parentof(x))
521
+ else
522
+ if x == rightof(parentof(x))
523
+ x = parentof(x)
524
+ rotateleft(x)
525
+ end
526
+ setcolor(parentof(x), :black);
527
+ setcolor(parentof(parentof(x)), :red);
528
+ rotateright(parentof(parentof(x)));
529
+ end
530
+ else
531
+ y = leftof(parentof(parentof(x)))
532
+ if colorof(y) == :red
533
+ setcolor(parentof(x), :black)
534
+ setcolor(y, :black)
535
+ setcolor(parentof(parentof(x)), :red)
536
+ x = parentof(parentof(x))
537
+ else
538
+ if x == leftof(parentof(x))
539
+ x = parentof(x)
540
+ rotateright(x)
541
+ end
542
+ setcolor(parentof(x), :black);
543
+ setcolor(parentof(parentof(x)), :red);
544
+ rotateleft(parentof(parentof(x)));
545
+ end
546
+ end
547
+ end
548
+
549
+ @root.color = :black;
550
+ end
551
+
552
+ def rotateleft(p)
553
+ if p != nil
554
+ r = p.right
555
+ p.right = r.left
556
+ if r.left != nil
557
+ r.left.parent = p
558
+ end
559
+ r.parent = p.parent
560
+ if p.parent == nil
561
+ @root = r
562
+ elsif p.parent.left == p
563
+ p.parent.left = r
564
+ else
565
+ p.parent.right = r
566
+ end
567
+ r.left = p
568
+ p.parent = r
569
+ end
570
+ end
571
+
572
+ def rotateright(p)
573
+ if p != nil
574
+ l = p.left
575
+ p.left = l.right
576
+ if l.right != nil
577
+ l.right.parent = p
578
+ end
579
+ l.parent = p.parent
580
+ if p.parent == nil
581
+ @root = l
582
+ elsif p.parent.right == p
583
+ p.parent.right = l
584
+ else
585
+ p.parent.left = l
586
+ end
587
+ l.right = p
588
+ p.parent = l
589
+ end
590
+ end
591
+
592
+ def parentof(node)
593
+ return (node == nil ? nil : node.parent)
594
+ end
595
+
596
+ def leftof(node)
597
+ return (node == nil ? nil : node.left)
598
+ end
599
+
600
+ def rightof(node)
601
+ return (node == nil ? nil : node.right)
602
+ end
603
+
604
+ def colorof(node)
605
+ if node != nil
606
+ node.color
607
+ end
608
+ end
609
+
610
+ def setcolor(node, color)
611
+ if node != nil
612
+ node.color = color
613
+ end
614
+ end
615
+
616
+ def compare_key(key1,key2)
617
+ if !key1.integer? || !key2.integer?
618
+ raise "#{key1} type is #{key1.class}, #{key2} type is #{key2.class}, but need type Integer"
619
+ end
620
+ if @compare_proc != nil
621
+ return @cmp_proc.call(key1, key2)
622
+ else
623
+ if key1 > key2
624
+ return 1
625
+ elsif key1 < key2
626
+ return -1
627
+ elsif key1 == key2
628
+ return 0
629
+ end
630
+ end
631
+ end
632
+
633
+ end
634
+ end