xunch 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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