From nobody at rubyforge.org Sun Mar 4 13:49:54 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Sun, 4 Mar 2007 13:49:54 -0500 (EST)
Subject: [Archipelago-submits] [220] trunk/archipelago: made debug output
from dump better.
Message-ID: <20070304184954.63E6352420C3@rubyforge.org>
Revision: 220
Author: zond
Date: 2007-03-04 13:49:53 -0500 (Sun, 04 Mar 2007)
Log Message:
-----------
made debug output from dump better. made console services able to select drburi as well. lowered validation interval slightly. added lots of debug stuff to dump. made dump not run redistribute within the each-block when redistributing orphan keys. added options to hashish db getters. made sanitation synchronize on a unique lock when fetching instead of the return value.
Modified Paths:
--------------
trunk/archipelago/lib/archipelago/disco.rb
trunk/archipelago/lib/archipelago/dump.rb
trunk/archipelago/lib/archipelago/hashish.rb
trunk/archipelago/lib/archipelago/sanitation.rb
trunk/archipelago/script/officer.rb
trunk/archipelago/script/pirate.rb
trunk/archipelago/script/services.rb
Modified: trunk/archipelago/lib/archipelago/disco.rb
===================================================================
--- trunk/archipelago/lib/archipelago/disco.rb 2007-02-22 22:33:52 UTC (rev 219)
+++ trunk/archipelago/lib/archipelago/disco.rb 2007-03-04 18:49:53 UTC (rev 220)
@@ -54,7 +54,7 @@
# Default pause between trying to validate all services we
# know about.
#
- VALIDATION_INTERVAL = 60
+ VALIDATION_INTERVAL = 30
#
# Only save stuff that we KNOW we want.
#
Modified: trunk/archipelago/lib/archipelago/dump.rb
===================================================================
--- trunk/archipelago/lib/archipelago/dump.rb 2007-02-22 22:33:52 UTC (rev 219)
+++ trunk/archipelago/lib/archipelago/dump.rb 2007-03-04 18:49:53 UTC (rev 220)
@@ -125,6 +125,7 @@
values = @db.duplicates(key).collect do |value|
[value[0...4], value[4..-1]]
end
+ return values
end
#
@@ -178,6 +179,7 @@
@officer.redistribute(key)
@db.delete(key)
rescue Archipelago::Sanitation::NotEnoughDataException => e
+ @debug_callable.call("#{service_id}.check_key(#{key}) got #{e}: #{PP.pp(e.backtrace, "")}") if @debug_callable
# What shall we do in this case?
# * Nothing, and wait for the admins to make a manual check?
# * Send an email someplace?
@@ -211,7 +213,7 @@
break if check_key(key)
end
rescue Exception => e
- # /moo
+ @debug_callable.call("#{service_id}.start_edge_check() got #{e}: #{PP.pp(e.backtrace, "")}") if @debug_callable
end
sleep(@check_interval)
end
@@ -242,6 +244,7 @@
@debug_callable.call("#{service_id}.redistribute_key(#{key}) called") if @debug_callable
@officer.redistribute(key)
rescue Archipelago::Sanitation::NotEnoughDataException => e
+ @debug_callable.call("#{service_id}.redistribute_key(#{key}) got #{e}: #{PP.pp(e.backtrace, "")}") if @debug_callable
# What shall we do in this case?
# * Nothing, and wait for the admins to make a manual check?
# * Send an email someplace?
@@ -263,20 +266,24 @@
#
def lost_peer(record)
@debug_callable.call("#{service_id}.lost_peer(#{record[:service_id]}) called") if @debug_callable
+ keys_to_redistribute = []
if right_before?(record[:service_id])
@debug_callable.call("#{service_id}.lost_peer(#{record[:service_id]}) is right_before, redistributing from #{@officer.second_master_to(service_id)}") if @debug_callable
@db.reverse_each_key do |key|
- redistribute_key(key)
+ keys_to_redistribute << key
break if key < @officer.second_master_to(service_id)
end
end
if right_after?(record[:service_id])
@debug_callable.call("#{service_id}.lost_peer(#{record[:service_id]}) is right_after, redistributing up to #{record[:service_id]}") if @debug_callable
@db.each_key do |key|
- redistribute_key(key)
+ keys_to_redistribute << key
break if key > record[:service_id]
end
end
+ keys_to_redistribute.each do |key|
+ redistribute_key(key)
+ end
end
end
Modified: trunk/archipelago/lib/archipelago/hashish.rb
===================================================================
--- trunk/archipelago/lib/archipelago/hashish.rb 2007-02-22 22:33:52 UTC (rev 219)
+++ trunk/archipelago/lib/archipelago/hashish.rb 2007-03-04 18:49:53 UTC (rev 220)
@@ -271,13 +271,13 @@
return hashish
end
#
- # Returns something acting like an uncached Berkeley Hash DB instance allowing duplicate entries
+ # Returns something acting like a Berkeley Hash DB instance allowing duplicate entries
# and transactions using +name+.
#
- def get_dup_tree(name)
+ def get_dup_tree(name, flags = BDB::CREATE | BDB::NOMMAP)
db = BDB::Hash.open(Pathname.new(File.join(@env.home, name)).expand_path,
nil,
- BDB::CREATE | BDB::NOMMAP,
+ flags,
0,
"env" => @env,
"set_flags" => BDB::DUP)
@@ -285,10 +285,10 @@
return db
end
#
- # Returns something acting like an uncached Berkeley Hash DB instance using +name+.
+ # Returns something acting like a Berkeley Hash DB instance using +name+.
#
- def get_hashish(name)
- db = @env.open_db(BDB::HASH, name, nil, BDB::CREATE | BDB::NOMMAP)
+ def get_hashish(name, flags = BDB::CREATE | BDB::NOMMAP)
+ db = @env.open_db(BDB::HASH, name, nil, flags)
@bdb_dbs << db
return db
end
Modified: trunk/archipelago/lib/archipelago/sanitation.rb
===================================================================
--- trunk/archipelago/lib/archipelago/sanitation.rb 2007-02-22 22:33:52 UTC (rev 219)
+++ trunk/archipelago/lib/archipelago/sanitation.rb 2007-03-04 18:49:53 UTC (rev 220)
@@ -214,18 +214,17 @@
newest_timestamp = "\000\000\000\000"
threads = []
rval = Oneliner::SuperString.new
- rval.extend(MonitorMixin)
+ lock = Archipelago::Current::Lock.new
dump_hash.t_each do |dump_id, nr_of_chunks_available|
site = @sites[dump_id][:service]
begin
chunks = site.fetch(key)
- rval.mon_synchronize do
+ lock.mon_synchronize do
while chunks.size > 0
t, data = chunks.shift
if t > newest_timestamp
rval = Oneliner::SuperString.new
- rval.extend(MonitorMixin)
newest_timestamp = t
end
Modified: trunk/archipelago/script/officer.rb
===================================================================
--- trunk/archipelago/script/officer.rb 2007-02-22 22:33:52 UTC (rev 219)
+++ trunk/archipelago/script/officer.rb 2007-03-04 18:49:53 UTC (rev 220)
@@ -5,6 +5,6 @@
begin
DRb.uri
rescue DRb::DRbServerNotFound
- DRb.start_service
+ DRb.start_service(ENV["DRBURI"])
end
@o = Archipelago::Sanitation::Officer.new
Modified: trunk/archipelago/script/pirate.rb
===================================================================
--- trunk/archipelago/script/pirate.rb 2007-02-22 22:33:52 UTC (rev 219)
+++ trunk/archipelago/script/pirate.rb 2007-03-04 18:49:53 UTC (rev 220)
@@ -5,7 +5,7 @@
begin
DRb.uri
rescue DRb::DRbServerNotFound
- DRb.start_service
+ DRb.start_service(ENV["DRBURI"])
end
@p = Archipelago::Pirate::Captain.new
@p.evaluate!(File.join(File.dirname(__FILE__), 'overloads.rb'))
Modified: trunk/archipelago/script/services.rb
===================================================================
--- trunk/archipelago/script/services.rb 2007-02-22 22:33:52 UTC (rev 219)
+++ trunk/archipelago/script/services.rb 2007-03-04 18:49:53 UTC (rev 220)
@@ -26,7 +26,7 @@
c = Archipelago::Treasure::Chest.new(:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(File.join(ARGV[0], "chest"))))
c.publish!
puts "published #{c.class} with id #{c.service_id}"
-d = Archipelago::Dump::Site.new(:debug_callable => Proc.new do |msg| puts msg end,
+d = Archipelago::Dump::Site.new(:debug_callable => Proc.new do |msg| puts "#{$$}:#{Thread.current}:#{Time.new.to_f}: #{msg}" end,
:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(File.join(ARGV[0], "dump"))))
d.publish!
puts "published #{d.class} with id #{d.service_id}"
From nobody at rubyforge.org Sun Mar 4 17:49:23 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Sun, 4 Mar 2007 17:49:23 -0500 (EST)
Subject: [Archipelago-submits] [221]
trunk/oneliner/lib/oneliner/superstring.rb: better error message
Message-ID: <20070304224923.3F37252420F6@rubyforge.org>
Revision: 221
Author: zond
Date: 2007-03-04 17:49:22 -0500 (Sun, 04 Mar 2007)
Log Message:
-----------
better error message
Modified Paths:
--------------
trunk/oneliner/lib/oneliner/superstring.rb
Modified: trunk/oneliner/lib/oneliner/superstring.rb
===================================================================
--- trunk/oneliner/lib/oneliner/superstring.rb 2007-03-04 18:49:53 UTC (rev 220)
+++ trunk/oneliner/lib/oneliner/superstring.rb 2007-03-04 22:49:22 UTC (rev 221)
@@ -65,7 +65,7 @@
# Returns whether decoding is done.
#
def decode!(chunk)
- raise "chunk is too small for metadata (8 bytes)" unless chunk.size > 7
+ raise "#{chunk.inspect} is too small for metadata (8 bytes)" unless chunk.size > 7
@decode_done = nil
From nobody at rubyforge.org Sun Mar 4 18:32:37 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Sun, 4 Mar 2007 18:32:37 -0500 (EST)
Subject: [Archipelago-submits] [222] trunk/archipelago: commented the big
sanitation test.
Message-ID: <20070304233237.A08DC52420CB@rubyforge.org>
Revision: 222
Author: zond
Date: 2007-03-04 18:32:37 -0500 (Sun, 04 Mar 2007)
Log Message:
-----------
commented the big sanitation test. added recovery and maintenance tests to the big sanitation test. fixed a small irregularity in publishable. made dump not do the redistribute within the each loop. renamed a few methods in dump. made sanitation#next_to? work even if one id doesnt exist anymore.
Modified Paths:
--------------
trunk/archipelago/lib/archipelago/disco.rb
trunk/archipelago/lib/archipelago/dump.rb
trunk/archipelago/lib/archipelago/sanitation.rb
trunk/archipelago/tests/sanitation_test.rb
Modified: trunk/archipelago/lib/archipelago/disco.rb
===================================================================
--- trunk/archipelago/lib/archipelago/disco.rb 2007-03-04 22:49:22 UTC (rev 221)
+++ trunk/archipelago/lib/archipelago/disco.rb 2007-03-04 23:32:37 UTC (rev 222)
@@ -352,7 +352,7 @@
attr_reader :hash
include Archipelago::Current::Synchronized
include Archipelago::Current::ThreadedCollection
- def_delegators :@hash, :[], :size, :each, :empty?, :delete, :values, :keys, :include?
+ def_delegators :@hash, :[], :size, :each, :empty?, :values, :keys, :include?
def initialize(options = {})
super
@hash = options[:hash] || {}
@@ -371,8 +371,9 @@
# Delete +key+.
#
def delete(key)
- value = @hash.delete(key)
+ value = @hash[key]
@jockey.instance_eval do notify_subscribers(:lost, value) end if @jockey && value
+ @hash.delete(key)
end
#
# Merge this locker with another.
Modified: trunk/archipelago/lib/archipelago/dump.rb
===================================================================
--- trunk/archipelago/lib/archipelago/dump.rb 2007-03-04 22:49:22 UTC (rev 221)
+++ trunk/archipelago/lib/archipelago/dump.rb 2007-03-04 23:32:37 UTC (rev 222)
@@ -160,33 +160,21 @@
end
#
- # Checks whether +key+ should reside here.
- #
- # If not, will ask our Archipelago::Sanitation::Officer to
+ # Will ask our Archipelago::Sanitation::Officer to
# redistribute it.
#
# If that succeeded will delete it from our database.
#
- # Will return whether it belongs here.
- #
- def check_key(key)
- if belongs_here?(key)
- @debug_callable.call("#{service_id}.check_key(#{key}) returns true") if @debug_callable
- return true
- else
- begin
- @debug_callable.call("#{service_id}.check_key(#{key}) redistributes, deletes and returns false") if @debug_callable
- @officer.redistribute(key)
- @db.delete(key)
- rescue Archipelago::Sanitation::NotEnoughDataException => e
- @debug_callable.call("#{service_id}.check_key(#{key}) got #{e}: #{PP.pp(e.backtrace, "")}") if @debug_callable
- # What shall we do in this case?
- # * Nothing, and wait for the admins to make a manual check?
- # * Send an email someplace?
- # * Store it somewhere so that the manual check goes faster?
- ensure
- return false
- end
+ def redistribute_and_delete(key)
+ begin
+ @officer.redistribute(key)
+ @db.delete(key)
+ rescue Archipelago::Sanitation::NotEnoughDataException => e
+ @debug_callable.call("#{service_id}.redistribute_and_delete(#{key}) got #{e}: #{PP.pp(e.backtrace, "")}") if @debug_callable
+ # What shall we do in this case?
+ # * Nothing, and wait for the admins to make a manual check?
+ # * Send an email someplace?
+ # * Store it somewhere so that the manual check goes faster?
end
end
@@ -206,16 +194,22 @@
loop do
begin
@debug_callable.call("#{service_id}.start_edge_check doing its thang") if @debug_callable
+ keys_to_check = Set.new
@db.reverse_each_key do |key|
- break if check_key(key)
+ break if belongs_here?(key)
+ keys_to_check << key
end
@db.each_key do |key|
- break if check_key(key)
+ break if belongs_here?(key)
+ keys_to_check << key
end
+ keys_to_check.each do |key|
+ redistribute_and_delete(key)
+ end
+ sleep(@check_interval)
rescue Exception => e
@debug_callable.call("#{service_id}.start_edge_check() got #{e}: #{PP.pp(e.backtrace, "")}") if @debug_callable
end
- sleep(@check_interval)
end
end
end
@@ -239,12 +233,11 @@
#
# Tell the officer to redistribute +key+ and ignore any errors.
#
- def redistribute_key(key)
+ def redistribute(key)
begin
- @debug_callable.call("#{service_id}.redistribute_key(#{key}) called") if @debug_callable
@officer.redistribute(key)
rescue Archipelago::Sanitation::NotEnoughDataException => e
- @debug_callable.call("#{service_id}.redistribute_key(#{key}) got #{e}: #{PP.pp(e.backtrace, "")}") if @debug_callable
+ @debug_callable.call("#{service_id}.redistribute(#{key}) got #{e}: #{PP.pp(e.backtrace, "")}") if @debug_callable
# What shall we do in this case?
# * Nothing, and wait for the admins to make a manual check?
# * Send an email someplace?
@@ -266,7 +259,7 @@
#
def lost_peer(record)
@debug_callable.call("#{service_id}.lost_peer(#{record[:service_id]}) called") if @debug_callable
- keys_to_redistribute = []
+ keys_to_redistribute = Set.new
if right_before?(record[:service_id])
@debug_callable.call("#{service_id}.lost_peer(#{record[:service_id]}) is right_before, redistributing from #{@officer.second_master_to(service_id)}") if @debug_callable
@db.reverse_each_key do |key|
@@ -282,7 +275,7 @@
end
end
keys_to_redistribute.each do |key|
- redistribute_key(key)
+ redistribute(key)
end
end
Modified: trunk/archipelago/lib/archipelago/sanitation.rb
===================================================================
--- trunk/archipelago/lib/archipelago/sanitation.rb 2007-03-04 22:49:22 UTC (rev 221)
+++ trunk/archipelago/lib/archipelago/sanitation.rb 2007-03-04 23:32:37 UTC (rev 222)
@@ -187,11 +187,17 @@
end
#
- # Returns whether +service_id1+ and +service_id2+
- # are in that order in the array.
+ # Returns whether +service_id1+ and +service_id2+ would come in that
+ # order in the site array if both existed.
#
def next_to?(service_id1, service_id2)
- return @sites.include?(service_id1) && get_least_greater_than(@sites, service_id1, 1).first == service_id2
+ if @sites.include?(service_id1)
+ return get_least_greater_than(@sites, service_id1, 1).first <= service_id2
+ elsif @sites.include?(service_id2)
+ return get_greatest_less_than(@sites, service_id2, 1).first >= service_id1
+ else
+ return false
+ end
end
#
Modified: trunk/archipelago/tests/sanitation_test.rb
===================================================================
--- trunk/archipelago/tests/sanitation_test.rb 2007-03-04 22:49:22 UTC (rev 221)
+++ trunk/archipelago/tests/sanitation_test.rb 2007-03-04 23:32:37 UTC (rev 222)
@@ -34,6 +34,9 @@
end
def test_navigation
+ #
+ # Setup the network
+ #
@d.stop!
dumps = []
cleaner2 = Archipelago::Sanitation::Officer.new(:minimum_nr_of_chunks => 3)
@@ -44,10 +47,16 @@
:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(__FILE__).parent.join("master_test_#{n}.db")))
dumps[n].publish!
end
+ #
+ # Wait until it is alive
+ #
assert_within(30) do
cleaner2.update_services!
dumps.collect do |d| d.service_id end.sort == cleaner2.sites.keys.sort
end
+ #
+ # Perform checks on all dumps
+ #
10.times do |n|
if n < 9
assert(dumps[n].instance_eval do right_before?(dumps[n+1].service_id) end,
@@ -61,6 +70,9 @@
"#{dumps[0].service_id} is supposed to be right_after? #{dumps[n].service_id}, but isnt. services are #{cleaner2.sites.keys.inspect}")
end
end
+ #
+ # Check that only fresh data is returned
+ #
s1 = Oneliner::SuperString.new("brappa")
s2 = Oneliner::SuperString.new("brappa2")
assert_equal(0, dumps[0].db.size)
@@ -69,7 +81,10 @@
dumps[1].insert!("a", [s2.encode(200)], "aaab")
assert(cleaner2.responsible_sites("a").include?(dumps[0].service_id))
assert(cleaner2.responsible_sites("a").include?(dumps[1].service_id))
- assert_equal("brappa2", cleaner2["a"])
+ assert_equal(s2.to_s, cleaner2["a"])
+ #
+ # Check the utility methods of sanitation
+ #
assert_equal([dumps[9].service_id, dumps[0].service_id, dumps[1].service_id].sort,
cleaner2.instance_eval do
get_greatest_less_than(@sites, dumps[2].service_id, 3)
@@ -82,6 +97,55 @@
cleaner2.second_master_to(dumps[4].service_id))
assert_equal(dumps[9].service_id,
cleaner2.second_master_to(dumps[0].service_id))
+ #
+ # Check the recovery methods
+ #
+ assert_equal({
+ "6"=>0,
+ "7"=>0,
+ "8"=>0,
+ "9"=>0,
+ "0"=>1,
+ "1"=>1,
+ "2"=>0,
+ "3"=>0,
+ "4"=>0,
+ "5"=>0
+ },
+ chunks_by_id(cleaner2, "a"))
+ cleaner2.redistribute("a")
+ healthy_chunks = {
+ "6"=>0,
+ "7"=>0,
+ "8"=>0,
+ "9"=>0,
+ "0"=>1,
+ "1"=>1,
+ "2"=>1,
+ "3"=>0,
+ "4"=>0,
+ "5"=>0
+ }
+ assert_equal(healthy_chunks,
+ chunks_by_id(cleaner2, "a"))
+ dumps[1].instance_eval do @valid = false end
+ cleaner2.update_services!
+ dumps[0].instance_eval do lost_peer({:service_id => dumps[1].service_id}) end
+ dumps[2].instance_eval do lost_peer({:service_id => dumps[1].service_id}) end
+ assert_equal({"6"=>0, "7"=>0, "8"=>0, "9"=>0, "0"=>1, "2"=>1, "3"=>1, "4"=>0, "5"=>0},
+ chunks_by_id(cleaner2, "a"))
+
+ dumps[1].instance_eval do @valid = true end
+ assert_within(5) do
+ cleaner2.update_services!
+ ["0","1","2","3","4","5","6","7","8","9"].sort == cleaner2.sites.keys.sort
+ end
+ assert_equal({"0"=>1, "1"=>1, "2"=>1},
+ cleaner2.responsible_sites("a"))
+ dumps[3].instance_eval do @edge_check_thread.wakeup end
+ assert_within(5) do
+ chunks_by_id(cleaner2, "a") == healthy_chunks
+ end
ensure
dumps.extend(Archipelago::Current::ThreadedCollection)
dumps.t_each do |dump|
@@ -98,4 +162,12 @@
assert_equal(@c["hej"], nil)
end
+ private
+
+ def chunks_by_id(officer, key)
+ officer.sites.values.inject({}) do |result, service_desc|
+ result.merge({service_desc[:service_id] => service_desc[:service].fetch(key).size})
+ end
+ end
+
end
From nobody at rubyforge.org Mon Mar 5 04:08:53 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Mon, 5 Mar 2007 04:08:53 -0500 (EST)
Subject: [Archipelago-submits] [223] trunk/archipelago: gave sanitation_test
more generous limits.
Message-ID: <20070305090854.CAAC05240C45@rubyforge.org>
Revision: 223
Author: zond
Date: 2007-03-05 04:08:51 -0500 (Mon, 05 Mar 2007)
Log Message:
-----------
gave sanitation_test more generous limits. readded a few debug messages to dump. made dump more thrifty when recovering lost peers. made hashish actually return a TREE not a HASH when asked for one :O.
Modified Paths:
--------------
trunk/archipelago/lib/archipelago/dump.rb
trunk/archipelago/lib/archipelago/hashish.rb
trunk/archipelago/lib/archipelago/sanitation.rb
trunk/archipelago/tests/sanitation_test.rb
Modified: trunk/archipelago/lib/archipelago/dump.rb
===================================================================
--- trunk/archipelago/lib/archipelago/dump.rb 2007-03-04 23:32:37 UTC (rev 222)
+++ trunk/archipelago/lib/archipelago/dump.rb 2007-03-05 09:08:51 UTC (rev 223)
@@ -167,8 +167,10 @@
#
def redistribute_and_delete(key)
begin
+ @debug_callable.call("#{service_id}.redistribute_and_delete(#{key}) gonna redist and delete") if @debug_callable
@officer.redistribute(key)
@db.delete(key)
+ @debug_callable.call("#{service_id}.redistribute_and_delete(#{key}) done redistributing") if @debug_callable
rescue Archipelago::Sanitation::NotEnoughDataException => e
@debug_callable.call("#{service_id}.redistribute_and_delete(#{key}) got #{e}: #{PP.pp(e.backtrace, "")}") if @debug_callable
# What shall we do in this case?
@@ -262,14 +264,13 @@
keys_to_redistribute = Set.new
if right_before?(record[:service_id])
@debug_callable.call("#{service_id}.lost_peer(#{record[:service_id]}) is right_before, redistributing from #{@officer.second_master_to(service_id)}") if @debug_callable
- @db.reverse_each_key do |key|
+ @db.each_key(@officer.second_master_to(service_id)) do |key|
keys_to_redistribute << key
- break if key < @officer.second_master_to(service_id)
end
end
if right_after?(record[:service_id])
@debug_callable.call("#{service_id}.lost_peer(#{record[:service_id]}) is right_after, redistributing up to #{record[:service_id]}") if @debug_callable
- @db.each_key do |key|
+ @db.each_key(@officer.predecessor(record[:service_id])) do |key|
keys_to_redistribute << key
break if key > record[:service_id]
end
Modified: trunk/archipelago/lib/archipelago/hashish.rb
===================================================================
--- trunk/archipelago/lib/archipelago/hashish.rb 2007-03-04 23:32:37 UTC (rev 222)
+++ trunk/archipelago/lib/archipelago/hashish.rb 2007-03-05 09:08:51 UTC (rev 223)
@@ -271,11 +271,11 @@
return hashish
end
#
- # Returns something acting like a Berkeley Hash DB instance allowing duplicate entries
+ # Returns something acting like a Berkeley Btree DB instance allowing duplicate entries
# and transactions using +name+.
#
def get_dup_tree(name, flags = BDB::CREATE | BDB::NOMMAP)
- db = BDB::Hash.open(Pathname.new(File.join(@env.home, name)).expand_path,
+ db = BDB::Btree.open(Pathname.new(File.join(@env.home, name)).expand_path,
nil,
flags,
0,
Modified: trunk/archipelago/lib/archipelago/sanitation.rb
===================================================================
--- trunk/archipelago/lib/archipelago/sanitation.rb 2007-03-04 23:32:37 UTC (rev 222)
+++ trunk/archipelago/lib/archipelago/sanitation.rb 2007-03-05 09:08:51 UTC (rev 223)
@@ -208,6 +208,14 @@
return get_greatest_less_than(@sites, service_id, @minimum_nr_of_chunks - 1).first
end
+ #
+ # Gets the predecessor of +service_id+ in the array of services.
+ #
+ def predecessor(service_id)
+ update_services!
+ return get_greatest_less_than(@sites, service_id, 1).first
+ end
+
private
#
Modified: trunk/archipelago/tests/sanitation_test.rb
===================================================================
--- trunk/archipelago/tests/sanitation_test.rb 2007-03-04 23:32:37 UTC (rev 222)
+++ trunk/archipelago/tests/sanitation_test.rb 2007-03-05 09:08:51 UTC (rev 223)
@@ -136,14 +136,16 @@
chunks_by_id(cleaner2, "a"))
dumps[1].instance_eval do @valid = true end
- assert_within(5) do
+ assert_within(10) do
cleaner2.update_services!
["0","1","2","3","4","5","6","7","8","9"].sort == cleaner2.sites.keys.sort
end
assert_equal({"0"=>1, "1"=>1, "2"=>1},
cleaner2.responsible_sites("a"))
+# dumps[3].instance_eval do @debug_callable = Proc.new do |m| puts m end end
dumps[3].instance_eval do @edge_check_thread.wakeup end
- assert_within(5) do
+ assert_within(10) do
+# pp chunks_by_id(cleaner2, "a")
chunks_by_id(cleaner2, "a") == healthy_chunks
end
ensure
From nobody at rubyforge.org Tue Mar 6 08:28:17 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Tue, 6 Mar 2007 08:28:17 -0500 (EST)
Subject: [Archipelago-submits] [224] trunk/archipelago: moved from
configurable persistence provider to configurable persistence
directory.
Message-ID: <20070306132818.1F5A952409A3@rubyforge.org>
Revision: 224
Author: zond
Date: 2007-03-06 08:28:17 -0500 (Tue, 06 Mar 2007)
Log Message:
-----------
moved from configurable persistence provider to configurable persistence directory. renamed console to console.rb
Modified Paths:
--------------
trunk/archipelago/lib/archipelago/disco.rb
trunk/archipelago/lib/archipelago/dump.rb
trunk/archipelago/script/services.rb
trunk/archipelago/tests/dump_test.rb
trunk/archipelago/tests/pirate_test.rb
trunk/archipelago/tests/sanitation_test.rb
trunk/archipelago/tests/treasure_benchmark.rb
trunk/archipelago/tests/treasure_test.rb
Added Paths:
-----------
trunk/archipelago/script/console.rb
Removed Paths:
-------------
trunk/archipelago/script/console
Modified: trunk/archipelago/lib/archipelago/disco.rb
===================================================================
--- trunk/archipelago/lib/archipelago/disco.rb 2007-03-05 09:08:51 UTC (rev 223)
+++ trunk/archipelago/lib/archipelago/disco.rb 2007-03-06 13:28:17 UTC (rev 224)
@@ -161,7 +161,7 @@
#
# The provider of happy magic persistent hashes of different kinds.
#
- @persistence_provider ||= options[:persistence_provider] || Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(File.expand_path(__FILE__)).parent.join(self.class.name + ".db"))
+ @persistence_provider ||= Archipelago::Hashish::BerkeleyHashishProvider.new(options[:persistence_directory] || Pathname.new(File.expand_path(__FILE__)).parent.join(self.class.name + ".db"))
#
# Stuff that didnt fit in any of the other databases.
#
Modified: trunk/archipelago/lib/archipelago/dump.rb
===================================================================
--- trunk/archipelago/lib/archipelago/dump.rb 2007-03-05 09:08:51 UTC (rev 223)
+++ trunk/archipelago/lib/archipelago/dump.rb 2007-03-06 13:28:17 UTC (rev 224)
@@ -49,12 +49,8 @@
attr_accessor :db, :debug_callable, :officer, :persistence_provider
def initialize(options = {})
+ initialize_publishable(options)
#
- # The provider of happy magic persistent hashes of different kinds.
- #
- @persistence_provider = options[:persistence_provider] || Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(File.expand_path(__FILE__)).parent.join("cove_tanker.db"))
-
- #
# The callable object that will get sent our debug messages if it exists.
#
@debug_callable = options[:debug_callable]
@@ -73,12 +69,6 @@
# The minimum pause between checking if keys belong with us.
#
@check_interval = options[:check_interval] || CHECK_INTERVAL
-
- #
- # Use the given options to initialize the publishable
- # instance variables.
- #
- initialize_publishable(options)
end
#
Deleted: trunk/archipelago/script/console
===================================================================
--- trunk/archipelago/script/console 2007-03-05 09:08:51 UTC (rev 223)
+++ trunk/archipelago/script/console 2007-03-06 13:28:17 UTC (rev 224)
@@ -1,3 +0,0 @@
-#!/usr/bin/env ruby
-
-exec 'irb', '-r', File.join(File.dirname(__FILE__), 'pirate.rb'), '-r', File.join(File.dirname(__FILE__), 'officer.rb'), "-I", 'lib'
Copied: trunk/archipelago/script/console.rb (from rev 219, trunk/archipelago/script/console)
===================================================================
--- trunk/archipelago/script/console.rb (rev 0)
+++ trunk/archipelago/script/console.rb 2007-03-06 13:28:17 UTC (rev 224)
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+
+exec 'irb', '-r', File.join(File.dirname(__FILE__), 'pirate.rb'), '-r', File.join(File.dirname(__FILE__), 'officer.rb'), "-I", 'lib'
Modified: trunk/archipelago/script/services.rb
===================================================================
--- trunk/archipelago/script/services.rb 2007-03-05 09:08:51 UTC (rev 223)
+++ trunk/archipelago/script/services.rb 2007-03-06 13:28:17 UTC (rev 224)
@@ -20,14 +20,14 @@
DRb.start_service
end
-t = Archipelago::Tranny::Manager.new(:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(File.join(ARGV[0], "tranny"))))
+t = Archipelago::Tranny::Manager.new(:persistence_directory => Pathname.new(File.join(ARGV[0], "tranny")))
t.publish!
puts "published #{t.class} with id #{t.service_id}"
-c = Archipelago::Treasure::Chest.new(:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(File.join(ARGV[0], "chest"))))
+c = Archipelago::Treasure::Chest.new(:persistence_directory => Pathname.new(File.join(ARGV[0], "chest")))
c.publish!
puts "published #{c.class} with id #{c.service_id}"
d = Archipelago::Dump::Site.new(:debug_callable => Proc.new do |msg| puts "#{$$}:#{Thread.current}:#{Time.new.to_f}: #{msg}" end,
- :persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(File.join(ARGV[0], "dump"))))
+ :persistence_directory => Pathname.new(File.join(ARGV[0], "dump")))
d.publish!
puts "published #{d.class} with id #{d.service_id}"
Modified: trunk/archipelago/tests/dump_test.rb
===================================================================
--- trunk/archipelago/tests/dump_test.rb 2007-03-05 09:08:51 UTC (rev 223)
+++ trunk/archipelago/tests/dump_test.rb 2007-03-06 13:28:17 UTC (rev 224)
@@ -5,7 +5,7 @@
def setup
DRb.start_service
- @d = Archipelago::Dump::Site.new(:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(__FILE__).parent.join("site.db")))
+ @d = Archipelago::Dump::Site.new(:persistence_directory => Pathname.new(__FILE__).parent.join("site.db"))
end
def teardown
Modified: trunk/archipelago/tests/pirate_test.rb
===================================================================
--- trunk/archipelago/tests/pirate_test.rb 2007-03-05 09:08:51 UTC (rev 223)
+++ trunk/archipelago/tests/pirate_test.rb 2007-03-06 13:28:17 UTC (rev 224)
@@ -7,11 +7,11 @@
DRb.start_service("druby://localhost:#{rand(1000) + 5000}")
@p = Archipelago::Pirate::Captain.new(:chest_description => {:class => "TestChest"},
:tranny_description => {:class => "TestManager"})
- @c = TestChest.new(:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(__FILE__).parent.join("chest.db")))
+ @c = TestChest.new(:persistence_directory => Pathname.new(__FILE__).parent.join("chest.db"))
@c.publish!
- @c2 = TestChest.new(:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(__FILE__).parent.join("chest2.db")))
+ @c2 = TestChest.new(:persistence_directory => Pathname.new(__FILE__).parent.join("chest2.db"))
@c2.publish!
- @tm = TestManager.new(:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(__FILE__).parent.join("tranny.db")))
+ @tm = TestManager.new(:persistence_directory => Pathname.new(__FILE__).parent.join("tranny.db"))
@tm.publish!
assert_within(10) do
@p.update_services!
Modified: trunk/archipelago/tests/sanitation_test.rb
===================================================================
--- trunk/archipelago/tests/sanitation_test.rb 2007-03-05 09:08:51 UTC (rev 223)
+++ trunk/archipelago/tests/sanitation_test.rb 2007-03-06 13:28:17 UTC (rev 224)
@@ -9,7 +9,7 @@
def setup
DRb.start_service
- @d = Archipelago::Dump::Site.new(:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(__FILE__).parent.join("site.db")))
+ @d = Archipelago::Dump::Site.new(:persistence_directory => Pathname.new(__FILE__).parent.join("site.db"))
@d.publish!
@c = Archipelago::Sanitation::Officer.new
assert_within(10) do
@@ -44,7 +44,7 @@
10.times do |n|
dumps[n] = Archipelago::Dump::Site.new(:officer => cleaner2,
:service_description => {:service_id => n.to_s},
- :persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(__FILE__).parent.join("master_test_#{n}.db")))
+ :persistence_directory => Pathname.new(__FILE__).parent.join("master_test_#{n}.db"))
dumps[n].publish!
end
#
Modified: trunk/archipelago/tests/treasure_benchmark.rb
===================================================================
--- trunk/archipelago/tests/treasure_benchmark.rb 2007-03-05 09:08:51 UTC (rev 223)
+++ trunk/archipelago/tests/treasure_benchmark.rb 2007-03-06 13:28:17 UTC (rev 224)
@@ -5,7 +5,7 @@
def setup
DRb.start_service
- @c = TestChest.new(:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(__FILE__).parent.join("chest.db")))
+ @c = TestChest.new(:persistence_directory => Pathname.new(__FILE__).parent.join("chest.db"))
end
def teardown
Modified: trunk/archipelago/tests/treasure_test.rb
===================================================================
--- trunk/archipelago/tests/treasure_test.rb 2007-03-05 09:08:51 UTC (rev 223)
+++ trunk/archipelago/tests/treasure_test.rb 2007-03-06 13:28:17 UTC (rev 224)
@@ -24,9 +24,9 @@
def setup
DRb.start_service
- @c = TestChest.new(:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(__FILE__).parent.join("chest.db")))
- @c2 = TestChest.new(:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(__FILE__).parent.join("chest2.db")))
- @tm = TestManager.new(:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(__FILE__).parent.join("tranny1.db")))
+ @c = TestChest.new(:persistence_directory => Pathname.new(__FILE__).parent.join("chest.db"))
+ @c2 = TestChest.new(:persistence_directory => Pathname.new(__FILE__).parent.join("chest2.db"))
+ @tm = TestManager.new(:persistence_directory => Pathname.new(__FILE__).parent.join("tranny1.db"))
$BURKMAT = 0
$BURKMAT2 = 0
$BURKMAT3 = 0
@@ -87,7 +87,7 @@
def test_around_load
@c["brunis"] = A.new("hirr")
@c.persistence_provider.close!
- @c = TestChest.new(:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(__FILE__).parent.join("chest.db")))
+ @c = TestChest.new(:persistence_directory => Pathname.new(__FILE__).parent.join("chest.db"))
a = @c["brunis"]
assert_equal(1, $BURKMAT3)
assert_equal(1, $BURKMAT4)
From nobody at rubyforge.org Tue Mar 6 09:17:40 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Tue, 6 Mar 2007 09:17:40 -0500 (EST)
Subject: [Archipelago-submits] [225] trunk/archipelago: created a
sanitation_benchmark.
Message-ID: <20070306141740.7D3A352409A0@rubyforge.org>
Revision: 225
Author: zond
Date: 2007-03-06 09:17:40 -0500 (Tue, 06 Mar 2007)
Log Message:
-----------
created a sanitation_benchmark. improved the verbosity of the sanitation_test. prettified disco.rb. improved comments in treasure.rb and tranny.rb
Modified Paths:
--------------
trunk/archipelago/lib/archipelago/disco.rb
trunk/archipelago/lib/archipelago/tranny.rb
trunk/archipelago/lib/archipelago/treasure.rb
trunk/archipelago/tests/sanitation_test.rb
Added Paths:
-----------
trunk/archipelago/tests/sanitation_benchmark.rb
Modified: trunk/archipelago/lib/archipelago/disco.rb
===================================================================
--- trunk/archipelago/lib/archipelago/disco.rb 2007-03-06 13:28:17 UTC (rev 224)
+++ trunk/archipelago/lib/archipelago/disco.rb 2007-03-06 14:17:40 UTC (rev 225)
@@ -90,7 +90,7 @@
@jockey.stop! if defined?(@jockey) && (!defined?(Archipelago::Disco::MC) || @jockey != Archipelago::Disco::MC)
@jockey_options ||= {}
- jockey_options = (@jockey_options || {}).merge(options[:jockey_options] || {})
+ jockey_options = @jockey_options.merge(options[:jockey_options] || {})
if options[:jockey]
@jockey = options[:jockey]
@@ -98,14 +98,14 @@
@jockey.setup(jockey_options)
end
else
- unless jockey_options.empty?
- @jockey = Archipelago::Disco::Jockey.new(jockey_options)
- else
+ if jockey_options.empty?
if defined?(Archipelago::Disco::MC)
@jockey = Archipelago::Disco::MC
else
@jockey = Archipelago::Disco::Jockey.new
end
+ else
+ @jockey = Archipelago::Disco::Jockey.new(jockey_options)
end
end
end
Modified: trunk/archipelago/lib/archipelago/tranny.rb
===================================================================
--- trunk/archipelago/lib/archipelago/tranny.rb 2007-03-06 13:28:17 UTC (rev 224)
+++ trunk/archipelago/lib/archipelago/tranny.rb 2007-03-06 14:17:40 UTC (rev 225)
@@ -99,7 +99,7 @@
#
# Will use a BerkeleyHashishProvider using tranny_manager.db in the same dir to get its hashes
- # if not :persistence_provider is given.
+ # if not :persistence_directory is given.
#
# Will create Transactions timing out after :transaction_timeout seconds or TRANSACTION_TIMEOUT
# if none is given.
Modified: trunk/archipelago/lib/archipelago/treasure.rb
===================================================================
--- trunk/archipelago/lib/archipelago/treasure.rb 2007-03-06 13:28:17 UTC (rev 224)
+++ trunk/archipelago/lib/archipelago/treasure.rb 2007-03-06 14:17:40 UTC (rev 225)
@@ -245,7 +245,7 @@
# Initialize a Chest
#
# Will use a BerkeleyHashishProvider using treasure_chest.db in the same dir to get its hashes
- # if not :persistence_provider is given.
+ # if not :persistence_directory is given.
#
# Will try to recover crashed transaction every :transaction_recovery_interval seconds
# or TRANSACTION_RECOVERY_INTERVAL if none is given.
Added: trunk/archipelago/tests/sanitation_benchmark.rb
===================================================================
--- trunk/archipelago/tests/sanitation_benchmark.rb (rev 0)
+++ trunk/archipelago/tests/sanitation_benchmark.rb 2007-03-06 14:17:40 UTC (rev 225)
@@ -0,0 +1,43 @@
+
+require File.join(File.dirname(__FILE__), 'test_helper')
+
+class SanitationBenchmark < Test::Unit::TestCase
+
+ def setup
+ DRb.start_service
+ @d = Archipelago::Dump::Site.new(:persistence_directory => Pathname.new(__FILE__).parent.join("site.db"))
+ @d.publish!
+ @c = Archipelago::Sanitation::Officer.new
+ assert_within(10) do
+ @c.update_services!
+ s = @c.instance_eval do @sites end
+ s.keys == [@d.service_id]
+ end
+ end
+
+ def teardown
+ @d.stop!
+ @d.instance_eval do @persistence_provider.unlink! end
+ DRb.stop_service
+ end
+
+ def test_set_get
+ k = "apa"
+ v = "gnu"
+ bm("Sanitation#[]=", :n => 100) do
+ @c[k] = v
+ k = k.next
+ end
+ k = "apa"
+ bm("Sanitation#[]", :n => 100) do
+ t = @c[k]
+ k = k.next
+ end
+ k = "apa"
+ bm("Sanitation#delete", :n => 100) do
+ t = @c.delete!(k)
+ k = k.next
+ end
+ end
+
+end
Modified: trunk/archipelago/tests/sanitation_test.rb
===================================================================
--- trunk/archipelago/tests/sanitation_test.rb 2007-03-06 13:28:17 UTC (rev 224)
+++ trunk/archipelago/tests/sanitation_test.rb 2007-03-06 14:17:40 UTC (rev 225)
@@ -3,10 +3,6 @@
class SanitationTest < Test::Unit::TestCase
- def test_truth
- assert(true)
- end
-
def setup
DRb.start_service
@d = Archipelago::Dump::Site.new(:persistence_directory => Pathname.new(__FILE__).parent.join("site.db"))
@@ -144,9 +140,13 @@
cleaner2.responsible_sites("a"))
# dumps[3].instance_eval do @debug_callable = Proc.new do |m| puts m end end
dumps[3].instance_eval do @edge_check_thread.wakeup end
- assert_within(10) do
-# pp chunks_by_id(cleaner2, "a")
- chunks_by_id(cleaner2, "a") == healthy_chunks
+ begin
+ assert_within(20) do
+ chunks_by_id(cleaner2, "a") == healthy_chunks
+ end
+ rescue Test::Unit::AssertionFailedError => e
+ pp chunks_by_id(cleaner2, "a")
+ raise e
end
ensure
dumps.extend(Archipelago::Current::ThreadedCollection)
From nobody at rubyforge.org Tue Mar 6 10:49:26 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Tue, 6 Mar 2007 10:49:26 -0500 (EST)
Subject: [Archipelago-submits] [226] trunk/archipelago: fixed a borkination
in sanitation_test.
Message-ID: <20070306154926.F01A05241D74@rubyforge.org>
Revision: 226
Author: zond
Date: 2007-03-06 10:49:26 -0500 (Tue, 06 Mar 2007)
Log Message:
-----------
fixed a borkination in sanitation_test. broke BerkeleyHashish into CachedHashish and BerkeleyHashish. added some goddamn lying comments (so far) into treasure.rb
Modified Paths:
--------------
trunk/archipelago/lib/archipelago/hashish.rb
trunk/archipelago/lib/archipelago/tranny.rb
trunk/archipelago/lib/archipelago/treasure.rb
trunk/archipelago/tests/sanitation_test.rb
Modified: trunk/archipelago/lib/archipelago/hashish.rb
===================================================================
--- trunk/archipelago/lib/archipelago/hashish.rb 2007-03-06 14:17:40 UTC (rev 225)
+++ trunk/archipelago/lib/archipelago/hashish.rb 2007-03-06 15:49:26 UTC (rev 226)
@@ -25,49 +25,62 @@
#
module Hashish
- #
- # In essence a Berkeley Database backed Hash.
- #
- # Will cache all values having been written or read
- # in a normal Hash cache for fast access.
- #
- # Will save the last update timestamp for all keys
- # in a separate Hash cache AND a separate Berkeley Database.
- #
- class BerkeleyHashish
- include Archipelago::Current::Synchronized
+ module CachedHashish
#
- # Initialize an instance with the +name+ and BDB::Env +env+.
+ # Returns true if what +key+ points to is no longer +value+.
#
- def initialize(name, env)
- super()
- @content_db = env.open_db(BDB::HASH, name, "content", BDB::CREATE)
- @content = {}
- @timestamps_db = env.open_db(BDB::HASH, name, "timestamps", BDB::CREATE | BDB::NOMMAP)
- @timestamps = {}
- @lock = Archipelago::Current::Lock.new
+ def changed?(key, value)
+ raise "You have to implement me!"
end
#
- # Close the @content_db and @timestamps_db behind this BerkeleyHashish
+ # Get the serialized timestamp for +serialized_key+ from the persistent backer.
#
+ def do_get_timestamp_from_db(serialized_key)
+ raise "You have to implement me!"
+ end
+ #
+ # Actually writes +key+ serialized as +serialized_key+ an
+ # +serialized_value+ to the db. Used by write_to_db.
+ #
+ def do_write_to_db(key, serialized_key, serialized_value)
+ raise "You have to implement me!"
+ end
+ #
+ # Close the persistent backers behind this CachedHashish.
+ #
def close!
- @content_db.close
- @timestamps_db.close
+ raise "You have to implement me!"
end
#
- # Returns a deep ( Marshal.load(Marshal.dump(o)) ) clone
- # of the object represented by +key+.
+ # Returns whether the persistent backer contains +key+.
#
- def get_deep_clone(key)
- return Marshal.load(@content_db[Marshal.dump(key)])
+ def db_include?
+ raise "You have to implement me!"
end
#
- # Returns true if this BerkeleyHashish include +key+.
+ # Will perform an actual fetching of +serialized_key+ from the persistent backer.
#
- def include?(key)
- @content.include?(key) || !@content_db[Marshal.dump(key)].nil?
+ def do_get_from_db(serialized_key)
+ raise "You have to implement me!"
end
#
+ # Actually deletes +serialized_key+ from the persistent backer.
+ #
+ def do_delete_from_persistence(serialized_key)
+ raise "You have to implement me!"
+ end
+
+ #
+ # Will do +callable+.call(key, value) for each
+ # key-and-value pair in this Hashish.
+ #
+ # NB: This is totaly thread-unsafe, only do this
+ # for management or rescue!
+ #
+ def each(callable)
+ raise "You have to implement me!"
+ end
+ #
# Simply get the value for the +key+.
#
# Will call value.load_hook and send it
@@ -129,17 +142,70 @@
else
- serialized_value = Marshal.dump(value)
- serialized_key = Marshal.dump(key)
- old_serialized_value = @content_db[serialized_key]
+ write_to_db(key, serialized_key, serialized_value, value) if changed?(key, value)
- write_to_db(key, serialized_key, serialized_value, value) if old_serialized_value && old_serialized_value != serialized_value
-
end
end
end
#
+ # Returns a deep ( Marshal.load(Marshal.dump(o)) ) clone
+ # of the object represented by +key+.
+ #
+ def get_deep_clone(key)
+ return Marshal.load(Marshal.dump(@content[key]))
+ end
+ #
+ # Initializes the cache hashes for this CachedHashish.
+ #
+ def initialize_cached_hashish
+ @content = {}
+ @timestamps = {}
+ @lock = Archipelago::Current::Lock.new
+ end
+ #
+ # Delete +key+ and its value and timestamp.
+ #
+ def delete(key)
+ @lock.synchronize_on(key) do
+
+ serialized_key = Marshal.dump(key)
+
+ @content.delete(key)
+ @timestamps.delete(key)
+ do_delete_from_persistence(serialized_key)
+ end
+ end
+ #
+ # Read +key+ from db and if it is found
+ # put it in the cache Hash.
+ #
+ # Will call value.load_hook and send it
+ # a block that does the actuall insertion of the value
+ # into the live hash if value.respond_to?(:load_hook).
+ #
+ def get_from_db(key)
+ serialized_key = Marshal.dump(key)
+ serialized_value = do_get_from_db(serialized_key)
+ return nil unless serialized_value
+
+ value = Marshal.load(serialized_value)
+ if value.respond_to?(:load_hook)
+ value.load_hook do
+ @content[key] = value
+ end
+ else
+ @content[key] = value
+ end
+ return value
+ end
+ #
+ # Returns true if this BerkeleyHashish include +key+.
+ #
+ def include?(key)
+ @content.include?(key) || db_include?(key)
+ end
+ #
# Returns the last time the value under +key+ was changed.
#
def timestamp(key)
@@ -149,7 +215,7 @@
return timestamp if timestamp
serialized_key = Marshal.dump(key)
- serialized_timestamp = @timestamps_db[serialized_key]
+ serialized_timestamp = do_get_timestamp_from_db(serialized_key)
return nil unless serialized_timestamp
timestamp = Marshal.load(serialized_timestamp)
@@ -159,6 +225,55 @@
end
end
#
+ # Write +key+, serialized as +serialized_key+ and
+ # +serialized_value+ to the db.
+ #
+ # Will call value.save_hook(old_value) and send
+ # it a block that does the actual saving if
+ # value.respond_to?(:save_hook).
+ #
+ def write_to_db(key, serialized_key, serialized_value, value)
+ if value.respond_to?(:save_hook)
+ old_serialized_value = do_get_from_db(serialized_key)
+ old_value = old_serialized_value ? Marshal.load(old_serialized_value) : nil
+ value.save_hook(old_value) do
+ do_write_to_db(key, serialized_key, serialized_value)
+ end
+ else
+ do_write_to_db(key, serialized_key, serialized_value)
+ end
+ end
+ end
+
+ #
+ # In essence a Berkeley Database backed Hash.
+ #
+ # Will cache all values having been written or read
+ # in a normal Hash cache for fast access.
+ #
+ # Will save the last update timestamp for all keys
+ # in a separate Hash cache AND a separate Berkeley Database.
+ #
+ class BerkeleyHashish
+ include Archipelago::Current::Synchronized
+ include Archipelago::Hashish::CachedHashish
+ #
+ # Initialize an instance with the +name+ and BDB::Env +env+.
+ #
+ def initialize(name, env)
+ super()
+ initialize_cached_hashish
+ @content_db = env.open_db(BDB::HASH, name, "content", BDB::CREATE)
+ @timestamps_db = env.open_db(BDB::HASH, name, "timestamps", BDB::CREATE | BDB::NOMMAP)
+ end
+ #
+ # Close the @content_db and @timestamps_db behind this BerkeleyHashish
+ #
+ def close!
+ @content_db.close
+ @timestamps_db.close
+ end
+ #
# Will do +callable+.call(key, value) for each
# key-and-value pair in this Hashish.
#
@@ -171,42 +286,32 @@
callable.call(key, self.[](key))
end
end
+
+ private
+
#
- # Delete +key+ and its value and timestamp.
+ # Get the serialized timestamp for +serialized_key+ from the persistent backer.
#
- def delete(key)
- @lock.synchronize_on(key) do
+ def do_get_timestamp_from_db(serialized_key)
+ return @timestamps_db[serialized_key]
+ end
- serialized_key = Marshal.dump(key)
-
- @content.delete(key)
+ #
+ # Actually deletes +serialized_key+ from the persistent backer.
+ #
+ def do_delete_from_persistence(serialized_key)
+ @timestamps_db[serialized_key] = nil
@content_db[serialized_key] = nil
- @timestamps.delete(key)
- @timestamps_db[serialized_key] = nil
-
- end
end
-
- private
-
+
#
- # Write +key+, serialized as +serialized_key+ and
- # +serialized_value+ to the db.
+ # Returns true if what +key+ points to is no longer +value+.
#
- # Will call value.save_hook(old_value) and send
- # it a block that does the actual saving if
- # value.respond_to?(:save_hook).
- #
- def write_to_db(key, serialized_key, serialized_value, value)
- if value.respond_to?(:save_hook)
- old_serialized_value = @content_db[serialized_key]
- old_value = old_serialized_value ? Marshal.load(old_serialized_value) : nil
- value.save_hook(old_value) do
- do_write_to_db(key, serialized_key, serialized_value)
- end
- else
- do_write_to_db(key, serialized_key, serialized_value)
- end
+ def changed?(key, value)
+ serialized_value = Marshal.dump(value)
+ serialized_key = Marshal.dump(key)
+ old_serialized_value = @content_db[serialized_key]
+ return old_serialized_value && old_serialized_value != serialized_value
end
#
@@ -220,30 +325,21 @@
@timestamps_db[serialized_key] = Marshal.dump(now)
@timestamps[key] = now
end
+
+ #
+ # Returns whether the persistent backer contains +key+.
+ #
+ def db_include?(key)
+ return !@content_db[Marshal.dump(key)].nil?
+ end
#
- # Read +key+ from db and if it is found
- # put it in the cache Hash.
+ # Will perform an actual fetching of +serialized_key+ from the persistent backer.
#
- # Will call value.load_hook and send it
- # a block that does the actuall insertion of the value
- # into the live hash if value.respond_to?(:load_hook).
- #
- def get_from_db(key)
- serialized_key = Marshal.dump(key)
- serialized_value = @content_db[serialized_key]
- return nil unless serialized_value
-
- value = Marshal.load(serialized_value)
- if value.respond_to?(:load_hook)
- value.load_hook do
- @content[key] = value
- end
- else
- @content[key] = value
- end
- return value
+ def do_get_from_db(serialized_key)
+ return @content_db[serialized_key]
end
+
end
#
@@ -265,10 +361,13 @@
# hash-like instance (see Archipelago::Hashish::BerkeleyHashish)
# using +name+.
#
- def get_cached_hashish(name)
- hashish = BerkeleyHashish.new(name, @env)
- @berkeley_hashishes << hashish
- return hashish
+ def get_cached_hashish(options)
+ if options[:officer]
+ else
+ hashish = BerkeleyHashish.new(options[:name], @env)
+ @berkeley_hashishes << hashish
+ return hashish
+ end
end
#
# Returns something acting like a Berkeley Btree DB instance allowing duplicate entries
Modified: trunk/archipelago/lib/archipelago/tranny.rb
===================================================================
--- trunk/archipelago/lib/archipelago/tranny.rb 2007-03-06 14:17:40 UTC (rev 225)
+++ trunk/archipelago/lib/archipelago/tranny.rb 2007-03-06 15:49:26 UTC (rev 226)
@@ -111,7 +111,7 @@
@transaction_timeout = options[:transaction_timeout] || TRANSACTION_TIMEOUT
- @db = @persistence_provider.get_cached_hashish("db")
+ @db = @persistence_provider.get_cached_hashish(:name => "db")
end
#
Modified: trunk/archipelago/lib/archipelago/treasure.rb
===================================================================
--- trunk/archipelago/lib/archipelago/treasure.rb 2007-03-06 14:17:40 UTC (rev 225)
+++ trunk/archipelago/lib/archipelago/treasure.rb 2007-03-06 15:49:26 UTC (rev 226)
@@ -250,6 +250,9 @@
# Will try to recover crashed transaction every :transaction_recovery_interval seconds
# or TRANSACTION_RECOVERY_INTERVAL if none is given.
#
+ # Will store the actual data in a remote Archipelago::Dump network if :officer is given
+ # or in a local hash if not.
+ #
# Will use Archipelago::Disco::Publishable by calling initialize_publishable with +options+.
#
def initialize(options = {})
@@ -285,7 +288,7 @@
# The magical persistent map that defines how we actually
# store our data.
#
- @db = @persistence_provider.get_cached_hashish("db")
+ @db = @persistence_provider.get_cached_hashish(:name => "db", :officer => options[:officer] || nil)
initialize_prepared(options[:transaction_recovery_interval] || TRANSACTION_RECOVERY_INTERVAL)
Modified: trunk/archipelago/tests/sanitation_test.rb
===================================================================
--- trunk/archipelago/tests/sanitation_test.rb 2007-03-06 14:17:40 UTC (rev 225)
+++ trunk/archipelago/tests/sanitation_test.rb 2007-03-06 15:49:26 UTC (rev 226)
@@ -23,7 +23,7 @@
def test_missing_bits
s1 = Oneliner::SuperString.new("brappa")
- @d.insert!("a", s1.encode(9), "abab")
+ @d.insert!("a", [s1.encode(9)], "abab")
assert_raise(Archipelago::Sanitation::NotEnoughDataException) do
@c["a"]
end
From nobody at rubyforge.org Tue Mar 6 11:44:42 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Tue, 6 Mar 2007 11:44:42 -0500 (EST)
Subject: [Archipelago-submits] [227] trunk/archipelago/lib/archipelago:
created a trial Dump backed hashish.
Message-ID: <20070306164442.B59605242072@rubyforge.org>
Revision: 227
Author: zond
Date: 2007-03-06 11:44:42 -0500 (Tue, 06 Mar 2007)
Log Message:
-----------
created a trial Dump backed hashish. made treasure smarter when selecting hashish
Modified Paths:
--------------
trunk/archipelago/lib/archipelago/hashish.rb
trunk/archipelago/lib/archipelago/treasure.rb
Modified: trunk/archipelago/lib/archipelago/hashish.rb
===================================================================
--- trunk/archipelago/lib/archipelago/hashish.rb 2007-03-06 15:49:26 UTC (rev 226)
+++ trunk/archipelago/lib/archipelago/hashish.rb 2007-03-06 16:44:42 UTC (rev 227)
@@ -25,7 +25,15 @@
#
module Hashish
+ #
+ # In essence a persistence backed Hash.
+ #
+ # Will cache everything including timestamps for last
+ # modification in normal Hashes, but keep everything stored
+ # in a persistency backer defined by the subclass.
+ #
module CachedHashish
+ include Archipelago::Current::Synchronized
#
# Returns true if what +key+ points to is no longer +value+.
#
@@ -40,8 +48,11 @@
end
#
# Actually writes +key+ serialized as +serialized_key+ an
- # +serialized_value+ to the db. Used by write_to_db.
+ # +serialized_value+ to the db, and a new timestamp to the
+ # timestamp db, and the same timestamp to the @timestamp attribute.
#
+ # Used by write_to_db.
+ #
def do_write_to_db(key, serialized_key, serialized_value)
raise "You have to implement me!"
end
@@ -69,7 +80,6 @@
def do_delete_from_persistence(serialized_key)
raise "You have to implement me!"
end
-
#
# Will do +callable+.call(key, value) for each
# key-and-value pair in this Hashish.
@@ -237,25 +247,68 @@
old_serialized_value = do_get_from_db(serialized_key)
old_value = old_serialized_value ? Marshal.load(old_serialized_value) : nil
value.save_hook(old_value) do
- do_write_to_db(key, serialized_key, serialized_value)
+ now = Time.now
+ do_write_to_db(key, serialized_key, serialized_value, now)
+ @timestamps[key] = now
end
else
- do_write_to_db(key, serialized_key, serialized_value)
+ now = Time.now
+ do_write_to_db(key, serialized_key, serialized_value, now)
+ @timestamps[key] = now
end
end
end
#
- # In essence a Berkeley Database backed Hash.
+ # An Archipelago::Dump network backed CachedHashish.
#
- # Will cache all values having been written or read
- # in a normal Hash cache for fast access.
+ class DumpHashish
+ include Archipelago::Hashish::CachedHashish
+ #
+ # Initialize a new DumpHashish with the given +officer+ to find Dumps.
+ #
+ def initialize(officer)
+ super()
+ initialize_cached_hashish
+ @officer = officer
+ @hash_by_key = {}
+ end
+ def changed?(key, value)
+ return (old_hash = @hash_by_key[Marshal.dump(key)]) && old_hash != Digest::SHA1.hexdigest(Marshal.dump(value))
+ end
+ def do_get_timestamp_from_db(serialized_key)
+ return @officer[officer_key(serialized_key, "timestamp")]
+ end
+ def do_write_to_db(key, serialized_key, serialized_value, now)
+ @officer[officer_key(serialized_key, "content")] = serialized_value
+ @officer[officer_key(serialized_key, "timestamp")] = Marshal.dump(now)
+ @hash_by_key[serialized_key] = Digest::SHA1.hexdigest(serialized_value)
+ end
+ def close!
+ end
+ def officer_key(serialized_key, space)
+ Digest::SHA1.hexdigest("DumpHashish:#{space}:#{serialized_key}")
+ end
+ def db_include?(key)
+ return !@officer[officer_key(Marshal.dump(key), "content")].nil?
+ end
+ def do_get_from_db(serialized_key)
+ return @officer[officer_key(Marshal.dump(key), "content")]
+ end
+ def do_delete_from_persistence(serialized_key)
+ @officer.delete(officer_key(serialized_key, "content"))
+ @officer.delete(officer_key(serialized_key, "timestamp"))
+ @hash_by_key[serialized_key] = Digest::SHA1.hexdigest(serialized_value)
+ end
+ def each(callable)
+ raise "You have to implement me!"
+ end
+ end
+
#
- # Will save the last update timestamp for all keys
- # in a separate Hash cache AND a separate Berkeley Database.
+ # A CachedHashish backed by a few BDB::Hashes.
#
class BerkeleyHashish
- include Archipelago::Current::Synchronized
include Archipelago::Hashish::CachedHashish
#
# Initialize an instance with the +name+ and BDB::Env +env+.
@@ -266,80 +319,39 @@
@content_db = env.open_db(BDB::HASH, name, "content", BDB::CREATE)
@timestamps_db = env.open_db(BDB::HASH, name, "timestamps", BDB::CREATE | BDB::NOMMAP)
end
- #
- # Close the @content_db and @timestamps_db behind this BerkeleyHashish
- #
def close!
@content_db.close
@timestamps_db.close
end
- #
- # Will do +callable+.call(key, value) for each
- # key-and-value pair in this Hashish.
- #
- # NB: This is totaly thread-unsafe, only do this
- # for management or rescue!
- #
def each(callable)
@content_db.each do |serialized_key, serialized_value|
key = Marshal.load(serialized_key)
callable.call(key, self.[](key))
end
end
-
- private
-
- #
- # Get the serialized timestamp for +serialized_key+ from the persistent backer.
- #
def do_get_timestamp_from_db(serialized_key)
return @timestamps_db[serialized_key]
end
-
- #
- # Actually deletes +serialized_key+ from the persistent backer.
- #
def do_delete_from_persistence(serialized_key)
- @timestamps_db[serialized_key] = nil
- @content_db[serialized_key] = nil
+ @timestamps_db[serialized_key] = nil
+ @content_db[serialized_key] = nil
end
-
- #
- # Returns true if what +key+ points to is no longer +value+.
- #
def changed?(key, value)
serialized_value = Marshal.dump(value)
serialized_key = Marshal.dump(key)
old_serialized_value = @content_db[serialized_key]
return old_serialized_value && old_serialized_value != serialized_value
end
-
- #
- # Actually writes +key+ serialized as +serialized_key+ an
- # +serialized_value+ to the db. Used by write_to_db.
- #
- def do_write_to_db(key, serialized_key, serialized_value)
- now = Time.now
-
+ def do_write_to_db(key, serialized_key, serialized_value, now)
@content_db[serialized_key] = serialized_value
@timestamps_db[serialized_key] = Marshal.dump(now)
- @timestamps[key] = now
end
-
- #
- # Returns whether the persistent backer contains +key+.
- #
def db_include?(key)
return !@content_db[Marshal.dump(key)].nil?
end
-
- #
- # Will perform an actual fetching of +serialized_key+ from the persistent backer.
- #
def do_get_from_db(serialized_key)
return @content_db[serialized_key]
end
-
end
#
@@ -356,23 +368,17 @@
@berkeley_hashishes = []
@bdb_dbs = []
end
- #
- # Returns a cleverly cached (but slightly inefficient)
- # hash-like instance (see Archipelago::Hashish::BerkeleyHashish)
- # using +name+.
- #
def get_cached_hashish(options)
if options[:officer]
- else
+ hashish = DumpHashish.new(options[:officer])
+ @berkeley_hashishes << hashish
+ return hashish
+ elsif options[:name]
hashish = BerkeleyHashish.new(options[:name], @env)
@berkeley_hashishes << hashish
return hashish
end
end
- #
- # Returns something acting like a Berkeley Btree DB instance allowing duplicate entries
- # and transactions using +name+.
- #
def get_dup_tree(name, flags = BDB::CREATE | BDB::NOMMAP)
db = BDB::Btree.open(Pathname.new(File.join(@env.home, name)).expand_path,
nil,
@@ -383,17 +389,11 @@
@bdb_dbs << db
return db
end
- #
- # Returns something acting like a Berkeley Hash DB instance using +name+.
- #
def get_hashish(name, flags = BDB::CREATE | BDB::NOMMAP)
db = @env.open_db(BDB::HASH, name, nil, flags)
@bdb_dbs << db
return db
end
- #
- # Closes databases opened by this instance.
- #
def close!
@berkeley_hashishes.each do |h|
h.close!
@@ -402,9 +402,6 @@
d.close
end
end
- #
- # Closes databases opened by this instance and removes the persistent files.
- #
def unlink!
close!
home = Pathname.new(@env.home).expand_path
Modified: trunk/archipelago/lib/archipelago/treasure.rb
===================================================================
--- trunk/archipelago/lib/archipelago/treasure.rb 2007-03-06 15:49:26 UTC (rev 226)
+++ trunk/archipelago/lib/archipelago/treasure.rb 2007-03-06 16:44:42 UTC (rev 227)
@@ -288,7 +288,11 @@
# The magical persistent map that defines how we actually
# store our data.
#
- @db = @persistence_provider.get_cached_hashish(:name => "db", :officer => options[:officer] || nil)
+ if options[:officer]
+ @db = @persistence_provider.get_cached_hashish(:officer => options[:officer])
+ else
+ @db = @persistence_provider.get_cached_hashish(:name => "db")
+ end
initialize_prepared(options[:transaction_recovery_interval] || TRANSACTION_RECOVERY_INTERVAL)
From nobody at rubyforge.org Wed Mar 7 05:18:09 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Wed, 7 Mar 2007 05:18:09 -0500 (EST)
Subject: [Archipelago-submits] [228] trunk/archipelago/lib/archipelago: made
Captain#responsible_chest public.
Message-ID: <20070307101809.2DDAB5241CCC@rubyforge.org>
Revision: 228
Author: zond
Date: 2007-03-07 05:18:06 -0500 (Wed, 07 Mar 2007)
Log Message:
-----------
made Captain#responsible_chest public. improved documentation in hashish.rb. made Dubloons more error resilient.
Modified Paths:
--------------
trunk/archipelago/lib/archipelago/hashish.rb
trunk/archipelago/lib/archipelago/pirate.rb
trunk/archipelago/lib/archipelago/treasure.rb
Modified: trunk/archipelago/lib/archipelago/hashish.rb
===================================================================
--- trunk/archipelago/lib/archipelago/hashish.rb 2007-03-06 16:44:42 UTC (rev 227)
+++ trunk/archipelago/lib/archipelago/hashish.rb 2007-03-07 10:18:06 UTC (rev 228)
@@ -48,12 +48,12 @@
end
#
# Actually writes +key+ serialized as +serialized_key+ an
- # +serialized_value+ to the db, and a new timestamp to the
- # timestamp db, and the same timestamp to the @timestamp attribute.
+ # +serialized_value+ to the db, and +timestamp+ to the
+ # timestamp db.
#
# Used by write_to_db.
#
- def do_write_to_db(key, serialized_key, serialized_value)
+ def do_write_to_db(key, serialized_key, serialized_value, timestamp)
raise "You have to implement me!"
end
#
@@ -293,7 +293,9 @@
return !@officer[officer_key(Marshal.dump(key), "content")].nil?
end
def do_get_from_db(serialized_key)
- return @officer[officer_key(Marshal.dump(key), "content")]
+ serialized_value = @officer[officer_key(serialized_key, "content")]
+ @hash_by_key[serialized_key] = Digest::SHA1.hexdigest(serialized_value)
+ return serialized_value
end
def do_delete_from_persistence(serialized_key)
@officer.delete(officer_key(serialized_key, "content"))
Modified: trunk/archipelago/lib/archipelago/pirate.rb
===================================================================
--- trunk/archipelago/lib/archipelago/pirate.rb 2007-03-06 16:44:42 UTC (rev 227)
+++ trunk/archipelago/lib/archipelago/pirate.rb 2007-03-07 10:18:06 UTC (rev 228)
@@ -256,8 +256,6 @@
@trannies = @jockey.lookup(Archipelago::Disco::Query.new(@tranny_description), 0)
end
- private
-
#
# Get the chest responsible for +key+.
#
@@ -272,6 +270,8 @@
return @chests[sorted_chest_ids.first]
end
+ private
+
#
# Make sure all our known chests have evaluated
# all files we need them to.
Modified: trunk/archipelago/lib/archipelago/treasure.rb
===================================================================
--- trunk/archipelago/lib/archipelago/treasure.rb 2007-03-06 16:44:42 UTC (rev 227)
+++ trunk/archipelago/lib/archipelago/treasure.rb 2007-03-07 10:18:06 UTC (rev 228)
@@ -162,7 +162,7 @@
end
#
# Load some instance variables and replace @chest if we know that
- # it actually is not correct.
+ # it is incorrect.
#
def self._load(s)
key, chest, transaction, chest_id = Marshal.load(s)
@@ -212,17 +212,31 @@
begin
return @chest.call_instance_method(@key, meth, @transaction, *args, &block)
rescue DRb::DRbConnError => e
- if defined?(Archipelago::Disco::MC)
- possible_replacements = Archipelago::Disco::MC.lookup(Archipelago::Disco::Query.new({:service_id => @chest_id}))
- raise e if possible_replacements.empty?
-
- @chest = possible_replacements[@chest_id][:service]
- CHEST_BY_SERVICE_ID[@chest_id] = @chest
-
- retry
- else
- raise e
+ #
+ # Here is some fancy rescuing being done.
+ #
+ # First: If we have an MC we will try to reconnect to our actual chest.
+ #
+ # Second: If that failed, we will try to reconnect to the chest that (according to the BLACKBEARD)
+ # is responsible for our key - if it actually HAS our key.
+ #
+ # If this fails, lets raise hell.
+ #
+ possible_replacements = []
+ possible_replacements = Archipelago::Disco::MC.lookup(Archipelago::Disco::Query.new({:service_id => @chest_id})) if defined?(Archipelago::Disco::MC)
+ if possible_replacements.empty? && defined?(Archipelago::Pirate::BLACKBEARD)
+ responsible_chest = Archipelago::Pirate::BLACKBEARD.responsible_chest(@key)
+ possible_replacements << responsible_chest if responsible_chest[:service].include?(@key, @transaction)
end
+ raise e if possible_replacements.empty?
+
+ #
+ # And for all the future dubloons that we unmarshal, lets remember this.
+ #
+ @chest = possible_replacements[@chest_id][:service]
+ CHEST_BY_SERVICE_ID[@chest_id] = @chest
+
+ retry
end
end
From nobody at rubyforge.org Wed Mar 7 05:18:47 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Wed, 7 Mar 2007 05:18:47 -0500 (EST)
Subject: [Archipelago-submits] [229]
trunk/archipelago/lib/archipelago/treasure.rb: beautified
code in treasure.rb
Message-ID: <20070307101847.6DB745241DFB@rubyforge.org>
Revision: 229
Author: zond
Date: 2007-03-07 05:18:46 -0500 (Wed, 07 Mar 2007)
Log Message:
-----------
beautified code in treasure.rb
Modified Paths:
--------------
trunk/archipelago/lib/archipelago/treasure.rb
Modified: trunk/archipelago/lib/archipelago/treasure.rb
===================================================================
--- trunk/archipelago/lib/archipelago/treasure.rb 2007-03-07 10:18:06 UTC (rev 228)
+++ trunk/archipelago/lib/archipelago/treasure.rb 2007-03-07 10:18:46 UTC (rev 229)
@@ -223,7 +223,9 @@
# If this fails, lets raise hell.
#
possible_replacements = []
- possible_replacements = Archipelago::Disco::MC.lookup(Archipelago::Disco::Query.new({:service_id => @chest_id})) if defined?(Archipelago::Disco::MC)
+ if defined?(Archipelago::Disco::MC)
+ possible_replacements = Archipelago::Disco::MC.lookup(Archipelago::Disco::Query.new({:service_id => @chest_id}))
+ end
if possible_replacements.empty? && defined?(Archipelago::Pirate::BLACKBEARD)
responsible_chest = Archipelago::Pirate::BLACKBEARD.responsible_chest(@key)
possible_replacements << responsible_chest if responsible_chest[:service].include?(@key, @transaction)
From nobody at rubyforge.org Wed Mar 7 05:25:44 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Wed, 7 Mar 2007 05:25:44 -0500 (EST)
Subject: [Archipelago-submits] [230]
trunk/archipelago/lib/archipelago/treasure.rb: fixed bugs and
beautified the dubloon rescuing
Message-ID: <20070307102545.128DF5242320@rubyforge.org>
Revision: 230
Author: zond
Date: 2007-03-07 05:25:44 -0500 (Wed, 07 Mar 2007)
Log Message:
-----------
fixed bugs and beautified the dubloon rescuing
Modified Paths:
--------------
trunk/archipelago/lib/archipelago/treasure.rb
Modified: trunk/archipelago/lib/archipelago/treasure.rb
===================================================================
--- trunk/archipelago/lib/archipelago/treasure.rb 2007-03-07 10:18:46 UTC (rev 229)
+++ trunk/archipelago/lib/archipelago/treasure.rb 2007-03-07 10:25:44 UTC (rev 230)
@@ -222,21 +222,23 @@
#
# If this fails, lets raise hell.
#
- possible_replacements = []
+ new_chest_record = nil
if defined?(Archipelago::Disco::MC)
- possible_replacements = Archipelago::Disco::MC.lookup(Archipelago::Disco::Query.new({:service_id => @chest_id}))
+ new_chest_record = Archipelago::Disco::MC.lookup(Archipelago::Disco::Query.new({
+ :service_id => @chest_id
+ }))[@chest_id]
end
- if possible_replacements.empty? && defined?(Archipelago::Pirate::BLACKBEARD)
- responsible_chest = Archipelago::Pirate::BLACKBEARD.responsible_chest(@key)
- possible_replacements << responsible_chest if responsible_chest[:service].include?(@key, @transaction)
+ if new_chest_record.nil? && defined?(Archipelago::Pirate::BLACKBEARD)
+ new_chest_record = Archipelago::Pirate::BLACKBEARD.responsible_chest(@key)
+ new_chest_record = nil unless new_chest_record[:service].include?(@key, @transaction)
end
- raise e if possible_replacements.empty?
+ raise e unless new_chest_record
#
# And for all the future dubloons that we unmarshal, lets remember this.
#
- @chest = possible_replacements[@chest_id][:service]
- CHEST_BY_SERVICE_ID[@chest_id] = @chest
+ @chest = new_chest_record[:service]
+ CHEST_BY_SERVICE_ID[new_chest_record[:service_id]] = @chest
retry
end
From nobody at rubyforge.org Wed Mar 7 06:46:27 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Wed, 7 Mar 2007 06:46:27 -0500 (EST)
Subject: [Archipelago-submits] [231] trunk/archipelago: added a test for a
sanitation backed chest.
Message-ID: <20070307114627.8261D524234E@rubyforge.org>
Revision: 231
Author: zond
Date: 2007-03-07 06:46:27 -0500 (Wed, 07 Mar 2007)
Log Message:
-----------
added a test for a sanitation backed chest. renamed test_navigation to test_multi. made services.rb back the chest with sanitation instead of with bdb. made publishable.stop! also close the @persistence_provider. removed the loops from disco again. removed a lot of over zealous update_services! and added them where they were actually needed. made hashish work properly by moving some serialization from out of the changed? method and not digesting nil in do_get_from_db.
Modified Paths:
--------------
trunk/archipelago/lib/archipelago/disco.rb
trunk/archipelago/lib/archipelago/dump.rb
trunk/archipelago/lib/archipelago/hashish.rb
trunk/archipelago/lib/archipelago/sanitation.rb
trunk/archipelago/lib/archipelago/treasure.rb
trunk/archipelago/script/services.rb
trunk/archipelago/tests/sanitation_test.rb
Modified: trunk/archipelago/lib/archipelago/disco.rb
===================================================================
--- trunk/archipelago/lib/archipelago/disco.rb 2007-03-07 10:25:44 UTC (rev 230)
+++ trunk/archipelago/lib/archipelago/disco.rb 2007-03-07 11:46:27 UTC (rev 231)
@@ -242,6 +242,7 @@
end
end
end
+ @persistence_provider.close!
end
end
@@ -362,18 +363,20 @@
# Set +key+ to +value+.
#
def []=(key, value)
- if @jockey && !@hash.include?(key)
+ existed_before = @hash.include?(key)
+ @hash[key] = value
+ # Notifying AFTER the fact to avoid loops.
+ if @jockey && !existed_before
@jockey.instance_eval do notify_subscribers(:found, value) end
end
- @hash[key] = value
end
#
# Delete +key+.
#
def delete(key)
- value = @hash[key]
+ value = @hash.delete(key)
+ # Notifying AFTER the fact to avoid loops.
@jockey.instance_eval do notify_subscribers(:lost, value) end if @jockey && value
- @hash.delete(key)
end
#
# Merge this locker with another.
Modified: trunk/archipelago/lib/archipelago/dump.rb
===================================================================
--- trunk/archipelago/lib/archipelago/dump.rb 2007-03-07 10:25:44 UTC (rev 230)
+++ trunk/archipelago/lib/archipelago/dump.rb 2007-03-07 11:46:27 UTC (rev 231)
@@ -195,6 +195,7 @@
break if belongs_here?(key)
keys_to_check << key
end
+ @officer.update_services! unless keys_to_check.empty?
keys_to_check.each do |key|
redistribute_and_delete(key)
end
@@ -250,6 +251,7 @@
# it was master to.
#
def lost_peer(record)
+ @officer.update_services!
@debug_callable.call("#{service_id}.lost_peer(#{record[:service_id]}) called") if @debug_callable
keys_to_redistribute = Set.new
if right_before?(record[:service_id])
Modified: trunk/archipelago/lib/archipelago/hashish.rb
===================================================================
--- trunk/archipelago/lib/archipelago/hashish.rb 2007-03-07 10:25:44 UTC (rev 230)
+++ trunk/archipelago/lib/archipelago/hashish.rb 2007-03-07 11:46:27 UTC (rev 231)
@@ -152,7 +152,9 @@
else
- write_to_db(key, serialized_key, serialized_value, value) if changed?(key, value)
+ serialized_value = Marshal.dump(value)
+ serialized_key = Marshal.dump(key)
+ write_to_db(key, serialized_key, serialized_value, value) if changed?(serialized_key, serialized_value)
end
@@ -273,8 +275,8 @@
@officer = officer
@hash_by_key = {}
end
- def changed?(key, value)
- return (old_hash = @hash_by_key[Marshal.dump(key)]) && old_hash != Digest::SHA1.hexdigest(Marshal.dump(value))
+ def changed?(serialized_key, serialized_value)
+ return (old_hash = @hash_by_key[serialized_key]) && old_hash != Digest::SHA1.hexdigest(serialized_value)
end
def do_get_timestamp_from_db(serialized_key)
return @officer[officer_key(serialized_key, "timestamp")]
@@ -294,7 +296,7 @@
end
def do_get_from_db(serialized_key)
serialized_value = @officer[officer_key(serialized_key, "content")]
- @hash_by_key[serialized_key] = Digest::SHA1.hexdigest(serialized_value)
+ @hash_by_key[serialized_key] = Digest::SHA1.hexdigest(serialized_value) if serialized_value
return serialized_value
end
def do_delete_from_persistence(serialized_key)
@@ -338,9 +340,7 @@
@timestamps_db[serialized_key] = nil
@content_db[serialized_key] = nil
end
- def changed?(key, value)
- serialized_value = Marshal.dump(value)
- serialized_key = Marshal.dump(key)
+ def changed?(serialized_key, serialized_value)
old_serialized_value = @content_db[serialized_key]
return old_serialized_value && old_serialized_value != serialized_value
end
Modified: trunk/archipelago/lib/archipelago/sanitation.rb
===================================================================
--- trunk/archipelago/lib/archipelago/sanitation.rb 2007-03-07 10:25:44 UTC (rev 230)
+++ trunk/archipelago/lib/archipelago/sanitation.rb 2007-03-07 11:46:27 UTC (rev 231)
@@ -110,7 +110,6 @@
chunk_size = (super_string.size / nr_of_needed_chunks) + @metadata_overhead
chunk_size = @minimum_recoverable_size / nr_of_needed_chunks if chunk_size < @minimum_recoverable_size / nr_of_needed_chunks
- update_services!
dump_hash = responsible_sites(key)
super_string.encode(8)
dump_hash.t_each do |dump_id, nr_of_chunks_needed|
@@ -123,7 +122,6 @@
end
def delete!(key)
- update_services!
dump_hash = responsible_sites(key)
dump_hash.t_each do |dump_id, nr_of_chunks_available|
@sites[dump_id][:service].delete!(key)
@@ -212,7 +210,6 @@
# Gets the predecessor of +service_id+ in the array of services.
#
def predecessor(service_id)
- update_services!
return get_greatest_less_than(@sites, service_id, 1).first
end
@@ -222,7 +219,6 @@
# Returns [the value for +key+, the timestamp for the value].
#
def fetch(key)
- update_services!
dump_hash = responsible_sites(key)
dump_ids = dump_hash.keys
newest_timestamp = "\000\000\000\000"
Modified: trunk/archipelago/lib/archipelago/treasure.rb
===================================================================
--- trunk/archipelago/lib/archipelago/treasure.rb 2007-03-07 10:25:44 UTC (rev 230)
+++ trunk/archipelago/lib/archipelago/treasure.rb 2007-03-07 11:46:27 UTC (rev 231)
@@ -229,6 +229,7 @@
}))[@chest_id]
end
if new_chest_record.nil? && defined?(Archipelago::Pirate::BLACKBEARD)
+ Archipelago::Pirate::BLACKBEARD.update_services!
new_chest_record = Archipelago::Pirate::BLACKBEARD.responsible_chest(@key)
new_chest_record = nil unless new_chest_record[:service].include?(@key, @transaction)
end
Modified: trunk/archipelago/script/services.rb
===================================================================
--- trunk/archipelago/script/services.rb 2007-03-07 10:25:44 UTC (rev 230)
+++ trunk/archipelago/script/services.rb 2007-03-07 11:46:27 UTC (rev 231)
@@ -19,16 +19,19 @@
else
DRb.start_service
end
+puts "using URI #{DRb.uri}"
t = Archipelago::Tranny::Manager.new(:persistence_directory => Pathname.new(File.join(ARGV[0], "tranny")))
t.publish!
puts "published #{t.class} with id #{t.service_id}"
-c = Archipelago::Treasure::Chest.new(:persistence_directory => Pathname.new(File.join(ARGV[0], "chest")))
-c.publish!
-puts "published #{c.class} with id #{c.service_id}"
-d = Archipelago::Dump::Site.new(:debug_callable => Proc.new do |msg| puts "#{$$}:#{Thread.current}:#{Time.new.to_f}: #{msg}" end,
- :persistence_directory => Pathname.new(File.join(ARGV[0], "dump")))
+
+d = Archipelago::Dump::Site.new(:persistence_directory => Pathname.new(File.join(ARGV[0], "dump")))
d.publish!
puts "published #{d.class} with id #{d.service_id}"
+c = Archipelago::Treasure::Chest.new(:persistence_directory => Pathname.new(File.join(ARGV[0], "chest")),
+ :officer => Archipelago::Sanitation::CLEANER)
+c.publish!
+puts "published #{c.class} with id #{c.service_id}"
+
DRb.thread.join
Modified: trunk/archipelago/tests/sanitation_test.rb
===================================================================
--- trunk/archipelago/tests/sanitation_test.rb 2007-03-07 10:25:44 UTC (rev 230)
+++ trunk/archipelago/tests/sanitation_test.rb 2007-03-07 11:46:27 UTC (rev 231)
@@ -21,6 +21,30 @@
DRb.stop_service
end
+ def test_sanitation_backed_chest
+ chest = Archipelago::Treasure::Chest.new(:persistence_directory => Pathname.new(__FILE__).parent.join("site_chest.db"),
+ :officer => @c)
+ begin
+ assert_equal(nil, chest["klibb"])
+ assert_equal("kladd", chest["klibb"] = "kladd")
+ assert_equal("kladd", chest["klibb"])
+ assert_equal("KLADD", chest["klibb"].upcase!)
+ klibb = chest["klibb"]
+ assert_equal("kladd", klibb.downcase)
+ assert_equal("kladd", klibb.downcase!)
+ assert_equal("KLADD", klibb.upcase)
+ assert_equal("kladd", klibb)
+ @d.stop!
+ @c.update_services!
+ assert_raise(Archipelago::Sanitation::NoRemoteDatabaseAvailableException) do
+ klibb.upcase!
+ end
+ ensure
+ chest.stop!
+ chest.instance_eval do @persistence_provider.unlink! end
+ end
+ end
+
def test_missing_bits
s1 = Oneliner::SuperString.new("brappa")
@d.insert!("a", [s1.encode(9)], "abab")
@@ -29,7 +53,15 @@
end
end
- def test_navigation
+
+ def test_get_set
+ @c["hej"] = "hoho"
+ assert_equal("hoho", @c["hej"])
+ @c.delete!("hej")
+ assert_equal(@c["hej"], nil)
+ end
+
+ def test_multi
#
# Setup the network
#
@@ -157,13 +189,6 @@
end
end
- def test_get_set
- @c["hej"] = "hoho"
- assert_equal("hoho", @c["hej"])
- @c.delete!("hej")
- assert_equal(@c["hej"], nil)
- end
-
private
def chunks_by_id(officer, key)
From nobody at rubyforge.org Wed Mar 7 07:18:32 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Wed, 7 Mar 2007 07:18:32 -0500 (EST)
Subject: [Archipelago-submits] [232] trunk/archipelago/TODO: updated TODO
Message-ID: <20070307121832.A1FC85242322@rubyforge.org>
Revision: 232
Author: zond
Date: 2007-03-07 07:18:32 -0500 (Wed, 07 Mar 2007)
Log Message:
-----------
updated TODO
Modified Paths:
--------------
trunk/archipelago/TODO
Modified: trunk/archipelago/TODO
===================================================================
--- trunk/archipelago/TODO 2007-03-07 11:46:27 UTC (rev 231)
+++ trunk/archipelago/TODO 2007-03-07 12:18:32 UTC (rev 232)
@@ -1,19 +1,4 @@
- * Create a failover/redundancy framework
- * For example: Create a new HashishProvider with built in redundancy,
- for example using the Chord project: http://pdos.csail.mit.edu/chord/
- * Or: Create migration methods that move objects between Chests opon
- startup and shutdown, and make them keep backups at each others
- persistence backends.
- * Or: Create something that stores data the way Chord does (with erasure
- codes) but doesnt use the same look up mechanism.
- * Problem: We still have to implement the entire maintenance protocol
- of Chord (continously checking if our data is safely replicated across
- the network, continously checking that our data belong with us)
-
- * Replace Raider with some well known near-optimal erasure code, for example
- Online Codes: http://en.wikipedia.org/wiki/Online_codes
-
* Make Chest aware about whether transactions have affected it 'for real' ie
check whether the instance before the call differs from the instance after
the call. Preferably without incurring performance lossage.
@@ -30,3 +15,5 @@
* Create a memcached-client that uses Disco instance to find all memcached instances
in the network and distribute requests among them.
+ * Create a file server services that handles really big files within transactions
+ and allows the POSTing and GETing of them via HTTP.
From nobody at rubyforge.org Wed Mar 7 07:19:12 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Wed, 7 Mar 2007 07:19:12 -0500 (EST)
Subject: [Archipelago-submits] [233] trunk/hyperactive/TODO: updated a link
Message-ID: <20070307121912.3D0FE524234F@rubyforge.org>
Revision: 233
Author: zond
Date: 2007-03-07 07:19:12 -0500 (Wed, 07 Mar 2007)
Log Message:
-----------
updated a link
Modified Paths:
--------------
trunk/hyperactive/TODO
Modified: trunk/hyperactive/TODO
===================================================================
--- trunk/hyperactive/TODO 2007-03-07 12:18:32 UTC (rev 232)
+++ trunk/hyperactive/TODO 2007-03-07 12:19:12 UTC (rev 233)
@@ -1,4 +1,4 @@
* Create a validation framework for Hyperactive::Record::Bass
- * Create a sorted data structure, for example AA trees: http://www.eternallyconfuzzled.com/tuts/andersson.html
\ No newline at end of file
+ * Create a sorted data structure, for example AA trees: http://www.eternallyconfuzzled.com/tuts/datastructures/jsw_tut_andersson.aspx
\ No newline at end of file
From nobody at rubyforge.org Thu Mar 8 03:45:22 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Thu, 8 Mar 2007 03:45:22 -0500 (EST)
Subject: [Archipelago-submits] [234] trunk/hyperactive/lib/hyperactive: made
sure Persistent includes Pimp.
Message-ID: <20070308084522.4A8C252423E4@rubyforge.org>
Revision: 234
Author: zond
Date: 2007-03-08 03:45:21 -0500 (Thu, 08 Mar 2007)
Log Message:
-----------
made sure Persistent includes Pimp. made sure with_transaction retains the old transaction.
Modified Paths:
--------------
trunk/hyperactive/lib/hyperactive/index.rb
trunk/hyperactive/lib/hyperactive/record.rb
Modified: trunk/hyperactive/lib/hyperactive/index.rb
===================================================================
--- trunk/hyperactive/lib/hyperactive/index.rb 2007-03-07 12:19:12 UTC (rev 233)
+++ trunk/hyperactive/lib/hyperactive/index.rb 2007-03-08 08:45:21 UTC (rev 234)
@@ -142,7 +142,7 @@
super
base.class_eval do
#
- # We depend on lots of hooks.
+ # We depend on hooks.
#
include(Hyperactive::Hooker::Pimp)
end
Modified: trunk/hyperactive/lib/hyperactive/record.rb
===================================================================
--- trunk/hyperactive/lib/hyperactive/record.rb 2007-03-07 12:19:12 UTC (rev 233)
+++ trunk/hyperactive/lib/hyperactive/record.rb 2007-03-08 08:45:21 UTC (rev 234)
@@ -65,6 +65,10 @@
# The transaction we are currently in.
#
attr_reader :transaction
+ #
+ # We depend on hooks.
+ #
+ include(Hyperactive::Hooker::Pimp)
end
base.extend(ClassMethods)
@@ -86,11 +90,12 @@
# See Hyperactive::List::Head and Hyperactive::Hash::Head for examples of this behaviour.
#
def with_transaction(transaction, &block)
+ old_transaction = @transaction if defined?(@transaction)
@transaction = transaction
begin
return yield
ensure
- @transaction = nil
+ @transaction = old_transaction
end
end
From nobody at rubyforge.org Thu Mar 8 11:30:28 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Thu, 8 Mar 2007 11:30:28 -0500 (EST)
Subject: [Archipelago-submits] [235] trunk/hyperactive: added prototype for
anderson trees
Message-ID: <20070308163028.D148B5241E8E@rubyforge.org>
Revision: 235
Author: zond
Date: 2007-03-08 11:30:27 -0500 (Thu, 08 Mar 2007)
Log Message:
-----------
added prototype for anderson trees
Modified Paths:
--------------
trunk/hyperactive/lib/hyperactive/hash.rb
trunk/hyperactive/lib/hyperactive/index.rb
trunk/hyperactive/lib/hyperactive/record.rb
Added Paths:
-----------
trunk/hyperactive/lib/hyperactive/tree.rb
trunk/hyperactive/tests/tree_test.rb
Modified: trunk/hyperactive/lib/hyperactive/hash.rb
===================================================================
--- trunk/hyperactive/lib/hyperactive/hash.rb 2007-03-08 08:45:21 UTC (rev 234)
+++ trunk/hyperactive/lib/hyperactive/hash.rb 2007-03-08 16:30:27 UTC (rev 235)
@@ -25,8 +25,8 @@
module Hyperactive
#
- # The package containing the Hash class that provides any
- # kind of index for your Hyperactive classes.
+ # The package containing the Hash class that provides unsorted
+ # indices for your Hyperactive classes.
#
# Is supposed to be constantly scaling, but preliminary benchmarks show some
# problems with that assumption?
@@ -110,7 +110,7 @@
# Insert +value+ under +key+ in this Hash.
#
def []=(key, value)
- self.list = Hyperactive::List::Head.get_instance_with_transaction(@transaction) unless self.list
+ self.list ||= Hyperactive::List::Head.get_instance_with_transaction(@transaction)
if (element = Archipelago::Pirate::BLACKBEARD[my_key_for(key), @transaction])
element.value = value
@@ -126,7 +126,7 @@
# Delete +key+ from this Hash.
#
def delete(key)
- self.list = Hyperactive::List::Head.get_instance_with_transaction(@transaction) unless self.list
+ self.list ||= Hyperactive::List::Head.get_instance_with_transaction(@transaction)
return_value = nil
@@ -177,7 +177,7 @@
# Get my private key for a given +key+.
#
def my_key_for(key)
- Digest::SHA1.hexdigest("#{Marshal.dump(key)}#{self.record_id}")
+ Digest::SHA1.hexdigest("Hyperactive::Hash::Head:#{self.record_id}:#{Marshal.dump(key)}")
end
end
Modified: trunk/hyperactive/lib/hyperactive/index.rb
===================================================================
--- trunk/hyperactive/lib/hyperactive/index.rb 2007-03-08 08:45:21 UTC (rev 234)
+++ trunk/hyperactive/lib/hyperactive/index.rb 2007-03-08 16:30:27 UTC (rev 235)
@@ -16,6 +16,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'hyperactive/hash'
+require 'hyperactive/tree'
require 'hyperactive/hooker'
module Hyperactive
Modified: trunk/hyperactive/lib/hyperactive/record.rb
===================================================================
--- trunk/hyperactive/lib/hyperactive/record.rb 2007-03-08 08:45:21 UTC (rev 234)
+++ trunk/hyperactive/lib/hyperactive/record.rb 2007-03-08 16:30:27 UTC (rev 235)
@@ -180,9 +180,19 @@
# which is not what you usually want. Every other time you fetch it using a select or other
# method you will instead receive a proxy object to the database. This means that nothing you
# do to it at that point will be persistent or even necessarily have a defined result.
- # Therefore: do not use the instantiated object, instead call my_instance.save
+ # Therefore: do not use the instantiated object, instead call my_instance.create or MyClass.get_instance
# to get a proxy to the object stored into the database.
#
+ # NB: When a subclass is created with get_instance, initialize is called
+ # and then create is called and its return value returned.
+ # If you instead use get_instance_with_transaction, initialize will be called (like before)
+ # and then create with the given transaction. This means that you can not use transactions in
+ # initialize and at the same time make get_instance_with_transaction work, since
+ # the initialize method wont know about the transaction sent to get_instance_with_transaction.
+ # Of course, this can be avoided using varying degrees of ugly hack, but a simpler way is to try and
+ # make initialize not do transaction-dependant stuff, like create new Hyperactive::Record::Bass
+ # instances, but instead do that in (for example) create_hooks (See Hyperactive::Hooker::Pimp).
+ #
class Bass
include Hyperactive::Record::Persistent
Added: trunk/hyperactive/lib/hyperactive/tree.rb
===================================================================
--- trunk/hyperactive/lib/hyperactive/tree.rb (rev 0)
+++ trunk/hyperactive/lib/hyperactive/tree.rb 2007-03-08 16:30:27 UTC (rev 235)
@@ -0,0 +1,115 @@
+# Archipelago - a distributed computing toolkit for ruby
+# Copyright (C) 2006 Martin Kihlgren
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+
+require 'rubygems'
+require 'archipelago/pirate'
+require 'hyperactive/record'
+require 'digest/sha1'
+
+module Hyperactive
+
+ #
+ # The package containing the Tree class that provides sorted
+ # indices for your Hyperactive classes.
+ #
+ # Is supposed to be logarithmically scaling.
+ #
+ module Tree
+
+ class Node < Hyperactive::Record::Bass
+
+ @@nil_node = nil
+ @@nil_node_record_id = nil
+
+ include Hyperactive::Cleaner::Accessors
+
+ attr_accessor :right, :left, :level, :value, :key
+
+ def initialize(options = {})
+ super()
+ self.right = options[:right] || self.class.nil_node
+ self.right = self if self.right == :self
+ self.left = options[:left] || self.class.nil_node
+ self.left = self if self.left == :self
+ self.level = options[:level] || 1
+ self.value = options[:value]
+ self.key = options[:key]
+ end
+
+ def insert(key, value)
+ if self.nil_node?
+ return Node.get_instance_with_transaction(transaction, :key => key, :value => value)
+ else
+ side = :left
+ side = :right if self.key < key
+ self.send("#{side}=", self.send(side).insert(key, value))
+ rval = self.skew
+ rval = rval.split
+ return rval
+ end
+ end
+
+ def skew
+ end
+
+ def split
+ end
+
+ def nil_node?
+ self.record_id == self.class.nil_node_record_id
+ end
+
+ def self.nil_node_record_id
+ @@nil_node_record_id ||= self.nil_node.record_id
+ end
+
+ def self.nil_node
+ nil_node_key = Digest::SHA1.hexdigest("Hyperactive::Tree::Node::nil_node")
+ @@nil_node ||= if (existing_element = Archipelago::Pirate::BLACKBEARD[nil_node_key])
+ existing_element
+ else
+ rval = Node.get_instance(:left => :self,
+ :right => :self,
+ :level => 0)
+ Archipelago::Pirate::BLACKBEARD[nil_node_key] = rval
+ rval
+ end
+ end
+
+ end
+
+ class Root < Hyperactive::Record::Bass
+
+ include Hyperactive::Cleaner::Accessors
+
+ attr_accessor :root
+
+ def initialize
+ super
+ self.root = Node.nil_node
+ end
+
+ def []=(key, value)
+ self.root = root.insert(key, value)
+ end
+
+ end
+
+ end
+
+end
Added: trunk/hyperactive/tests/tree_test.rb
===================================================================
--- trunk/hyperactive/tests/tree_test.rb (rev 0)
+++ trunk/hyperactive/tests/tree_test.rb 2007-03-08 16:30:27 UTC (rev 235)
@@ -0,0 +1,42 @@
+
+require File.join(File.dirname(__FILE__), 'test_helper')
+
+class TreeTest < Test::Unit::TestCase
+
+ def setup
+ @c = TestChest.new(:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(__FILE__).parent.join("chest.db")))
+ @c.publish!
+ @c2 = TestChest.new(:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(__FILE__).parent.join("chest2.db")))
+ @c2.publish!
+ @tm = TestManager.new(:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(__FILE__).parent.join("tranny1.db")))
+ @tm.publish!
+ Archipelago::Pirate::BLACKBEARD.setup(:chest_description => {:class => 'TestChest'},
+ :tranny_description => {:class => 'TestManager'})
+ Archipelago::Pirate::BLACKBEARD.update_services!
+ assert_within(10) do
+ Archipelago::Pirate::BLACKBEARD.chests.keys.sort == [@c.service_id, @c2.service_id].sort
+ end
+ assert_within(10) do
+ Archipelago::Pirate::BLACKBEARD.trannies.keys == [@tm.service_id]
+ end
+ end
+
+ def teardown
+ @c.stop!
+ @c.persistence_provider.unlink!
+ @c2.stop!
+ @c2.persistence_provider.unlink!
+ @tm.stop!
+ @tm.persistence_provider.unlink!
+ end
+
+ def test_insert
+ t = Hyperactive::Tree::Root.get_instance
+ assert(t.root.nil_node?)
+ t["epa"] = "apa"
+ assert_equal("apa", t.root.value)
+ assert(t.root.left.nil_node?)
+ assert(t.root.right.nil_node?)
+ end
+
+end
From nobody at rubyforge.org Fri Mar 9 05:28:46 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Fri, 9 Mar 2007 05:28:46 -0500 (EST)
Subject: [Archipelago-submits] [236] trunk: added a patched version of rbtree
Message-ID: <20070309102847.301385240952@rubyforge.org>
Revision: 236
Author: zond
Date: 2007-03-09 05:28:46 -0500 (Fri, 09 Mar 2007)
Log Message:
-----------
added a patched version of rbtree
Added Paths:
-----------
trunk/rbtree-0.2.0/
trunk/rbtree-0.2.0/ChangeLog
trunk/rbtree-0.2.0/LICENSE
trunk/rbtree-0.2.0/MANIFEST
trunk/rbtree-0.2.0/README
trunk/rbtree-0.2.0/depend
trunk/rbtree-0.2.0/dict.c
trunk/rbtree-0.2.0/dict.h
trunk/rbtree-0.2.0/extconf.rb
trunk/rbtree-0.2.0/rbtree.c
trunk/rbtree-0.2.0/test.rb
Property changes on: trunk/rbtree-0.2.0
___________________________________________________________________
Name: svn:ignore
+ rbtree.bundle
mkmf.log
Makefile
Added: trunk/rbtree-0.2.0/ChangeLog
===================================================================
--- trunk/rbtree-0.2.0/ChangeLog (rev 0)
+++ trunk/rbtree-0.2.0/ChangeLog 2007-03-09 10:28:46 UTC (rev 236)
@@ -0,0 +1,421 @@
+2007-02-01 OZAWA Takuma
+
+ * version 0.2.0 released.
+
+2007-01-25 OZAWA Takuma
+
+ * rbtree.c (rbtree_readjust): remove a warning.
+
+ * rbtree.c (rbtree_default): should not call default procedure if
+ no key is given.
+
+ * rbtree.c (rbtree_equal): returns true if two rbtrees have same
+ set of key-value set.
+
+2004-10-27 OZAWA Takuma
+
+ * version 0.1.3 released.
+
+2004-07-29 OZAWA Takuma
+
+ * rbtree.c (rbtree_bound): RBTree#bound(lower, upper = lower).
+
+2004-07-11 OZAWA Takuma
+
+ * rbtree.c (MultiRBTree): new class. MultiRBTree allows duplicates
+ of keys.
+
+ * rbtree.c (rbtree_dump, rbtree_s_load): use Array as a storage
+ for performance improvement.
+
+ * rbtree.c (rbtree_equal, rbtree_initialize_copy, rbtree_update):
+ should use rb_obj_is_kind_of.
+
+ * rbtree.c (iter_lev): added to count iterator level for nesting
+ and thread-safety.
+
+2004-06-29 OZAWA Takuma
+
+ * dict.c: remove codes Ruby/RBTree doesn't need. now not supposed
+ to use dict.c with other program.
+
+ * dict.h: ditto.
+
+ * dict.c (dict_equal): remove key_eql argument. use
+ dict->dict_compare to compare keys.
+
+2004-06-20 OZAWA Takuma
+
+ * rbtree.c (rbtree_cmp): use rb_str_cmp if the type of keys is
+ string.
+
+ * rbtree.c (rbtree_cmp): use rb_cmpint.
+
+ * rbtree.c (rbtree_user_cmp): ditto.
+
+2004-06-12 OZAWA Takuma
+
+ * version 0.1.2 release.
+
+2004-05-31 OZAWA Takuma
+
+ * rbtree.c (rbtree_initialize_copy): if an exception is raised in
+ the method rbtree is not modified.
+
+ * rbtree.c (rbtree_delete_if): if an exception is raised in the
+ block rbtree is not modified(no keys are deleted).
+
+ * rbtree.c (rbtree_readjust): use rb_gc_force_recycle.
+
+2004-05-24 OZAWA Takuma
+
+ * rbtree.c (rbtree_initialize_copy): use aset_i. duplicating a key
+ of string is fast because of copy-on-write.
+
+ * rbtree.c (rbtree_readjust): ditto.
+
+ * rbtree.c (rbtree_update): ditto.
+
+ * rbtree.c (rbtree_aset): no dict_lookup for better performance.
+
+ * rbtree.c (rbtree_update): was same whether a block is given or not.
+
+2004-05-23 OZAWA Takuma
+
+ * rbtree.c (inspect_rbtree): add OBJ_INFECTs.
+
+ * rbtree.c (inspect_rbtree): change ``compare'' to ``cmp_proc''.
+
+ * rbtree.c (pp_block): ditto.
+
+2004-04-26 OZAWA Takuma
+
+ * rbtree.c (rbtree_s_create): accept Hash argument.
+
+ * rbtree.c (rbtree_s_create): should just copy keys and values.
+
+2004-02-19 OZAWA Takuma
+
+ * rbtree.c (pp_object_group): use id_object_group.
+
+2004-02-16 OZAWA Takuma
+
+ * version 0.1.1 release.
+
+2004-02-13 OZAWA Takuma
+
+ * README: rewritten.
+
+ * rbtree.c (document): incomplete document for rdoc.
+
+2004-02-08 OZAWA Takuma
+
+ * test.rb (test_pp): add pretty printing test.
+
+2004-02-07 OZAWA Takuma
+
+ * rbtree.c (rbtree_mark): should check rbtree and dict is
+ initialized. Thanks to Neil Spring.
+
+2004-02-05 OZAWA Takuma
+
+ * rbtree.c (prettyprint): polish code.
+
+ * test.rb (assert_raise): alias of assert_raises for Ruby 1.6.x.
+
+2004-02-02 OZAWA Takuma
+
+ * test.rb: clean code. no more RUnit support. use Test::Unit.
+
+2004-01-29 OZAWA Takuma
+
+ * rbtree.c (rbtree_dump): optimization.
+
+ * rbtree.c (rbtree_s_load): ditto.
+
+2004-01-27 OZAWA Takuma
+
+ * rbtree.c (readjust): RBTree#readjust() just readjusts elements
+ using current comparison block. use RBTree#readjust(nil) to set
+ default comparison block.
+
+ * extconf.rb (assertion): removed.
+
+2004-01-14 OZAWA Takuma
+
+ * ctest/test.c: removed.
+
+ * ctest/testlib.c: ditto.
+
+ * ctest/testlib.h: ditto.
+
+ * ctest/Makefile: ditto.
+
+2004-01-04 OZAWA Takuma
+
+ * version 0.1.0 release.
+
+2004-01-03 OZAWA Takuma
+
+ * rbtree.c (rbtree_to_rbtree): new method.
+
+ * rbtree.c (each_pair_i, rbtree_bound_body, rbtree_delete_if_body,
+ select_i, update_block_i): use rb_yield_values.
+
+ * rbtree.c (rbtree_each): RBTree#each should yield single value.
+
+ * rbtree.c (rbtree_select): select(key..) is removed.
+
+ * rbtree.c (rbtree_fetch): always warn if default argument and a
+ block are supplied at the same time.
+
+2003-08-12 takuma ozawa
+
+ * version 0.0.7 release.
+
+2003-08-11 takuma ozawa
+
+ * rbtree.c (rbtree_to_hash): copy default value.
+
+ * rbtree.c (rbtree_readjust): takes a Proc argument.
+
+ * rbtree.c (to_hash_i): optimization.
+
+2003-08-09 takuma ozawa
+
+ * rbtree.c (rbtree_merge): new method.
+
+ * rbtree.c (rbtree_select): select(key..) is deprecated.
+
+ * rbtree.c (rbtree_values_at): new method.
+
+ * rbtree.c (rbtree_initialized_copy): rbtree_copy_object changed to.
+
+2003-07-27 takuma ozawa
+
+ * rbtree.c (rbtree_dump): new method based on Ara Howard's code.
+ Thanks.
+
+ * rbtree.c (rbtree_s_load): ditto.
+
+2003-03-25 takuma ozawa
+
+ * version 0.0.6 release.
+
+2003-02-26 takuma ozawa
+
+ * rbtree.c (rbtree_readjust): rbtree_modify.
+
+2003-02-23 takuma ozawa
+
+ * rbtree.c (rbtree_copy_object): use copy_i.
+
+ * rbtree.c (rbtree_readjust): ditto.
+
+ * rbtree.c (rbtree_aset): not freeze a key.
+
+2003-01-24 takuma ozawa
+
+ * rbtree.c (rbtree_aset): optimization.
+
+2003-01-18 takuma ozawa
+
+ * rbtree.c (rbtree_aset): not raise an exception if a dict is
+ full and the key has been contained.
+
+2003-01-16 takuma ozawa
+
+ * version 0.0.5 release.
+
+2002-12-26 takuma ozawa
+
+ * rbtree.c (rbtree_alloc): new allocation framework.
+
+ * rbtree.c (rbtree_copy_object): changed become to copy_object.
+
+ * rbtree.c (rbtree_cmp): use NUM2INT in case nil returned.
+
+ * extconf.rb (assertion): assertion is off by default.
+
+ * rbtree.c (rbtree_aset): fixed a memory leak occured if the
+ comparison block raises an exception.
+
+2002-11-24 takuma ozawa
+
+ * rbtree.c (rbtree_pretty_print): new method.
+
+ * rbtree.c (rbtree_pretty_print_cycle): new method.
+
+2002-11-22 takuma ozawa
+
+ * rbtree.c (rbtree_inspect): format changed.
+
+ * rbtree.c (rbtree_user_cmp): use NUM2INT.
+
+2002-10-29 takuma ozawa
+
+ * version 0.0.4 release.
+
+2002-10-18 takuma ozawa
+
+ * extconf.rb (assertion): change name to assertion.
+
+2002-10-12 takuma ozawa
+
+ * rbtree.c (rbtree_readjust): assign Qnil to other's dict_context
+ after swap.
+
+ * rbtree.c (rbtree_bound): use dict_compare for range check.
+
+2002-10-10 takuma ozawa
+
+ * rbtree.c (rbtree_readjust): empty dict check.
+
+ * rbtree.c (rbtree_readjust): not use dict_readjust for better
+ readability.
+
+ * rbtree.c (rbtree_become): must copy dict_compare and
+ dict_context before copying nodes.
+
+ * rbtree.c (rbtree_delete_if): rbtree_modify.
+
+ * rbtree.c (rbtree_shift_pop): ditto.
+
+ * rbtree.c (rbtree_update): ditto.
+
+ * test.rb: Test::Unit.
+
+ * rbtree.c (rbtree_readjust): clear other's nodes after swap.
+
+ * rbtree.c (rbtree_readjust): assign Qnil to other's ifnone after
+ swap.
+
+2002-10-07 takuma ozawa
+
+ * rbtree.c (rbtree_update): self assignment check.
+
+ * rbtree.c (rbtree_update): replace duplicate value by return
+ value of block if given.
+
+2002-10-03 takuma ozawa
+
+ * rbtree.c (rbtree_become): self assignment check.
+
+2002-09-24 takuma ozawa
+
+ * rbtree.c (rbtree_cmp_proc): new method.
+
+ * rbtree.c (rbtree_readjust): if no block given, must assign Qnil
+ to context.
+
+ * rbtree.c (rbtree_inspect): format changed.
+
+2002-09-23 takuma ozawa
+
+ * dict.c (dict_equal): empty test must be after similar test.
+
+2002-09-22 takuma ozawa
+
+ * rbtree.c (rbtree_update): convert argument to RBTree.
+
+ * rbtree.c (rbtree_become): ditto.
+
+ * rbtree.c (rbtree_eq): use rb_equal.
+
+ * rbtree.c (rbtree_eql): change name to rbtree_eq.
+
+2002-09-20 takuma ozawa
+
+ * rbtree.c (RBTREE_FL_COPY): removed.
+
+ * ctest/test.c: rewritten in C. CppUnit is not required.
+
+2002-09-18 takuma ozawa
+
+ * depend: new file.
+
+ * rbtree.c (rbtree_equal): no need to check RBTREE_PROC_DEFAULT.
+
+ * extconf.rb (inline) check for inline keyword.
+
+2002-09-17 takuma ozawa
+
+ * version 0.0.3 release.
+
+2002-09-16 takuma ozawa
+
+ * rbtree.c (rbtree_reverse_each): new method.
+
+2002-09-12 takuma ozawa
+
+ * rbtree.c (rbtree_s_create): unset RBTREE_PROC_DEFAULT if the
+ argument is RBTree.
+
+ * rbtree.c (rbtree_clone): use rbtree_become.
+
+ * rbtree.c (version.h): not included.
+
+2002-09-11 takuma ozawa
+
+ * rbtree.c (rbtree_to_a): use OBJ_INFECT.
+
+ * rbtree.c (rbtree_to_hash): ditto.
+
+ * rbtree.c (rbtree_become): replaced by rbtree_replace.
+
+ * rbtree.c (rbtree_replace): removed. replace is implemented
+ useing rbtree_become.
+
+ * rbtree.c (rbtree_first_last): first or pop from empty tree
+ should not return its default proc.
+
+2002-09-09 takuma ozawa
+
+ * rbtree.c (rbtree_become): new method.
+
+ * rbtree.c (rbtree_clone): unset RBTREE_IN_ITERATION.
+
+ * rbtree.c (rbtree_replace): should copy ifnone.
+
+ * rbtree.c (rbtree_shift_pop): shift or pop from empty tree should
+ not return its default proc.
+
+ * rbtree.c (rbtree_default_proc): new method.
+
+ * rbtree.c (rbtree_equal): should check default values and
+ RBTREE_PROC_DEFAULT.
+
+2002-07-19 takuma ozawa
+
+ * dict.c (alloc_node): must return NULL if a memory allocation
+ fails.
+
+2002-07-13 takuma ozawa
+
+ * version 0.0.2 release.
+
+2002-07-11 takuma ozawa
+
+ * rbtree.c (rbtree_readjust): new method.
+
+2002-07-10 takuma ozawa
+
+ * dict.c (dict_clone): clone object must copy must_unfreeze
+ variable.
+
+2002-06-25 takuma ozawa
+
+ * dict.h (dnode_t): add must_freeze flag not to unfreeze already
+ freezed object.
+
+2002-06-22 takuma ozawa
+
+ * rbtree.c (rbtree_first): new method.
+
+ * rbtree.c (rbtree_last): new method.
+
+ * rbtree.c (rbtree_pop): new method.
+
+2002-06-14 takuma ozawa
+
+ * initial release.
Added: trunk/rbtree-0.2.0/LICENSE
===================================================================
--- trunk/rbtree-0.2.0/LICENSE (rev 0)
+++ trunk/rbtree-0.2.0/LICENSE 2007-03-09 10:28:46 UTC (rev 236)
@@ -0,0 +1,22 @@
+Copyright (c) 2002-2004, 2007 OZAWA Takuma
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
Added: trunk/rbtree-0.2.0/MANIFEST
===================================================================
--- trunk/rbtree-0.2.0/MANIFEST (rev 0)
+++ trunk/rbtree-0.2.0/MANIFEST 2007-03-09 10:28:46 UTC (rev 236)
@@ -0,0 +1,10 @@
+ChangeLog
+LICENSE
+MANIFEST
+README
+depend
+dict.c
+dict.h
+extconf.rb
+rbtree.c
+test.rb
Added: trunk/rbtree-0.2.0/README
===================================================================
--- trunk/rbtree-0.2.0/README (rev 0)
+++ trunk/rbtree-0.2.0/README 2007-03-09 10:28:46 UTC (rev 236)
@@ -0,0 +1,74 @@
+=begin
+
+= Ruby/RBTree
+
+RBTree is a sorted associative collection using Red-Black Tree as
+the internal data structure. The elements of RBTree are ordered and
+the interface is the almost same as Hash, so simply you can
+consider RBTree sorted Hash.
+
+Red-Black Tree is a kind of binary tree that automatically balances
+by itself when a node is inserted or deleted. Thus the complexity
+for insert, search and delete is O(log N) in expected and worst
+case. On the other hand the complexity of Hash is O(1). Because
+Hash is unordered the data structure is more effective than
+Red-Black Tree as an associative collection.
+
+The interface of RBTree is the almost same as Hash although there
+are some limitations.
+
+ * While iterating (e.g. in RBTree#each block), RBTree is
+ unmodifiable.
+
+ * Comparison is done using <=> method of key objects. So all types
+ of keys in RBTree should be comparable each other or an arbitrary
+ Proc might be set by RBTree#readjust.
+
+RBTree has a few searching methods that Hash doesn't have. They are
+RBTree#lower_bound, RBTree#upper_bound and RBTree#bound. See document
+of each method for details.
+
+Pretty printing is available for RBTree by using pp.rb. The output
+of pp is easier than p to read. Just call Kernel#pp for the object.
+
+MultiRBTree that allows duplicates of keys is also available.
+
+== Download
+
+ * ((<"Ruby/RBTree 0.2.0"|URL:rbtree-0.2.0.tar.gz>))
+
+== Requirement
+
+ * Ruby 1.6.7 or higher
+
+== Install
+
+ $ tar xzf rbtree-x.x.x.tar.gz
+ $ cd rbtree-x.x.x.tar.gz
+ $ ruby extconf.rb
+ $ make
+ $ make site-install
+
+== Test
+
+ $ ruby test.rb
+
+== Incomplete Document
+
+ $ rdoc rbtree.c
+
+== License
+
+MIT License. Copyright (c) 2002-2004, 2007 OZAWA Takuma.
+
+dict.c and dict.h are modified copies that are originally in Kazlib
+written by Kaz Kylheku. Copyright is held by Kaz Kylheku, see dict.c
+and dict.h for the license. The web page of Kazlib is at
+(()).
+
+== Support
+
+Bug fixes, suggestions and other feedbacks are welcomed. Please mail
+me at (()).
+
+=end
Added: trunk/rbtree-0.2.0/depend
===================================================================
--- trunk/rbtree-0.2.0/depend (rev 0)
+++ trunk/rbtree-0.2.0/depend 2007-03-09 10:28:46 UTC (rev 236)
@@ -0,0 +1,2 @@
+dict.o: dict.c dict.h
+rbtree.o: rbtree.c dict.h
Added: trunk/rbtree-0.2.0/dict.c
===================================================================
--- trunk/rbtree-0.2.0/dict.c (rev 0)
+++ trunk/rbtree-0.2.0/dict.c 2007-03-09 10:28:46 UTC (rev 236)
@@ -0,0 +1,1216 @@
+/*
+ * Dictionary Abstract Data Type
+ * Copyright (C) 1997 Kaz Kylheku
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * $Id: dict.c,v 1.15 2005/10/06 05:16:35 kuma Exp $
+ * $Name: $
+ */
+
+/*
+ * Modified for Ruby/RBTree by OZAWA Takuma.
+ */
+
+#include
+#include
+#include
+#include "dict.h"
+
+#include
+
+#ifdef KAZLIB_RCSID
+static const char rcsid[] = "$Id: dict.c,v 1.15 2005/10/06 05:16:35 kuma Exp $";
+#endif
+
+/*
+ * These macros provide short convenient names for structure members,
+ * which are embellished with dict_ prefixes so that they are
+ * properly confined to the documented namespace. It's legal for a
+ * program which uses dict to define, for instance, a macro called ``parent''.
+ * Such a macro would interfere with the dnode_t struct definition.
+ * In general, highly portable and reusable C modules which expose their
+ * structures need to confine structure member names to well-defined spaces.
+ * The resulting identifiers aren't necessarily convenient to use, nor
+ * readable, in the implementation, however!
+ */
+
+#define left dict_left
+#define right dict_right
+#define parent dict_parent
+#define color dict_color
+#define key dict_key
+#define data dict_data
+
+#define nilnode dict_nilnode
+#define nodecount dict_nodecount
+#define compare dict_compare
+#define allocnode dict_allocnode
+#define freenode dict_freenode
+#define context dict_context
+#define dupes dict_dupes
+
+#define dictptr dict_dictptr
+
+#define dict_root(D) ((D)->nilnode.left)
+#define dict_nil(D) (&(D)->nilnode)
+#define DICT_DEPTH_MAX 64
+
+#define COMPARE(dict, key1, key2) dict->compare(key1, key2, dict->context)
+
+static dnode_t *dnode_alloc(void *context);
+static void dnode_free(dnode_t *node, void *context);
+
+/*
+ * Perform a ``left rotation'' adjustment on the tree. The given node P and
+ * its right child C are rearranged so that the P instead becomes the left
+ * child of C. The left subtree of C is inherited as the new right subtree
+ * for P. The ordering of the keys within the tree is thus preserved.
+ */
+
+static void rotate_left(dnode_t *upper)
+{
+ dnode_t *lower, *lowleft, *upparent;
+
+ lower = upper->right;
+ upper->right = lowleft = lower->left;
+ lowleft->parent = upper;
+
+ lower->parent = upparent = upper->parent;
+
+ /* don't need to check for root node here because root->parent is
+ the sentinel nil node, and root->parent->left points back to root */
+
+ if (upper == upparent->left) {
+ upparent->left = lower;
+ } else {
+ assert (upper == upparent->right);
+ upparent->right = lower;
+ }
+
+ lower->left = upper;
+ upper->parent = lower;
+}
+
+/*
+ * This operation is the ``mirror'' image of rotate_left. It is
+ * the same procedure, but with left and right interchanged.
+ */
+
+static void rotate_right(dnode_t *upper)
+{
+ dnode_t *lower, *lowright, *upparent;
+
+ lower = upper->left;
+ upper->left = lowright = lower->right;
+ lowright->parent = upper;
+
+ lower->parent = upparent = upper->parent;
+
+ if (upper == upparent->right) {
+ upparent->right = lower;
+ } else {
+ assert (upper == upparent->left);
+ upparent->left = lower;
+ }
+
+ lower->right = upper;
+ upper->parent = lower;
+}
+
+/*
+ * Do a postorder traversal of the tree rooted at the specified
+ * node and free everything under it. Used by dict_free().
+ */
+
+static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil)
+{
+ if (node == nil)
+ return;
+ free_nodes(dict, node->left, nil);
+ free_nodes(dict, node->right, nil);
+ dict->freenode(node, dict->context);
+}
+
+/*
+ * This procedure performs a verification that the given subtree is a binary
+ * search tree. It performs an inorder traversal of the tree using the
+ * dict_next() successor function, verifying that the key of each node is
+ * strictly lower than that of its successor, if duplicates are not allowed,
+ * or lower or equal if duplicates are allowed. This function is used for
+ * debugging purposes.
+ */
+
+static int verify_bintree(dict_t *dict)
+{
+ dnode_t *first, *next;
+
+ first = dict_first(dict);
+
+ if (dict->dupes) {
+ while (first && (next = dict_next(dict, first))) {
+ if (COMPARE(dict, first->key, next->key) > 0)
+ return 0;
+ first = next;
+ }
+ } else {
+ while (first && (next = dict_next(dict, first))) {
+ if (COMPARE(dict, first->key, next->key) >= 0)
+ return 0;
+ first = next;
+ }
+ }
+ return 1;
+}
+
+
+/*
+ * This function recursively verifies that the given binary subtree satisfies
+ * three of the red black properties. It checks that every red node has only
+ * black children. It makes sure that each node is either red or black. And it
+ * checks that every path has the same count of black nodes from root to leaf.
+ * It returns the blackheight of the given subtree; this allows blackheights to
+ * be computed recursively and compared for left and right siblings for
+ * mismatches. It does not check for every nil node being black, because there
+ * is only one sentinel nil node. The return value of this function is the
+ * black height of the subtree rooted at the node ``root'', or zero if the
+ * subtree is not red-black.
+ */
+
+static unsigned int verify_redblack(dnode_t *nil, dnode_t *root)
+{
+ unsigned height_left, height_right;
+
+ if (root != nil) {
+ height_left = verify_redblack(nil, root->left);
+ height_right = verify_redblack(nil, root->right);
+ if (height_left == 0 || height_right == 0)
+ return 0;
+ if (height_left != height_right)
+ return 0;
+ if (root->color == dnode_red) {
+ if (root->left->color != dnode_black)
+ return 0;
+ if (root->right->color != dnode_black)
+ return 0;
+ return height_left;
+ }
+ if (root->color != dnode_black)
+ return 0;
+ return height_left + 1;
+ }
+ return 1;
+}
+
+/*
+ * Compute the actual count of nodes by traversing the tree and
+ * return it. This could be compared against the stored count to
+ * detect a mismatch.
+ */
+
+static dictcount_t verify_node_count(dnode_t *nil, dnode_t *root)
+{
+ if (root == nil)
+ return 0;
+ else
+ return 1 + verify_node_count(nil, root->left)
+ + verify_node_count(nil, root->right);
+}
+
+/*
+ * Verify that the tree contains the given node. This is done by
+ * traversing all of the nodes and comparing their pointers to the
+ * given pointer. Returns 1 if the node is found, otherwise
+ * returns zero. It is intended for debugging purposes.
+ */
+
+static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node)
+{
+ if (root != nil) {
+ return root == node
+ || verify_dict_has_node(nil, root->left, node)
+ || verify_dict_has_node(nil, root->right, node);
+ }
+ return 0;
+}
+
+
+/*
+ * Dynamically allocate and initialize a dictionary object.
+ */
+
+dict_t *dict_create(dict_comp_t comp)
+{
+ dict_t* new = ALLOC(dict_t);
+
+ if (new) {
+ new->compare = comp;
+ new->allocnode = dnode_alloc;
+ new->freenode = dnode_free;
+ new->context = NULL;
+ new->nodecount = 0;
+ new->nilnode.left = &new->nilnode;
+ new->nilnode.right = &new->nilnode;
+ new->nilnode.parent = &new->nilnode;
+ new->nilnode.color = dnode_black;
+ new->dupes = 0;
+ }
+ return new;
+}
+
+/*
+ * Select a different set of node allocator routines.
+ */
+
+void dict_set_allocator(dict_t *dict, dnode_alloc_t al,
+ dnode_free_t fr, void *context)
+{
+ assert (dict_count(dict) == 0);
+ assert ((al == NULL && fr == NULL) || (al != NULL && fr != NULL));
+
+ dict->allocnode = al ? al : dnode_alloc;
+ dict->freenode = fr ? fr : dnode_free;
+ dict->context = context;
+}
+
+/*
+ * Free a dynamically allocated dictionary object. Removing the nodes
+ * from the tree before deleting it is required.
+ */
+
+void dict_destroy(dict_t *dict)
+{
+ assert (dict_isempty(dict));
+ xfree(dict);
+}
+
+/*
+ * Free all the nodes in the dictionary by using the dictionary's
+ * installed free routine. The dictionary is emptied.
+ */
+
+void dict_free_nodes(dict_t *dict)
+{
+ dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
+ free_nodes(dict, root, nil);
+ dict->nodecount = 0;
+ dict->nilnode.left = &dict->nilnode;
+ dict->nilnode.right = &dict->nilnode;
+ dict->nilnode.parent = &dict->nilnode;
+}
+
+/*
+ * Obsolescent function, equivalent to dict_free_nodes
+ */
+
+void dict_free(dict_t *dict)
+{
+#ifdef KAZLIB_OBSOLESCENT_DEBUG
+ assert ("call to obsolescent function dict_free()" && 0);
+#endif
+ dict_free_nodes(dict);
+}
+
+/*
+ * Initialize a user-supplied dictionary object.
+ */
+
+dict_t *dict_init(dict_t *dict, dict_comp_t comp)
+{
+ dict->compare = comp;
+ dict->allocnode = dnode_alloc;
+ dict->freenode = dnode_free;
+ dict->context = NULL;
+ dict->nodecount = 0;
+ dict->nilnode.left = &dict->nilnode;
+ dict->nilnode.right = &dict->nilnode;
+ dict->nilnode.parent = &dict->nilnode;
+ dict->nilnode.color = dnode_black;
+ dict->dupes = 0;
+ return dict;
+}
+
+/*
+ * Initialize a dictionary in the likeness of another dictionary
+ */
+
+void dict_init_like(dict_t *dict, const dict_t *template)
+{
+ dict->compare = template->compare;
+ dict->allocnode = template->allocnode;
+ dict->freenode = template->freenode;
+ dict->context = template->context;
+ dict->nodecount = 0;
+ dict->nilnode.left = &dict->nilnode;
+ dict->nilnode.right = &dict->nilnode;
+ dict->nilnode.parent = &dict->nilnode;
+ dict->nilnode.color = dnode_black;
+ dict->dupes = template->dupes;
+
+ assert (dict_similar(dict, template));
+}
+
+/*
+ * Remove all nodes from the dictionary (without freeing them in any way).
+ */
+
+static void dict_clear(dict_t *dict)
+{
+ dict->nodecount = 0;
+ dict->nilnode.left = &dict->nilnode;
+ dict->nilnode.right = &dict->nilnode;
+ dict->nilnode.parent = &dict->nilnode;
+ assert (dict->nilnode.color == dnode_black);
+}
+
+/*
+ * Verify the integrity of the dictionary structure. This is provided for
+ * debugging purposes, and should be placed in assert statements. Just because
+ * this function succeeds doesn't mean that the tree is not corrupt. Certain
+ * corruptions in the tree may simply cause undefined behavior.
+ */
+
+int dict_verify(dict_t *dict)
+{
+ dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
+
+ /* check that the sentinel node and root node are black */
+ if (root->color != dnode_black)
+ return 0;
+ if (nil->color != dnode_black)
+ return 0;
+ if (nil->right != nil)
+ return 0;
+ /* nil->left is the root node; check that its parent pointer is nil */
+ if (nil->left->parent != nil)
+ return 0;
+ /* perform a weak test that the tree is a binary search tree */
+ if (!verify_bintree(dict))
+ return 0;
+ /* verify that the tree is a red-black tree */
+ if (!verify_redblack(nil, root))
+ return 0;
+ if (verify_node_count(nil, root) != dict_count(dict))
+ return 0;
+ return 1;
+}
+
+/*
+ * Determine whether two dictionaries are similar: have the same comparison and
+ * allocator functions, and same status as to whether duplicates are allowed.
+ */
+
+int dict_similar(const dict_t *left, const dict_t *right)
+{
+ if (left->compare != right->compare)
+ return 0;
+
+ if (left->allocnode != right->allocnode)
+ return 0;
+
+ if (left->freenode != right->freenode)
+ return 0;
+
+ if (left->context != right->context)
+ return 0;
+
+/* if (left->dupes != right->dupes) */
+/* return 0; */
+
+ return 1;
+}
+
+/*
+ * Locate a node in the dictionary having the given key.
+ * If the node is not found, a null a pointer is returned (rather than
+ * a pointer that dictionary's nil sentinel node), otherwise a pointer to the
+ * located node is returned.
+ */
+
+dnode_t *dict_lookup(dict_t *dict, const void *key)
+{
+ dnode_t *root = dict_root(dict);
+ dnode_t *nil = dict_nil(dict);
+ dnode_t *saved;
+ int result;
+
+ /* simple binary search adapted for trees that contain duplicate keys */
+
+ while (root != nil) {
+ result = COMPARE(dict, key, root->key);
+ if (result < 0)
+ root = root->left;
+ else if (result > 0)
+ root = root->right;
+ else {
+ if (!dict->dupes) { /* no duplicates, return match */
+ return root;
+ } else { /* could be dupes, find leftmost one */
+ do {
+ saved = root;
+ root = root->left;
+ while (root != nil && COMPARE(dict, key, root->key))
+ root = root->right;
+ } while (root != nil);
+ return saved;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Look for the node corresponding to the lowest key that is equal to or
+ * greater than the given key. If there is no such node, return null.
+ */
+
+dnode_t *dict_lower_bound(dict_t *dict, const void *key)
+{
+ dnode_t *root = dict_root(dict);
+ dnode_t *nil = dict_nil(dict);
+ dnode_t *tentative = 0;
+
+ while (root != nil) {
+ int result = COMPARE(dict, key, root->key);
+
+ if (result > 0) {
+ root = root->right;
+ } else if (result < 0) {
+ tentative = root;
+ root = root->left;
+ } else {
+ if (!dict->dupes) {
+ return root;
+ } else {
+ tentative = root;
+ root = root->left;
+ }
+ }
+ }
+
+ return tentative;
+}
+
+/*
+ * Look for the node corresponding to the greatest key that is equal to or
+ * lower than the given key. If there is no such node, return null.
+ */
+
+dnode_t *dict_upper_bound(dict_t *dict, const void *key)
+{
+ dnode_t *root = dict_root(dict);
+ dnode_t *nil = dict_nil(dict);
+ dnode_t *tentative = 0;
+
+ while (root != nil) {
+ int result = COMPARE(dict, key, root->key);
+
+ if (result < 0) {
+ root = root->left;
+ } else if (result > 0) {
+ tentative = root;
+ root = root->right;
+ } else {
+ if (!dict->dupes) {
+ return root;
+ } else {
+ tentative = root;
+ root = root->right;
+ }
+ }
+ }
+
+ return tentative;
+}
+
+/*
+ * Insert a node into the dictionary. The node should have been
+ * initialized with a data field. All other fields are ignored.
+ * The behavior is undefined if the user attempts to insert into
+ * a dictionary that is already full (for which the dict_isfull()
+ * function returns true).
+ */
+
+int dict_insert(dict_t *dict, dnode_t *node, const void *key)
+{
+ dnode_t *where = dict_root(dict), *nil = dict_nil(dict);
+ dnode_t *parent = nil, *uncle, *grandpa;
+ int result = -1;
+
+ node->key = key;
+
+ assert (!dict_isfull(dict));
+ assert (!dict_contains(dict, node));
+ assert (!dnode_is_in_a_dict(node));
+
+ /* basic binary tree insert */
+
+ while (where != nil) {
+ parent = where;
+ result = COMPARE(dict, key, where->key);
+ /* trap attempts at duplicate key insertion unless it's explicitly allowed */
+
+ if (!dict->dupes && result == 0) {
+ where->data = node->data;
+ return 0;
+ } else if (result < 0) {
+ where = where->left;
+ } else {
+ where = where->right;
+ }
+ }
+
+ assert (where == nil);
+
+ if (result < 0)
+ parent->left = node;
+ else
+ parent->right = node;
+
+ node->parent = parent;
+ node->left = nil;
+ node->right = nil;
+
+ dict->nodecount++;
+
+ /* red black adjustments */
+
+ node->color = dnode_red;
+
+ while (parent->color == dnode_red) {
+ grandpa = parent->parent;
+ if (parent == grandpa->left) {
+ uncle = grandpa->right;
+ if (uncle->color == dnode_red) { /* red parent, red uncle */
+ parent->color = dnode_black;
+ uncle->color = dnode_black;
+ grandpa->color = dnode_red;
+ node = grandpa;
+ parent = grandpa->parent;
+ } else { /* red parent, black uncle */
+ if (node == parent->right) {
+ rotate_left(parent);
+ parent = node;
+ assert (grandpa == parent->parent);
+ /* rotation between parent and child preserves grandpa */
+ }
+ parent->color = dnode_black;
+ grandpa->color = dnode_red;
+ rotate_right(grandpa);
+ break;
+ }
+ } else { /* symmetric cases: parent == parent->parent->right */
+ uncle = grandpa->left;
+ if (uncle->color == dnode_red) {
+ parent->color = dnode_black;
+ uncle->color = dnode_black;
+ grandpa->color = dnode_red;
+ node = grandpa;
+ parent = grandpa->parent;
+ } else {
+ if (node == parent->left) {
+ rotate_right(parent);
+ parent = node;
+ assert (grandpa == parent->parent);
+ }
+ parent->color = dnode_black;
+ grandpa->color = dnode_red;
+ rotate_left(grandpa);
+ break;
+ }
+ }
+ }
+
+ dict_root(dict)->color = dnode_black;
+
+ assert (dict_verify(dict));
+ return 1;
+}
+
+/*
+ * Delete the given node from the dictionary. If the given node does not belong
+ * to the given dictionary, undefined behavior results. A pointer to the
+ * deleted node is returned.
+ */
+
+dnode_t *dict_delete(dict_t *dict, dnode_t *delete)
+{
+ dnode_t *nil = dict_nil(dict), *child, *delparent = delete->parent;
+
+ /* basic deletion */
+
+ assert (!dict_isempty(dict));
+ assert (dict_contains(dict, delete));
+
+ /*
+ * If the node being deleted has two children, then we replace it with its
+ * successor (i.e. the leftmost node in the right subtree.) By doing this,
+ * we avoid the traditional algorithm under which the successor's key and
+ * value *only* move to the deleted node and the successor is spliced out
+ * from the tree. We cannot use this approach because the user may hold
+ * pointers to the successor, or nodes may be inextricably tied to some
+ * other structures by way of embedding, etc. So we must splice out the
+ * node we are given, not some other node, and must not move contents from
+ * one node to another behind the user's back.
+ */
+
+ if (delete->left != nil && delete->right != nil) {
+ dnode_t *next = dict_next(dict, delete);
+ dnode_t *nextparent = next->parent;
+ dnode_color_t nextcolor = next->color;
+
+ assert (next != nil);
+ assert (next->parent != nil);
+ assert (next->left == nil);
+
+ /*
+ * First, splice out the successor from the tree completely, by
+ * moving up its right child into its place.
+ */
+
+ child = next->right;
+ child->parent = nextparent;
+
+ if (nextparent->left == next) {
+ nextparent->left = child;
+ } else {
+ assert (nextparent->right == next);
+ nextparent->right = child;
+ }
+
+ /*
+ * Now that the successor has been extricated from the tree, install it
+ * in place of the node that we want deleted.
+ */
+
+ next->parent = delparent;
+ next->left = delete->left;
+ next->right = delete->right;
+ next->left->parent = next;
+ next->right->parent = next;
+ next->color = delete->color;
+ delete->color = nextcolor;
+
+ if (delparent->left == delete) {
+ delparent->left = next;
+ } else {
+ assert (delparent->right == delete);
+ delparent->right = next;
+ }
+
+ } else {
+ assert (delete != nil);
+ assert (delete->left == nil || delete->right == nil);
+
+ child = (delete->left != nil) ? delete->left : delete->right;
+
+ child->parent = delparent = delete->parent;
+
+ if (delete == delparent->left) {
+ delparent->left = child;
+ } else {
+ assert (delete == delparent->right);
+ delparent->right = child;
+ }
+ }
+
+ delete->parent = NULL;
+ delete->right = NULL;
+ delete->left = NULL;
+
+ dict->nodecount--;
+
+ assert (verify_bintree(dict));
+
+ /* red-black adjustments */
+
+ if (delete->color == dnode_black) {
+ dnode_t *parent, *sister;
+
+ dict_root(dict)->color = dnode_red;
+
+ while (child->color == dnode_black) {
+ parent = child->parent;
+ if (child == parent->left) {
+ sister = parent->right;
+ assert (sister != nil);
+ if (sister->color == dnode_red) {
+ sister->color = dnode_black;
+ parent->color = dnode_red;
+ rotate_left(parent);
+ sister = parent->right;
+ assert (sister != nil);
+ }
+ if (sister->left->color == dnode_black
+ && sister->right->color == dnode_black) {
+ sister->color = dnode_red;
+ child = parent;
+ } else {
+ if (sister->right->color == dnode_black) {
+ assert (sister->left->color == dnode_red);
+ sister->left->color = dnode_black;
+ sister->color = dnode_red;
+ rotate_right(sister);
+ sister = parent->right;
+ assert (sister != nil);
+ }
+ sister->color = parent->color;
+ sister->right->color = dnode_black;
+ parent->color = dnode_black;
+ rotate_left(parent);
+ break;
+ }
+ } else { /* symmetric case: child == child->parent->right */
+ assert (child == parent->right);
+ sister = parent->left;
+ assert (sister != nil);
+ if (sister->color == dnode_red) {
+ sister->color = dnode_black;
+ parent->color = dnode_red;
+ rotate_right(parent);
+ sister = parent->left;
+ assert (sister != nil);
+ }
+ if (sister->right->color == dnode_black
+ && sister->left->color == dnode_black) {
+ sister->color = dnode_red;
+ child = parent;
+ } else {
+ if (sister->left->color == dnode_black) {
+ assert (sister->right->color == dnode_red);
+ sister->right->color = dnode_black;
+ sister->color = dnode_red;
+ rotate_left(sister);
+ sister = parent->left;
+ assert (sister != nil);
+ }
+ sister->color = parent->color;
+ sister->left->color = dnode_black;
+ parent->color = dnode_black;
+ rotate_right(parent);
+ break;
+ }
+ }
+ }
+
+ child->color = dnode_black;
+ dict_root(dict)->color = dnode_black;
+ }
+
+ assert (dict_verify(dict));
+
+ return delete;
+}
+
+/*
+ * Allocate a node using the dictionary's allocator routine, give it
+ * the data item.
+ */
+
+int dict_alloc_insert(dict_t *dict, const void *key, void *data)
+{
+ dnode_t *node = dict->allocnode(dict->context);
+
+ if (node) {
+ dnode_init(node, data);
+ if (!dict_insert(dict, node, key))
+ dict->freenode(node, dict->context);
+ return 1;
+ }
+ return 0;
+}
+
+void dict_delete_free(dict_t *dict, dnode_t *node)
+{
+ dict_delete(dict, node);
+ dict->freenode(node, dict->context);
+}
+
+/*
+ * Return the node with the lowest (leftmost) key. If the dictionary is empty
+ * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
+ */
+
+dnode_t *dict_first(dict_t *dict)
+{
+ dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left;
+
+ if (root != nil)
+ while ((left = root->left) != nil)
+ root = left;
+
+ return (root == nil) ? NULL : root;
+}
+
+/*
+ * Return the node with the highest (rightmost) key. If the dictionary is empty
+ * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
+ */
+
+dnode_t *dict_last(dict_t *dict)
+{
+ dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *right;
+
+ if (root != nil)
+ while ((right = root->right) != nil)
+ root = right;
+
+ return (root == nil) ? NULL : root;
+}
+
+/*
+ * Return the given node's successor node---the node which has the
+ * next key in the the left to right ordering. If the node has
+ * no successor, a null pointer is returned rather than a pointer to
+ * the nil node.
+ */
+
+dnode_t *dict_next(dict_t *dict, dnode_t *curr)
+{
+ dnode_t *nil = dict_nil(dict), *parent, *left;
+
+ if (curr->right != nil) {
+ curr = curr->right;
+ while ((left = curr->left) != nil)
+ curr = left;
+ return curr;
+ }
+
+ parent = curr->parent;
+
+ while (parent != nil && curr == parent->right) {
+ curr = parent;
+ parent = curr->parent;
+ }
+
+ return (parent == nil) ? NULL : parent;
+}
+
+/*
+ * Return the given node's predecessor, in the key order.
+ * The nil sentinel node is returned if there is no predecessor.
+ */
+
+dnode_t *dict_prev(dict_t *dict, dnode_t *curr)
+{
+ dnode_t *nil = dict_nil(dict), *parent, *right;
+
+ if (curr->left != nil) {
+ curr = curr->left;
+ while ((right = curr->right) != nil)
+ curr = right;
+ return curr;
+ }
+
+ parent = curr->parent;
+
+ while (parent != nil && curr == parent->left) {
+ curr = parent;
+ parent = curr->parent;
+ }
+
+ return (parent == nil) ? NULL : parent;
+}
+
+void dict_allow_dupes(dict_t *dict)
+{
+ dict->dupes = 1;
+}
+
+dictcount_t dict_count(dict_t *dict)
+{
+ return dict->nodecount;
+}
+
+int dict_isempty(dict_t *dict)
+{
+ return dict->nodecount == 0;
+}
+
+int dict_isfull(dict_t *dict)
+{
+ return dict->nodecount == DICTCOUNT_T_MAX;
+}
+
+int dict_contains(dict_t *dict, dnode_t *node)
+{
+ return verify_dict_has_node(dict_nil(dict), dict_root(dict), node);
+}
+
+static dnode_t *dnode_alloc(void *context)
+{
+ return malloc(sizeof *dnode_alloc(NULL));
+}
+
+static void dnode_free(dnode_t *node, void *context)
+{
+ free(node);
+}
+
+dnode_t *dnode_create(void *data)
+{
+ dnode_t *new = malloc(sizeof *new);
+ if (new) {
+ new->data = data;
+ new->parent = NULL;
+ new->left = NULL;
+ new->right = NULL;
+ }
+ return new;
+}
+
+dnode_t *dnode_init(dnode_t *dnode, void *data)
+{
+ dnode->data = data;
+ dnode->parent = NULL;
+ dnode->left = NULL;
+ dnode->right = NULL;
+ return dnode;
+}
+
+void dnode_destroy(dnode_t *dnode)
+{
+ assert (!dnode_is_in_a_dict(dnode));
+ free(dnode);
+}
+
+void *dnode_get(dnode_t *dnode)
+{
+ return dnode->data;
+}
+
+const void *dnode_getkey(dnode_t *dnode)
+{
+ return dnode->key;
+}
+
+void dnode_put(dnode_t *dnode, void *data)
+{
+ dnode->data = data;
+}
+
+int dnode_is_in_a_dict(dnode_t *dnode)
+{
+ return (dnode->parent && dnode->left && dnode->right);
+}
+
+void dict_process(dict_t *dict, void *context, dnode_process_t function)
+{
+ dnode_t *node = dict_first(dict), *next;
+
+ while (node != NULL) {
+ /* check for callback function deleting */
+ /* the next node from under us */
+ assert (dict_contains(dict, node));
+ next = dict_next(dict, node);
+ function(dict, node, context);
+ node = next;
+ }
+}
+
+static void load_begin_internal(dict_load_t *load, dict_t *dict)
+{
+ load->dictptr = dict;
+ load->nilnode.left = &load->nilnode;
+ load->nilnode.right = &load->nilnode;
+}
+
+void dict_load_begin(dict_load_t *load, dict_t *dict)
+{
+ assert (dict_isempty(dict));
+ load_begin_internal(load, dict);
+}
+
+void dict_load_next(dict_load_t *load, dnode_t *newnode, const void *key)
+{
+ dict_t *dict = load->dictptr;
+ dnode_t *nil = &load->nilnode;
+
+ assert (!dnode_is_in_a_dict(newnode));
+ assert (dict->nodecount < DICTCOUNT_T_MAX);
+
+ #ifndef NDEBUG
+ if (dict->nodecount > 0) {
+ if (dict->dupes)
+ assert (COMPARE(dict, nil->left->key, key) <= 0);
+ else
+ assert (COMPARE(dict, nil->left->key, key) < 0);
+ }
+ #endif
+
+ newnode->key = key;
+ nil->right->left = newnode;
+ nil->right = newnode;
+ newnode->left = nil;
+ dict->nodecount++;
+}
+
+void dict_load_end(dict_load_t *load)
+{
+ dict_t *dict = load->dictptr;
+ dnode_t *tree[DICT_DEPTH_MAX] = { 0 };
+ dnode_t *curr, *dictnil = dict_nil(dict), *loadnil = &load->nilnode, *next;
+ dnode_t *complete = 0;
+ dictcount_t fullcount = DICTCOUNT_T_MAX, nodecount = dict->nodecount;
+ dictcount_t botrowcount;
+ unsigned baselevel = 0, level = 0, i;
+
+ assert (dnode_red == 0 && dnode_black == 1);
+
+ while (fullcount >= nodecount && fullcount)
+ fullcount >>= 1;
+
+ botrowcount = nodecount - fullcount;
+
+ for (curr = loadnil->left; curr != loadnil; curr = next) {
+ next = curr->left;
+
+ if (complete == NULL && botrowcount-- == 0) {
+ assert (baselevel == 0);
+ assert (level == 0);
+ baselevel = level = 1;
+ complete = tree[0];
+
+ if (complete != 0) {
+ tree[0] = 0;
+ complete->right = dictnil;
+ while (tree[level] != 0) {
+ tree[level]->right = complete;
+ complete->parent = tree[level];
+ complete = tree[level];
+ tree[level++] = 0;
+ }
+ }
+ }
+
+ if (complete == NULL) {
+ curr->left = dictnil;
+ curr->right = dictnil;
+ curr->color = level % 2;
+ complete = curr;
+
+ assert (level == baselevel);
+ while (tree[level] != 0) {
+ tree[level]->right = complete;
+ complete->parent = tree[level];
+ complete = tree[level];
+ tree[level++] = 0;
+ }
+ } else {
+ curr->left = complete;
+ curr->color = (level + 1) % 2;
+ complete->parent = curr;
+ tree[level] = curr;
+ complete = 0;
+ level = baselevel;
+ }
+ }
+
+ if (complete == NULL)
+ complete = dictnil;
+
+ for (i = 0; i < DICT_DEPTH_MAX; i++) {
+ if (tree[i] != 0) {
+ tree[i]->right = complete;
+ complete->parent = tree[i];
+ complete = tree[i];
+ }
+ }
+
+ dictnil->color = dnode_black;
+ dictnil->right = dictnil;
+ complete->parent = dictnil;
+ complete->color = dnode_black;
+ dict_root(dict) = complete;
+
+ assert (dict_verify(dict));
+}
+
+void dict_merge(dict_t *dest, dict_t *source)
+{
+ dict_load_t load;
+ dnode_t *leftnode = dict_first(dest), *rightnode = dict_first(source);
+
+ assert (dict_similar(dest, source));
+
+ if (source == dest)
+ return;
+
+ dest->nodecount = 0;
+ load_begin_internal(&load, dest);
+
+ for (;;) {
+ if (leftnode != NULL && rightnode != NULL) {
+ if (COMPARE(dest, leftnode->key, rightnode->key) < 0)
+ goto copyleft;
+ else
+ goto copyright;
+ } else if (leftnode != NULL) {
+ goto copyleft;
+ } else if (rightnode != NULL) {
+ goto copyright;
+ } else {
+ assert (leftnode == NULL && rightnode == NULL);
+ break;
+ }
+
+ copyleft:
+ {
+ dnode_t *next = dict_next(dest, leftnode);
+ #ifndef NDEBUG
+ leftnode->left = NULL; /* suppress assertion in dict_load_next */
+ #endif
+ dict_load_next(&load, leftnode, leftnode->key);
+ leftnode = next;
+ continue;
+ }
+
+ copyright:
+ {
+ dnode_t *next = dict_next(source, rightnode);
+ #ifndef NDEBUG
+ rightnode->left = NULL;
+ #endif
+ dict_load_next(&load, rightnode, rightnode->key);
+ rightnode = next;
+ continue;
+ }
+ }
+
+ dict_clear(source);
+ dict_load_end(&load);
+}
+
+int dict_equal(dict_t* dict1, dict_t* dict2,
+ dict_value_eql_t value_eql)
+{
+ dnode_t* node1;
+ dnode_t* node2;
+
+ if (dict_count(dict1) != dict_count(dict2))
+ return 0;
+ if (!dict_similar(dict1, dict2))
+ return 0;
+
+ for (node1 = dict_first(dict1), node2 = dict_first(dict2);
+ node1 != NULL && node2 != NULL;
+ node1 = dict_next(dict1, node1), node2 = dict_next(dict2, node2)) {
+
+ if (COMPARE(dict1, node1->key, node2->key) != 0)
+ return 0;
+ if (!value_eql(node1->data, node2->data))
+ return 0;
+ }
+ return 1;
+}
Added: trunk/rbtree-0.2.0/dict.h
===================================================================
--- trunk/rbtree-0.2.0/dict.h (rev 0)
+++ trunk/rbtree-0.2.0/dict.h 2007-03-09 10:28:46 UTC (rev 236)
@@ -0,0 +1,123 @@
+/*
+ * Dictionary Abstract Data Type
+ * Copyright (C) 1997 Kaz Kylheku
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * $Id: dict.h,v 1.9 2005/10/06 05:16:35 kuma Exp $
+ * $Name: $
+ */
+
+/*
+ * Modified for Ruby/RBTree by OZAWA Takuma.
+ */
+
+#ifndef DICT_H
+#define DICT_H
+
+#include
+
+/*
+ * Blurb for inclusion into C++ translation units
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned long dictcount_t;
+#define DICTCOUNT_T_MAX ULONG_MAX
+
+/*
+ * The dictionary is implemented as a red-black tree
+ */
+
+typedef enum { dnode_red, dnode_black } dnode_color_t;
+
+typedef struct dnode_t {
+ struct dnode_t *dict_left;
+ struct dnode_t *dict_right;
+ struct dnode_t *dict_parent;
+ dnode_color_t dict_color;
+ const void *dict_key;
+ void *dict_data;
+} dnode_t;
+
+typedef int (*dict_comp_t)(const void *, const void *, void *);
+typedef dnode_t *(*dnode_alloc_t)(void *);
+typedef void (*dnode_free_t)(dnode_t *, void *);
+
+typedef int (*dict_value_eql_t)(const void *, const void *);
+
+typedef struct dict_t {
+ dnode_t dict_nilnode;
+ dictcount_t dict_nodecount;
+ dict_comp_t dict_compare;
+ dnode_alloc_t dict_allocnode;
+ dnode_free_t dict_freenode;
+ void *dict_context;
+ int dict_dupes;
+} dict_t;
+
+typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
+
+typedef struct dict_load_t {
+ dict_t *dict_dictptr;
+ dnode_t dict_nilnode;
+} dict_load_t;
+
+extern dict_t *dict_create(dict_comp_t);
+extern void dict_set_allocator(dict_t *, dnode_alloc_t, dnode_free_t, void *);
+extern void dict_destroy(dict_t *);
+extern void dict_free_nodes(dict_t *);
+extern void dict_free(dict_t *);
+extern dict_t *dict_init(dict_t *, dict_comp_t);
+extern void dict_init_like(dict_t *, const dict_t *);
+extern int dict_verify(dict_t *);
+extern int dict_similar(const dict_t *, const dict_t *);
+extern dnode_t *dict_lookup(dict_t *, const void *);
+extern dnode_t *dict_lower_bound(dict_t *, const void *);
+extern dnode_t *dict_upper_bound(dict_t *, const void *);
+extern int dict_insert(dict_t *, dnode_t *, const void *);
+extern dnode_t *dict_delete(dict_t *, dnode_t *);
+extern int dict_alloc_insert(dict_t *, const void *, void *);
+extern void dict_delete_free(dict_t *, dnode_t *);
+extern dnode_t *dict_first(dict_t *);
+extern dnode_t *dict_last(dict_t *);
+extern dnode_t *dict_next(dict_t *, dnode_t *);
+extern dnode_t *dict_prev(dict_t *, dnode_t *);
+extern dictcount_t dict_count(dict_t *);
+extern int dict_isempty(dict_t *);
+extern int dict_isfull(dict_t *);
+extern int dict_contains(dict_t *, dnode_t *);
+extern void dict_allow_dupes(dict_t *);
+extern int dnode_is_in_a_dict(dnode_t *);
+extern dnode_t *dnode_create(void *);
+extern dnode_t *dnode_init(dnode_t *, void *);
+extern void dnode_destroy(dnode_t *);
+extern void *dnode_get(dnode_t *);
+extern const void *dnode_getkey(dnode_t *);
+extern void dnode_put(dnode_t *, void *);
+extern void dict_process(dict_t *, void *, dnode_process_t);
+extern void dict_load_begin(dict_load_t *, dict_t *);
+extern void dict_load_next(dict_load_t *, dnode_t *, const void *);
+extern void dict_load_end(dict_load_t *);
+extern void dict_merge(dict_t *, dict_t *);
+
+int dict_equal(dict_t*, dict_t*, dict_value_eql_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Added: trunk/rbtree-0.2.0/extconf.rb
===================================================================
--- trunk/rbtree-0.2.0/extconf.rb (rev 0)
+++ trunk/rbtree-0.2.0/extconf.rb 2007-03-09 10:28:46 UTC (rev 236)
@@ -0,0 +1,38 @@
+require 'mkmf'
+
+def print(*args)
+ STDOUT.print *args
+ STDOUT.flush
+end
+
+
+$CFLAGS << ' -std=c89 -pedantic -Wall -Wno-long-long'
+$defs << ' -DNDEBUG'
+
+
+print 'checking for allocation framework... '
+if Object.respond_to?(:allocate)
+ print "yes\n"
+ $defs << '-DHAVE_OBJECT_ALLOCATE'
+else
+ print "no\n"
+end
+
+have_func('rb_obj_init_copy')
+have_func('rb_block_proc')
+have_func('rb_yield_values')
+have_func('rb_marshal_dump')
+have_func('rb_marshal_load')
+
+print 'checking for inline keyword... '
+inline = ['inline', '__inline', '__inline__', ''].find {|e|
+ try_link(<
+#include
+#include
+#include
+#include "dict.h"
+
+#define RBTREE_IN_ITERATION FL_USER1
+#define RBTREE_PROC_DEFAULT FL_USER2
+#define HASH_PROC_DEFAULT FL_USER2
+
+#ifndef ULONG2NUM
+#define ULONG2NUM UINT2NUM
+#endif
+
+VALUE RBTree;
+VALUE MultiRBTree;
+
+static ID id_bound;
+static ID id_cmp;
+static ID id_call;
+static ID id_default;
+
+#ifndef HAVE_RB_MARSHAL_DUMP
+static VALUE rb_mMarshal;
+static ID id_dump;
+static ID id_load;
+#endif
+
+typedef struct {
+ dict_t* dict;
+ VALUE ifnone;
+ int iter_lev;
+} rbtree_t;
+
+#define RBTREE(rbtree) DATA_PTR(rbtree)
+#define DICT(rbtree) ((rbtree_t*)RBTREE(rbtree))->dict
+#define IFNONE(rbtree) ((rbtree_t*)RBTREE(rbtree))->ifnone
+#define ITER_LEV(rbtree) ((rbtree_t*)RBTREE(rbtree))->iter_lev
+#define COMPARE(rbtree) DICT(rbtree)->dict_compare
+#define CONTEXT(rbtree) DICT(rbtree)->dict_context
+
+#define TO_KEY(arg) ((const void*)arg)
+#define TO_VAL(arg) ((void*)arg)
+#define GET_KEY(dnode) ((VALUE)dnode_getkey(dnode))
+#define GET_VAL(dnode) ((VALUE)dnode_get(dnode))
+#define ASSOC(dnode) rb_assoc_new(GET_KEY(dnode), GET_VAL(dnode))
+
+/*********************************************************************/
+
+#ifndef HAVE_RB_BLOCK_PROC
+static VALUE
+rb_block_proc()
+{
+ return rb_f_lambda();
+}
+#endif
+
+#ifndef HAVE_RB_YIELD_VALUES
+static VALUE
+rb_yield_values(int n, ...)
+{
+ int i;
+ va_list ap;
+ VALUE ary = rb_ary_new2(n);
+
+ va_start(ap, n);
+ for (i = 0; i < n; i++)
+ rb_ary_push(ary, va_arg(ap, VALUE));
+ va_end(ap);
+ return rb_yield(ary);
+}
+#endif
+
+static int
+cmpint(VALUE i, VALUE a, VALUE b)
+{
+#if RUBY_VERSION_CODE >= 180
+ return rb_cmpint(i, a, b);
+#else
+ return rb_cmpint(i);
+#endif
+}
+
+
+static void
+rbtree_free(rbtree_t* rbtree)
+{
+ dict_free_nodes(rbtree->dict);
+ dict_destroy(rbtree->dict);
+ xfree(rbtree);
+}
+
+static void
+rbtree_mark(rbtree_t* rbtree)
+{
+ if (rbtree == NULL) return;
+
+ if (rbtree->dict != NULL) {
+ dict_t* dict = rbtree->dict;
+ dnode_t* node;
+ for (node = dict_first(dict);
+ node != NULL;
+ node = dict_next(dict, node)) {
+
+ rb_gc_mark(GET_KEY(node));
+ rb_gc_mark(GET_VAL(node));
+ }
+ rb_gc_mark((VALUE)dict->dict_context);
+ }
+ rb_gc_mark(rbtree->ifnone);
+}
+
+static dnode_t*
+rbtree_alloc_node(void* context)
+{
+ return ALLOC(dnode_t);
+}
+
+static void
+rbtree_free_node(dnode_t* node, void* context)
+{
+ xfree(node);
+}
+
+static void
+rbtree_argc_error()
+{
+ rb_raise(rb_eArgError, "wrong number of arguments");
+}
+
+static int
+rbtree_cmp(const void* key1, const void* key2, void* context)
+{
+ VALUE ret;
+ if (TYPE(key1) == T_STRING && TYPE(key2) == T_STRING)
+ return rb_str_cmp((VALUE)key1, (VALUE)key2);
+ ret = rb_funcall((VALUE)key1, id_cmp, 1, (VALUE)key2);
+ return cmpint(ret, (VALUE)key1, (VALUE)key2);
+}
+
+static int
+rbtree_user_cmp(const void* key1, const void* key2, void* cmp_proc)
+{
+ VALUE ret = rb_funcall((VALUE)cmp_proc, id_call, 2,
+ (VALUE)key1, (VALUE)key2);
+ return cmpint(ret, (VALUE)key1, (VALUE)key2);
+}
+
+static void
+rbtree_modify(VALUE self)
+{
+ if (ITER_LEV(self) > 0)
+ rb_raise(rb_eTypeError, "can't modify rbtree in iteration");
+ if (OBJ_FROZEN(self))
+ rb_error_frozen("rbtree");
+ if (!OBJ_TAINTED(self) && rb_safe_level() >= 4)
+ rb_raise(rb_eSecurityError, "Insecure: can't modify rbtree");
+}
+
+static VALUE
+rbtree_alloc(VALUE klass)
+{
+ dict_t* dict;
+ VALUE rbtree = Data_Wrap_Struct(klass, rbtree_mark, rbtree_free, 0);
+ RBTREE(rbtree) = ALLOC(rbtree_t);
+ MEMZERO(RBTREE(rbtree), rbtree_t, 1);
+
+ dict = dict_create(rbtree_cmp);
+ dict_set_allocator(dict, rbtree_alloc_node, rbtree_free_node,
+ (void*)Qnil);
+ if (klass == MultiRBTree)
+ dict_allow_dupes(dict);
+
+ DICT(rbtree) = dict;
+ IFNONE(rbtree) = Qnil;
+ return rbtree;
+}
+
+VALUE rbtree_aset(VALUE, VALUE, VALUE);
+VALUE rbtree_clear(VALUE);
+VALUE rbtree_has_key(VALUE, VALUE);
+VALUE rbtree_update(VALUE, VALUE);
+
+/*********************************************************************/
+
+#ifndef HAVE_OBJECT_ALLOCATE
+/*
+ *
+ */
+VALUE
+rbtree_s_new(int argc, VALUE* argv, VALUE klass)
+{
+ VALUE rbtree = rbtree_alloc(klass);
+ rb_obj_call_init(rbtree, argc, argv);
+ return rbtree;
+}
+#endif
+
+static int
+hash_to_rbtree_i(VALUE key, VALUE value, VALUE rbtree)
+{
+ if (key != Qundef)
+ rbtree_aset(rbtree, key, value);
+ return ST_CONTINUE;
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_s_create(int argc, VALUE* argv, VALUE klass)
+{
+ int i;
+ VALUE rbtree;
+
+ if (argc == 1) {
+ if (rb_obj_is_kind_of(argv[0], klass)) {
+ rbtree = rbtree_alloc(klass);
+ rbtree_update(rbtree, argv[0]);
+ return rbtree;
+ } else if (TYPE(argv[0]) == T_HASH) {
+ rbtree = rbtree_alloc(klass);
+ st_foreach(RHASH(argv[0])->tbl, hash_to_rbtree_i, rbtree);
+ return rbtree;
+ }
+ }
+
+ if (argc % 2 != 0)
+ rb_raise(rb_eArgError, "odd number of arguments for RBTree");
+
+ rbtree = rbtree_alloc(klass);
+ for (i = 0; i < argc; i += 2)
+ rbtree_aset(rbtree, argv[i], argv[i + 1]);
+ return rbtree;
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_initialize(int argc, VALUE* argv, VALUE self)
+{
+ rbtree_modify(self);
+
+ if (rb_block_given_p()) {
+ if (argc > 0)
+ rbtree_argc_error();
+ IFNONE(self) = rb_block_proc();
+ FL_SET(self, RBTREE_PROC_DEFAULT);
+ } else {
+ if (argc > 1)
+ rbtree_argc_error();
+ else if (argc == 1)
+ IFNONE(self) = argv[0];
+ }
+ return self;
+}
+
+/*********************************************************************/
+
+typedef enum {
+ INITIAL_VALUE, NODE_NOT_FOUND, NODE_FOUND
+} insert_node_ret_t;
+
+typedef struct {
+ dict_t* dict;
+ dnode_t* node;
+ const void* key;
+ insert_node_ret_t ret;
+} insert_node_t;
+
+static VALUE
+insert_node_body(insert_node_t* arg)
+{
+ if (dict_insert(arg->dict, arg->node, arg->key))
+ arg->ret = NODE_NOT_FOUND;
+ else
+ arg->ret = NODE_FOUND;
+ return Qnil;
+}
+
+static VALUE
+insert_node_ensure(insert_node_t* arg)
+{
+ dict_t* dict = arg->dict;
+ dnode_t* node = arg->node;
+ switch (arg->ret) {
+ case INITIAL_VALUE:
+ dict->dict_freenode(node, dict->dict_context);
+ break;
+ case NODE_NOT_FOUND:
+ if (TYPE(arg->key) == T_STRING)
+ node->dict_key = TO_KEY(rb_str_new4(GET_KEY(node)));
+ break;
+ case NODE_FOUND:
+ dict->dict_freenode(node, dict->dict_context);
+ break;
+ }
+ return Qnil;
+}
+
+static void
+rbtree_insert(VALUE self, VALUE key, VALUE value)
+{
+ insert_node_t arg;
+ dict_t* dict = DICT(self);
+ dnode_t* node = dict->dict_allocnode(dict->dict_context);
+
+ dnode_init(node, TO_VAL(value));
+
+ arg.dict = dict;
+ arg.node = node;
+ arg.key = TO_KEY(key);
+ arg.ret = INITIAL_VALUE;
+
+ rb_ensure(insert_node_body, (VALUE)&arg,
+ insert_node_ensure, (VALUE)&arg);
+}
+
+/*********************************************************************/
+
+/*
+ *
+ */
+VALUE
+rbtree_aset(VALUE self, VALUE key, VALUE value)
+{
+ rbtree_modify(self);
+
+ if (dict_isfull(DICT(self))) {
+ dnode_t* node = dict_lookup(DICT(self), TO_KEY(key));
+ if (node == NULL)
+ rb_raise(rb_eIndexError, "rbtree full");
+ else
+ dnode_put(node, TO_VAL(value));
+ return value;
+ }
+ rbtree_insert(self, key, value);
+ return value;
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_aref(VALUE self, VALUE key)
+{
+ dnode_t* node = dict_lookup(DICT(self), TO_KEY(key));
+ if (node == NULL)
+ return rb_funcall(self, id_default, 1, key);
+ else
+ return GET_VAL(node);
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_fetch(int argc, VALUE* argv, VALUE self)
+{
+ dnode_t* node;
+ int block_given;
+
+ if (argc == 0 || argc > 2)
+ rbtree_argc_error();
+ block_given = rb_block_given_p();
+ if (block_given && argc == 2)
+ rb_warn("block supersedes default value argument");
+
+ node = dict_lookup(DICT(self), TO_KEY(argv[0]));
+ if (node != NULL)
+ return GET_VAL(node);
+
+ if (block_given)
+ return rb_yield(argv[0]);
+ if (argc == 1)
+ rb_raise(rb_eIndexError, "key not found");
+ return argv[1];
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_size(VALUE self)
+{
+ return ULONG2NUM(dict_count(DICT(self)));
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_empty_p(VALUE self)
+{
+ return dict_isempty(DICT(self)) ? Qtrue : Qfalse;
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_default(int argc, VALUE* argv, VALUE self)
+{
+ VALUE key = Qnil;
+ if (argc == 1)
+ key = argv[0];
+ else if (argc > 1)
+ rbtree_argc_error();
+
+ if (FL_TEST(self, RBTREE_PROC_DEFAULT)) {
+ if (argc == 0) return Qnil;
+ return rb_funcall(IFNONE(self), id_call, 2, self, key);
+ }
+ return IFNONE(self);
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_set_default(VALUE self, VALUE ifnone)
+{
+ rbtree_modify(self);
+ IFNONE(self) = ifnone;
+ FL_UNSET(self, RBTREE_PROC_DEFAULT);
+ return ifnone;
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_default_proc(VALUE self)
+{
+ if (FL_TEST(self, RBTREE_PROC_DEFAULT))
+ return IFNONE(self);
+ return Qnil;
+}
+
+static int
+value_eq(const void* key1, const void* key2)
+{
+ return rb_equal((VALUE)key1, (VALUE)key2);
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_equal(VALUE self, VALUE other)
+{
+ int ret;
+ if (self == other)
+ return Qtrue;
+ if (!rb_obj_is_kind_of(other, MultiRBTree))
+ return Qfalse;
+ ret = dict_equal(DICT(self), DICT(other), value_eq);
+ return ret ? Qtrue : Qfalse;
+}
+
+/*********************************************************************/
+
+typedef enum {
+ EACH_NEXT, EACH_STOP
+} each_return_t;
+
+typedef each_return_t (*each_callback_func)(dnode_t*, void*);
+
+typedef struct {
+ VALUE self;
+ each_callback_func func;
+ void* arg;
+ int reverse;
+} rbtree_each_arg_t;
+
+static VALUE
+rbtree_each_ensure(VALUE self)
+{
+ ITER_LEV(self)--;
+ return Qnil;
+}
+
+static VALUE
+rbtree_each_body(rbtree_each_arg_t* arg)
+{
+ VALUE self = arg->self;
+ dict_t* dict = DICT(self);
+ dnode_t* node;
+ dnode_t* first_node;
+ dnode_t* (*next_func)(dict_t*, dnode_t*);
+
+ if (arg->reverse) {
+ first_node = dict_last(dict);
+ next_func = dict_prev;
+ } else {
+ first_node = dict_first(dict);
+ next_func = dict_next;
+ }
+
+ ITER_LEV(self)++;
+ for (node = first_node;
+ node != NULL;
+ node = next_func(dict, node)) {
+
+ if (arg->func(node, arg->arg) == EACH_STOP)
+ break;
+ }
+ return self;
+}
+
+static VALUE
+rbtree_for_each(VALUE self, each_callback_func func, void* arg)
+{
+ rbtree_each_arg_t each_arg;
+ each_arg.self = self;
+ each_arg.func = func;
+ each_arg.arg = arg;
+ each_arg.reverse = 0;
+ return rb_ensure(rbtree_each_body, (VALUE)&each_arg,
+ rbtree_each_ensure, self);
+}
+
+static VALUE
+rbtree_reverse_for_each(VALUE self, each_callback_func func, void* arg)
+{
+ rbtree_each_arg_t each_arg;
+ each_arg.self = self;
+ each_arg.func = func;
+ each_arg.arg = arg;
+ each_arg.reverse = 1;
+ return rb_ensure(rbtree_each_body, (VALUE)&each_arg,
+ rbtree_each_ensure, self);
+}
+
+/*********************************************************************/
+
+static each_return_t
+each_i(dnode_t* node, void* arg)
+{
+ rb_yield(ASSOC(node));
+ return EACH_NEXT;
+}
+
+/*
+ * call-seq:
+ * rbtree.each {|key, value| block} => rbtree
+ *
+ * Calls block once for each key in order, passing the key and value
+ * as a two-element array parameters.
+ */
+VALUE
+rbtree_each(VALUE self)
+{
+ return rbtree_for_each(self, each_i, NULL);
+}
+
+static each_return_t
+each_pair_i(dnode_t* node, void* arg)
+{
+ rb_yield_values(2, GET_KEY(node), GET_VAL(node));
+ return EACH_NEXT;
+}
+
+/*
+ * call-seq:
+ * rbtree.each_pair {|key, value| block} => rbtree
+ *
+ * Calls block once for each key in order, passing the key and value
+ * as parameters.
+ */
+VALUE
+rbtree_each_pair(VALUE self)
+{
+ return rbtree_for_each(self, each_pair_i, NULL);
+}
+
+static each_return_t
+each_key_i(dnode_t* node, void* arg)
+{
+ rb_yield(GET_KEY(node));
+ return EACH_NEXT;
+}
+
+/*
+ * call-seq:
+ * rbtree.each_key {|key| block} => rbtree
+ *
+ * Calls block once for each key in order, passing the key as
+ * parameters.
+ */
+VALUE
+rbtree_each_key(VALUE self)
+{
+ return rbtree_for_each(self, each_key_i, NULL);
+}
+
+static each_return_t
+each_value_i(dnode_t* node, void* arg)
+{
+ rb_yield(GET_VAL(node));
+ return EACH_NEXT;
+}
+
+/*
+ * call-seq:
+ * rbtree.each_value {|value| block} => rbtree
+ *
+ * Calls block once for each key in order, passing the value as
+ * parameters.
+ */
+VALUE
+rbtree_each_value(VALUE self)
+{
+ return rbtree_for_each(self, each_value_i, NULL);
+}
+
+/*
+ * call-seq:
+ * rbtree.reverse_each {|key, value| block} => rbtree
+ *
+ * Calls block once for each key in reverse order, passing the key and
+ * value as parameters.
+ */
+VALUE
+rbtree_reverse_each(VALUE self)
+{
+ return rbtree_reverse_for_each(self, each_pair_i, NULL);
+}
+
+static each_return_t
+aset_i(dnode_t* node, void* self)
+{
+ rbtree_aset((VALUE)self, GET_KEY(node), GET_VAL(node));
+ return EACH_NEXT;
+}
+
+static void
+copy_dict(VALUE src, VALUE dest, dict_comp_t cmp, void* context)
+{
+ VALUE temp = rbtree_alloc(CLASS_OF(dest));
+ COMPARE(temp) = cmp;
+ CONTEXT(temp) = context;
+ rbtree_for_each(src, aset_i, (void*)temp);
+ {
+ dict_t* t = DICT(temp);
+ DICT(temp) = DICT(dest);
+ DICT(dest) = t;
+ }
+ rbtree_free(RBTREE(temp));
+ rb_gc_force_recycle(temp);
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_initialize_copy(VALUE self, VALUE other)
+{
+ if (self == other)
+ return self;
+ if (!rb_obj_is_kind_of(other, CLASS_OF(self))) {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)",
+ rb_class2name(CLASS_OF(other)),
+ rb_class2name(CLASS_OF(self)));
+ }
+
+ copy_dict(other, self, COMPARE(other), CONTEXT(other));
+
+ IFNONE(self) = IFNONE(other);
+ if (FL_TEST(other, RBTREE_PROC_DEFAULT))
+ FL_SET(self, RBTREE_PROC_DEFAULT);
+ else
+ FL_UNSET(self, RBTREE_PROC_DEFAULT);
+ return self;
+}
+
+#ifndef HAVE_RB_OBJ_INIT_COPY
+/*
+ *
+ */
+VALUE
+rbtree_clone(VALUE self)
+{
+ VALUE clone = rbtree_alloc(CLASS_OF(self));
+ rbtree_initialize_copy(clone, self);
+ return clone;
+}
+#endif
+
+/*
+ *
+ */
+VALUE
+rbtree_values_at(int argc, VALUE* argv, VALUE self)
+{
+ int i;
+ VALUE ary = rb_ary_new();
+
+ for (i = 0; i < argc; i++)
+ rb_ary_push(ary, rbtree_aref(self, argv[i]));
+ return ary;
+}
+
+static each_return_t
+select_i(dnode_t* node, void* ary)
+{
+ if (RTEST(rb_yield_values(2, GET_KEY(node), GET_VAL(node))))
+ rb_ary_push((VALUE)ary, ASSOC(node));
+ return EACH_NEXT;
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_select(VALUE self)
+{
+ VALUE ary = rb_ary_new();
+ rbtree_for_each(self, select_i, (void*)ary);
+ return ary;
+}
+
+static each_return_t
+index_i(dnode_t* node, void* arg_)
+{
+ VALUE* arg = (VALUE*)arg_;
+ if (rb_equal(GET_VAL(node), arg[1])) {
+ arg[0] = GET_KEY(node);
+ return EACH_STOP;
+ }
+ return EACH_NEXT;
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_index(VALUE self, VALUE value)
+{
+ VALUE arg[2];
+ arg[0] = Qnil;
+ arg[1] = value;
+ rbtree_for_each(self, index_i, (void*)&arg);
+ return arg[0];
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_clear(VALUE self)
+{
+ rbtree_modify(self);
+ dict_free_nodes(DICT(self));
+ return self;
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_delete(VALUE self, VALUE key)
+{
+ dict_t* dict = DICT(self);
+ dnode_t* node;
+ VALUE value;
+
+ rbtree_modify(self);
+ node = dict_lookup(dict, TO_KEY(key));
+ if (node == NULL)
+ return rb_block_given_p() ? rb_yield(key) : Qnil;
+ value = GET_VAL(node);
+ dict_delete_free(dict, node);
+ return value;
+}
+
+/*********************************************************************/
+
+typedef struct dnode_list_t_ {
+ struct dnode_list_t_* prev;
+ dnode_t* node;
+} dnode_list_t;
+
+typedef struct {
+ VALUE self;
+ dnode_list_t* list;
+ int raised;
+} rbtree_delete_if_arg_t;
+
+static VALUE
+rbtree_delete_if_ensure(rbtree_delete_if_arg_t* arg)
+{
+ dict_t* dict = DICT(arg->self);
+ dnode_list_t* list = arg->list;
+
+ if (--ITER_LEV(arg->self) == 0) {
+ while (list != NULL) {
+ dnode_list_t* l = list;
+ if (!arg->raised)
+ dict_delete_free(dict, l->node);
+ list = l->prev;
+ xfree(l);
+ }
+ }
+ return Qnil;
+}
+
+static VALUE
+rbtree_delete_if_body(rbtree_delete_if_arg_t* arg)
+{
+ VALUE self = arg->self;
+ dict_t* dict = DICT(self);
+ dnode_t* node;
+
+ arg->raised = 1;
+ ITER_LEV(self)++;
+ for (node = dict_first(dict);
+ node != NULL;
+ node = dict_next(dict, node)) {
+
+ if (RTEST(rb_yield_values(2, GET_KEY(node), GET_VAL(node)))) {
+ dnode_list_t* l = ALLOC(dnode_list_t);
+ l->node = node;
+ l->prev = arg->list;
+ arg->list = l;
+ }
+ }
+ arg->raised = 0;
+ return self;
+}
+
+/*********************************************************************/
+
+/*
+ *
+ */
+VALUE
+rbtree_delete_if(VALUE self)
+{
+ rbtree_delete_if_arg_t arg;
+
+ rbtree_modify(self);
+ arg.self = self;
+ arg.list = NULL;
+ return rb_ensure(rbtree_delete_if_body, (VALUE)&arg,
+ rbtree_delete_if_ensure, (VALUE)&arg);
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_reject_bang(VALUE self)
+{
+ const dictcount_t count = dict_count(DICT(self));
+ rbtree_delete_if(self);
+ if (count == dict_count(DICT(self)))
+ return Qnil;
+ return self;
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_reject(VALUE self)
+{
+ return rbtree_reject_bang(rb_obj_dup(self));
+}
+
+static VALUE
+rbtree_shift_pop(VALUE self, const int mode)
+{
+ dict_t* dict = DICT(self);
+ dnode_t* node;
+ VALUE ret;
+
+ rbtree_modify(self);
+
+ if (dict_isempty(dict)) {
+ if (FL_TEST(self, RBTREE_PROC_DEFAULT)) {
+ return rb_funcall(IFNONE(self), id_call, 2, self, Qnil);
+ }
+ return IFNONE(self);
+ }
+
+ if (mode == 0)
+ node = dict_first(dict);
+ else
+ node = dict_last(dict);
+ ret = ASSOC(node);
+ dict_delete_free(dict, node);
+ return ret;
+}
+
+/*
+ * call-seq:
+ * rbtree.shift => array or object
+ *
+ * Removes the first(that is, the smallest) key-value pair and returns
+ * it as a two-item array.
+ */
+VALUE
+rbtree_shift(VALUE self)
+{
+ return rbtree_shift_pop(self, 0);
+}
+
+/*
+ * call-seq:
+ * rbtree.pop => array or object
+ *
+ * Removes the last(that is, the biggest) key-value pair and returns
+ * it as a two-item array.
+ */
+VALUE
+rbtree_pop(VALUE self)
+{
+ return rbtree_shift_pop(self, 1);
+}
+
+static each_return_t
+invert_i(dnode_t* node, void* rbtree)
+{
+ rbtree_aset((VALUE)rbtree, GET_VAL(node), GET_KEY(node));
+ return EACH_NEXT;
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_invert(VALUE self)
+{
+ VALUE rbtree = rbtree_alloc(CLASS_OF(self));
+ rbtree_for_each(self, invert_i, (void*)rbtree);
+ return rbtree;
+}
+
+static each_return_t
+update_block_i(dnode_t* node, void* self_)
+{
+ VALUE self = (VALUE)self_;
+ VALUE key = GET_KEY(node);
+ VALUE value = GET_VAL(node);
+
+ if (rbtree_has_key(self, key))
+ value = rb_yield_values(3, key, rbtree_aref(self, key), value);
+ rbtree_aset(self, key, value);
+ return EACH_NEXT;
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_update(VALUE self, VALUE other)
+{
+ rbtree_modify(self);
+
+ if (self == other)
+ return self;
+ if (!rb_obj_is_kind_of(other, CLASS_OF(self))) {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)",
+ rb_class2name(CLASS_OF(other)),
+ rb_class2name(CLASS_OF(self)));
+ }
+
+ if (rb_block_given_p())
+ rbtree_for_each(other, update_block_i, (void*)self);
+ else
+ rbtree_for_each(other, aset_i, (void*)self);
+ return self;
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_merge(VALUE self, VALUE other)
+{
+ return rbtree_update(rb_obj_dup(self), other);
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_has_key(VALUE self, VALUE key)
+{
+ return dict_lookup(DICT(self), TO_KEY(key)) == NULL ? Qfalse : Qtrue;
+}
+
+static each_return_t
+has_value_i(dnode_t* node, void* arg_)
+{
+ VALUE* arg = (VALUE*)arg_;
+ if (rb_equal(GET_VAL(node), arg[1])) {
+ arg[0] = Qtrue;
+ return EACH_STOP;
+ }
+ return EACH_NEXT;
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_has_value(VALUE self, VALUE value)
+{
+ VALUE arg[2];
+ arg[0] = Qfalse;
+ arg[1] = value;
+ rbtree_for_each(self, has_value_i, (void*)&arg);
+ return arg[0];
+}
+
+static each_return_t
+keys_i(dnode_t* node, void* ary)
+{
+ rb_ary_push((VALUE)ary, GET_KEY(node));
+ return EACH_NEXT;
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_keys(VALUE self)
+{
+ VALUE ary = rb_ary_new();
+ rbtree_for_each(self, keys_i, (void*)ary);
+ return ary;
+}
+
+static each_return_t
+values_i(dnode_t* node, void* ary)
+{
+ rb_ary_push((VALUE)ary, GET_VAL(node));
+ return EACH_NEXT;
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_values(VALUE self)
+{
+ VALUE ret = rb_ary_new();
+ rbtree_for_each(self, values_i, (void*)ret);
+ return ret;
+}
+
+static each_return_t
+to_a_i(dnode_t* node, void* ary)
+{
+ rb_ary_push((VALUE)ary, ASSOC(node));
+ return EACH_NEXT;
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_to_a(VALUE self)
+{
+ VALUE ary = rb_ary_new();
+ rbtree_for_each(self, to_a_i, (void*)ary);
+ OBJ_INFECT(ary, self);
+ return ary;
+}
+
+static each_return_t
+to_hash_i(dnode_t* node, void* hash)
+{
+ st_insert(RHASH(hash)->tbl, GET_KEY(node), GET_VAL(node));
+ return EACH_NEXT;
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_to_hash(VALUE self)
+{
+ VALUE hash = rb_hash_new();
+ rbtree_for_each(self, to_hash_i, (void*)hash);
+ RHASH(hash)->ifnone = IFNONE(self);
+ if (FL_TEST(self, RBTREE_PROC_DEFAULT))
+ FL_SET(hash, HASH_PROC_DEFAULT);
+ OBJ_INFECT(hash, self);
+ return hash;
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_to_rbtree(VALUE self)
+{
+ return self;
+}
+
+static VALUE
+rbtree_begin_inspect(VALUE self)
+{
+ const char* c = rb_class2name(CLASS_OF(self));
+ VALUE str = rb_str_new(0, strlen(c) + 5);
+ const size_t len = sprintf(RSTRING(str)->ptr, "#<%s: ", c);
+ RSTRING(str)->len = len;
+ return str;
+}
+
+static VALUE
+to_s_rbtree(VALUE self, VALUE nil)
+{
+ return rb_ary_to_s(rbtree_to_a(self));
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_to_s(VALUE self)
+{
+ if (rb_inspecting_p(self))
+ return rb_str_cat2(rbtree_begin_inspect(self), "...>");
+ return rb_protect_inspect(to_s_rbtree, self, Qnil);
+}
+
+static each_return_t
+inspect_i(dnode_t* node, void* ret_)
+{
+ VALUE ret = (VALUE)ret_;
+ VALUE str;
+
+ if (RSTRING(ret)->ptr[0] == '-')
+ RSTRING(ret)->ptr[0] = '#';
+ else
+ rb_str_cat2(ret, ", ");
+
+ str = rb_inspect(GET_KEY(node));
+ rb_str_append(ret, str);
+ OBJ_INFECT(ret, str);
+
+ rb_str_cat2(ret, "=>");
+
+ str = rb_inspect(GET_VAL(node));
+ rb_str_append(ret, str);
+ OBJ_INFECT(ret, str);
+
+ return EACH_NEXT;
+}
+
+static VALUE
+inspect_rbtree(VALUE self, VALUE ret)
+{
+ VALUE str;
+
+ rb_str_cat2(ret, "{");
+ RSTRING(ret)->ptr[0] = '-';
+ rbtree_for_each(self, inspect_i, (void*)ret);
+ RSTRING(ret)->ptr[0] = '#';
+ rb_str_cat2(ret, "}");
+
+ str = rb_inspect(IFNONE(self));
+ rb_str_cat2(ret, ", default=");
+ rb_str_append(ret, str);
+ OBJ_INFECT(ret, str);
+
+ str = rb_inspect((VALUE)CONTEXT(self));
+ rb_str_cat2(ret, ", cmp_proc=");
+ rb_str_append(ret, str);
+ OBJ_INFECT(ret, str);
+
+ rb_str_cat2(ret, ">");
+ OBJ_INFECT(ret, self);
+ return ret;
+}
+
+/*
+ *
+ */
+VALUE
+rbtree_inspect(VALUE self)
+{
+ VALUE str = rbtree_begin_inspect(self);
+ if (rb_inspecting_p(self))
+ return rb_str_cat2(str, "...>");
+ return rb_protect_inspect(inspect_rbtree, self, str);
+}
+
+/*
+ * call-seq:
+ * rbtree.lower_bound(key) => array
+ *
+ * Retruns key-value pair corresponding to the lowest key that is
+ * equal to or greater than the given key(inside of lower
+ * boundary). If there is no such key, returns nil.
+ */
+VALUE
+rbtree_lower_bound(VALUE self, VALUE key)
+{
+ dnode_t* node = dict_lower_bound(DICT(self), TO_KEY(key));
+ if (node == NULL)
+ return Qnil;
+ return ASSOC(node);
+}
+
+/*
+ * call-seq:
+ * rbtree.upper_bound(key) => array
+ *
+ * Retruns key-value pair corresponding to the greatest key that is
+ * equal to or lower than the given key(inside of upper boundary). If
+ * there is no such key, returns nil.
+ */
+VALUE
+rbtree_upper_bound(VALUE self, VALUE key)
+{
+ dnode_t* node = dict_upper_bound(DICT(self), TO_KEY(key));
+ if (node == NULL)
+ return Qnil;
+ return ASSOC(node);
+}
+
+/*********************************************************************/
+
+typedef struct {
+ VALUE self;
+ dnode_t* lower_node;
+ dnode_t* upper_node;
+ VALUE ret;
+} rbtree_bound_arg_t;
+
+static VALUE
+rbtree_bound_body(rbtree_bound_arg_t* arg)
+{
+ VALUE self = arg->self;
+ dict_t* dict = DICT(self);
+ dnode_t* lower_node = arg->lower_node;
+ dnode_t* upper_node = arg->upper_node;
+ const int block_given = rb_block_given_p();
+ VALUE ret = arg->ret;
+ dnode_t* node;
+
+ ITER_LEV(self)++;
+ for (node = lower_node;;
+ node = dict_next(dict, node)) {
+
+ if (block_given)
+ rb_yield_values(2, GET_KEY(node), GET_VAL(node));
+ else
+ rb_ary_push(ret, ASSOC(node));
+ if (node == upper_node)
+ break;
+ }
+ return ret;
+}
+
+/*********************************************************************/
+
+/*
+ * call-seq:
+ * rbtree.bound(key1, key2 = key1) => array
+ * rbtree.bound(key1, key2 = key1) {|key, value| block} => rbtree
+ *
+ * Returns an array containing key-value pairs between the result of
+ * RBTree#lower_bound and RBTree#upper_bound. If a block is given it
+ * calls the block once for each pair.
+ */
+VALUE
+rbtree_bound(int argc, VALUE* argv, VALUE self)
+{
+ dict_t* dict = DICT(self);
+ dnode_t* lower_node;
+ dnode_t* upper_node;
+ VALUE ret;
+
+ if (argc == 0 || argc > 2)
+ rbtree_argc_error();
+
+ lower_node = dict_lower_bound(dict, TO_KEY(argv[0]));
+ upper_node = dict_upper_bound(dict, TO_KEY(argv[argc - 1]));
+ ret = rb_block_given_p() ? self : rb_ary_new();
+
+ if (lower_node == NULL || upper_node == NULL ||
+ COMPARE(self)(dnode_getkey(lower_node),
+ dnode_getkey(upper_node),
+ CONTEXT(self)) > 0) {
+ return ret;
+ } else {
+ rbtree_bound_arg_t arg;
+ arg.self = self;
+ arg.lower_node = lower_node;
+ arg.upper_node = upper_node;
+ arg.ret = ret;
+
+ return rb_ensure(rbtree_bound_body, (VALUE)&arg,
+ rbtree_each_ensure, self);
+ }
+}
+
+static VALUE
+rbtree_first_last(VALUE self, const int mode)
+{
+ dict_t* dict = DICT(self);
+ dnode_t* node;
+
+ if (dict_isempty(dict)) {
+ if (FL_TEST(self, RBTREE_PROC_DEFAULT)) {
+ return rb_funcall(IFNONE(self), id_call, 2, self, Qnil);
+ }
+ return IFNONE(self);
+ }
+
+ if (mode == 0)
+ node = dict_first(dict);
+ else
+ node = dict_last(dict);
+ return ASSOC(node);
+}
+
+/*
+ * call-seq:
+ * rbtree.first => array or object
+ *
+ * Returns the first(that is, the smallest) key-value pair.
+ */
+VALUE
+rbtree_first(VALUE self)
+{
+ return rbtree_first_last(self, 0);
+}
+
+/*
+ * call-seq:
+ * rbtree.last => array of object
+ *
+ * Returns the last(that is, the biggest) key-value pair.
+ */
+VALUE
+rbtree_last(VALUE self)
+{
+ return rbtree_first_last(self, 1);
+}
+
+/*
+ * call-seq:
+ * rbtree.readjust => rbtree
+ * rbtree.readjust(nil) => rbtree
+ * rbtree.readjust(proc) => rbtree
+ * rbtree.readjust {|key1, key2| block} => rbtree
+ *
+ * Sets a proc to compare keys and readjusts elements using the given
+ * block or a Proc object given as the argument. The block takes two
+ * key arguments and returns negative, 0, or positive depending on the
+ * first argument is less than, equal to, or greater than the second
+ * one. If no block is given it just readjusts elements using current
+ * comparison block. If nil is given as the argument it sets default
+ * comparison block.
+ */
+VALUE
+rbtree_readjust(int argc, VALUE* argv, VALUE self)
+{
+ dict_comp_t cmp = NULL;
+ void* context = NULL;
+
+ rbtree_modify(self);
+
+ if (argc == 0) {
+ if (rb_block_given_p()) {
+ cmp = rbtree_user_cmp;
+ context = (void*)rb_block_proc();
+ } else {
+ cmp = COMPARE(self);
+ context = CONTEXT(self);
+ }
+ } else if (argc == 1 && !rb_block_given_p()) {
+ if (argv[0] == Qnil) {
+ cmp = rbtree_cmp;
+ context = (void*)Qnil;
+ } else {
+ if (CLASS_OF(argv[0]) != rb_cProc)
+ rb_raise(rb_eTypeError,
+ "wrong argument type %s (expected Proc)",
+ rb_class2name(CLASS_OF(argv[0])));
+ cmp = rbtree_user_cmp;
+ context = (void*)argv[0];
+ }
+ } else {
+ rbtree_argc_error();
+ }
+
+ if (dict_isempty(DICT(self))) {
+ COMPARE(self) = cmp;
+ CONTEXT(self) = context;
+ return self;
+ }
+ copy_dict(self, self, cmp, context);
+ return self;
+}
+
+/*
+ * call-seq:
+ * rbtree.cmp_proc => proc
+ *
+ * Returns the comparison block that is given by RBTree#readjust.
+ */
+VALUE
+rbtree_cmp_proc(VALUE self)
+{
+ return (VALUE)(CONTEXT(self));
+}
+
+/*********************************************************************/
+
+static ID id_comma_breakable;
+static ID id_object_group;
+static ID id_pp;
+static ID id_pp_hash;
+static ID id_text;
+
+typedef struct {
+ VALUE rbtree;
+ VALUE pp;
+} pp_arg_t;
+
+static VALUE
+pp_object_group(VALUE arg_)
+{
+ pp_arg_t* arg = (pp_arg_t*)arg_;
+ return rb_funcall(arg->pp, id_object_group, 1, arg->rbtree);
+}
+
+static VALUE
+pp_block(VALUE nil, pp_arg_t* arg)
+{
+ VALUE pp = arg->pp;
+ VALUE rbtree = arg->rbtree;
+
+ rb_funcall(pp, id_text, 1, rb_str_new2(": "));
+ rb_funcall(pp, id_pp_hash, 1, rbtree);
+ rb_funcall(pp, id_comma_breakable, 0);
+ rb_funcall(pp, id_text, 1, rb_str_new2("default="));
+ rb_funcall(pp, id_pp, 1, IFNONE(rbtree));
+ rb_funcall(pp, id_comma_breakable, 0);
+ rb_funcall(pp, id_text, 1, rb_str_new2("cmp_proc="));
+ rb_funcall(pp, id_pp, 1, CONTEXT(rbtree));
+ return pp;
+}
+
+/*********************************************************************/
+
+/*
+ * Called by pretty printing function pp.
+ */
+VALUE
+rbtree_pretty_print(VALUE self, VALUE pp)
+{
+ pp_arg_t pp_arg;
+ pp_arg.rbtree = self;
+ pp_arg.pp = pp;
+
+ return rb_iterate(pp_object_group, (VALUE)&pp_arg,
+ pp_block, (VALUE)&pp_arg);
+}
+
+/*
+ * Called by pretty printing function pp.
+ */
+VALUE
+rbtree_pretty_print_cycle(VALUE self, VALUE pp)
+{
+ return rb_funcall(pp, id_pp, 1, rbtree_inspect(self));
+}
+
+/*********************************************************************/
+
+#ifndef HAVE_RB_MARSHAL_DUMP
+static VALUE
+rb_marshal_dump(VALUE obj, VALUE port)
+{
+ return rb_funcall(rb_mMarshal, id_dump, 2, obj, port);
+}
+
+static VALUE
+rb_marshal_load(VALUE port)
+{
+ return rb_funcall(rb_mMarshal, id_load, 1, port);
+}
+#endif
+
+static each_return_t
+to_flatten_ary_i(dnode_t* node, void* ary)
+{
+ rb_ary_push((VALUE)ary, GET_KEY(node));
+ rb_ary_push((VALUE)ary, GET_VAL(node));
+ return EACH_NEXT;
+}
+
+/*********************************************************************/
+
+/*
+ * Called by Marshal.dump.
+ */
+VALUE
+rbtree_dump(VALUE self, VALUE limit)
+{
+ VALUE ary;
+ VALUE ret;
+
+ if (FL_TEST(self, RBTREE_PROC_DEFAULT))
+ rb_raise(rb_eTypeError, "cannot dump rbtree with default proc");
+ if ((VALUE)CONTEXT(self) != Qnil)
+ rb_raise(rb_eTypeError, "cannot dump rbtree with compare proc");
+
+ ary = rb_ary_new2(dict_count(DICT(self)) * 2 + 1);
+ rbtree_for_each(self, to_flatten_ary_i, (void*)ary);
+ rb_ary_push(ary, IFNONE(self));
+
+ ret = rb_marshal_dump(ary, limit);
+ rb_ary_clear(ary);
+ rb_gc_force_recycle(ary);
+ return ret;
+}
+
+/*
+ * Called by Marshal.load.
+ */
+VALUE
+rbtree_s_load(VALUE klass, VALUE str)
+{
+ VALUE rbtree = rbtree_alloc(klass);
+ VALUE ary = rb_marshal_load(str);
+ VALUE* ptr = RARRAY(ary)->ptr;
+ long len = RARRAY(ary)->len - 1;
+ long i;
+
+ for (i = 0; i < len; i += 2)
+ rbtree_aset(rbtree, ptr[i], ptr[i + 1]);
+ IFNONE(rbtree) = ptr[len];
+
+ rb_ary_clear(ary);
+ rb_gc_force_recycle(ary);
+ return rbtree;
+}
+
+/*********************************************************************/
+
+/*
+ * Document-class: RBTree
+ *
+ * RBTree is a sorted associative collection using Red-Black Tree as
+ * the internal data structure. The elements of RBTree are ordered and
+ * the interface is the almost same as Hash, so simply you can
+ * consider RBTree sorted Hash.
+ *
+ * Red-Black Tree is a kind of binary tree that automatically balances
+ * by itself when a node is inserted or deleted. Thus the complexity
+ * for insert, search and delete is O(log N) in expected and worst
+ * case. On the other hand the complexity of Hash is O(1). Because
+ * Hash is unordered the data structure is more effective than
+ * Red-Black Tree as an associative collection.
+ *
+ * The interface of RBTree is the almost same as Hash although there
+ * are some limitations.
+ *
+ * * While iterating (e.g. in RBTree#each block), RBTree is
+ * unmodifiable.
+ *
+ * * Comparison is done using <=> method of key objects. So all types
+ * of keys in RBTree should be comparable each other or an arbitrary
+ * Proc might be set by RBTree#readjust.
+ *
+ * RBTree has a few searching methods that Hash doesn't have. They are
+ * RBTree#lower_bound, RBTree#upper_bound and RBTree#bound. See
+ * document of each method for details.
+ *
+ * Pretty printing is available for RBTree by using pp.rb. The output
+ * of pp is easier than p to read. Just call Kernel#pp for the object.
+ *
+ * MultiRBTree that allows duplicates of keys is also available.
+ */
+void Init_rbtree()
+{
+ MultiRBTree = rb_define_class("MultiRBTree", rb_cData);
+ RBTree = rb_define_class("RBTree", MultiRBTree);
+
+ rb_include_module(MultiRBTree, rb_mEnumerable);
+
+#ifdef HAVE_OBJECT_ALLOCATE
+ rb_define_alloc_func(MultiRBTree, rbtree_alloc);
+#else
+ rb_define_singleton_method(MultiRBTree, "new", rbtree_s_new, -1);
+#endif
+ rb_define_singleton_method(MultiRBTree, "[]", rbtree_s_create, -1);
+
+ rb_define_method(MultiRBTree, "initialize", rbtree_initialize, -1);
+
+#ifdef HAVE_RB_OBJ_INIT_COPY
+ rb_define_method(MultiRBTree, "initialize_copy", rbtree_initialize_copy, 1);
+#else
+ rb_define_method(MultiRBTree, "clone", rbtree_clone, 0);
+#endif
+
+ rb_define_method(MultiRBTree, "to_a", rbtree_to_a, 0);
+ rb_define_method(MultiRBTree, "to_s", rbtree_to_s, 0);
+ rb_define_method(MultiRBTree, "to_hash", rbtree_to_hash, 0);
+ rb_define_method(MultiRBTree, "to_rbtree", rbtree_to_rbtree, 0);
+ rb_define_method(MultiRBTree, "inspect", rbtree_inspect, 0);
+
+ rb_define_method(MultiRBTree, "==", rbtree_equal, 1);
+ rb_define_method(MultiRBTree, "[]", rbtree_aref, 1);
+ rb_define_method(MultiRBTree, "fetch", rbtree_fetch, -1);
+ rb_define_method(MultiRBTree, "lower_bound", rbtree_lower_bound, 1);
+ rb_define_method(MultiRBTree, "upper_bound", rbtree_upper_bound, 1);
+ rb_define_method(MultiRBTree, "bound", rbtree_bound, -1);
+ rb_define_method(MultiRBTree, "first", rbtree_first, 0);
+ rb_define_method(MultiRBTree, "last", rbtree_last, 0);
+ rb_define_method(MultiRBTree, "[]=", rbtree_aset, 2);
+ rb_define_method(MultiRBTree, "store", rbtree_aset, 2);
+ rb_define_method(MultiRBTree, "default", rbtree_default, -1);
+ rb_define_method(MultiRBTree, "default=", rbtree_set_default, 1);
+ rb_define_method(MultiRBTree, "default_proc", rbtree_default_proc, 0);
+ rb_define_method(MultiRBTree, "index", rbtree_index, 1);
+ rb_define_method(MultiRBTree, "empty?", rbtree_empty_p, 0);
+ rb_define_method(MultiRBTree, "size", rbtree_size, 0);
+ rb_define_method(MultiRBTree, "length", rbtree_size, 0);
+
+ rb_define_method(MultiRBTree, "each", rbtree_each, 0);
+ rb_define_method(MultiRBTree, "each_value", rbtree_each_value, 0);
+ rb_define_method(MultiRBTree, "each_key", rbtree_each_key, 0);
+ rb_define_method(MultiRBTree, "each_pair", rbtree_each_pair, 0);
+ rb_define_method(MultiRBTree, "reverse_each", rbtree_reverse_each, 0);
+
+ rb_define_method(MultiRBTree, "keys", rbtree_keys, 0);
+ rb_define_method(MultiRBTree, "values", rbtree_values, 0);
+ rb_define_method(MultiRBTree, "values_at", rbtree_values_at, -1);
+
+ rb_define_method(MultiRBTree, "shift", rbtree_shift, 0);
+ rb_define_method(MultiRBTree, "pop", rbtree_pop, 0);
+ rb_define_method(MultiRBTree, "delete", rbtree_delete, 1);
+ rb_define_method(MultiRBTree, "delete_if", rbtree_delete_if, 0);
+ rb_define_method(MultiRBTree, "select", rbtree_select, 0);
+ rb_define_method(MultiRBTree, "reject", rbtree_reject, 0);
+ rb_define_method(MultiRBTree, "reject!", rbtree_reject_bang, 0);
+ rb_define_method(MultiRBTree, "clear", rbtree_clear, 0);
+ rb_define_method(MultiRBTree, "invert", rbtree_invert, 0);
+ rb_define_method(MultiRBTree, "update", rbtree_update, 1);
+ rb_define_method(MultiRBTree, "merge!", rbtree_update, 1);
+ rb_define_method(MultiRBTree, "merge", rbtree_merge, 1);
+ rb_define_method(MultiRBTree, "replace", rbtree_initialize_copy, 1);
+
+ rb_define_method(MultiRBTree, "include?", rbtree_has_key, 1);
+ rb_define_method(MultiRBTree, "member?", rbtree_has_key, 1);
+ rb_define_method(MultiRBTree, "has_key?", rbtree_has_key, 1);
+ rb_define_method(MultiRBTree, "has_value?", rbtree_has_value, 1);
+ rb_define_method(MultiRBTree, "key?", rbtree_has_key, 1);
+ rb_define_method(MultiRBTree, "value?", rbtree_has_value, 1);
+
+ rb_define_method(MultiRBTree, "readjust", rbtree_readjust, -1);
+ rb_define_method(MultiRBTree, "cmp_proc", rbtree_cmp_proc, 0);
+
+ rb_define_method(MultiRBTree, "_dump", rbtree_dump, 1);
+ rb_define_singleton_method(MultiRBTree, "_load", rbtree_s_load, 1);
+#ifndef HAVE_RB_MARSHAL_DUMP
+ rb_mMarshal = rb_path2class("Marshal");
+ id_dump = rb_intern("dump");
+ id_load = rb_intern("load");
+#endif
+
+ id_bound = rb_intern("bound");
+ id_cmp = rb_intern("<=>");
+ id_call = rb_intern("call");
+ id_default = rb_intern("default");
+
+
+ rb_define_method(MultiRBTree, "pretty_print", rbtree_pretty_print, 1);
+ rb_define_method(MultiRBTree,
+ "pretty_print_cycle", rbtree_pretty_print_cycle, 1);
+
+ id_comma_breakable = rb_intern("comma_breakable");
+ id_object_group = rb_intern("object_group");
+ id_pp_hash = rb_intern("pp_hash");
+ id_text = rb_intern("text");
+ id_pp = rb_intern("pp");
+}
Added: trunk/rbtree-0.2.0/test.rb
===================================================================
--- trunk/rbtree-0.2.0/test.rb (rev 0)
+++ trunk/rbtree-0.2.0/test.rb 2007-03-09 10:28:46 UTC (rev 236)
@@ -0,0 +1,895 @@
+require "rbtree"
+require "test/unit.rb"
+
+module Test
+ module Unit
+ class TestCase
+ unless method_defined?(:assert_raise)
+ alias :assert_raise :assert_raises
+ end
+ end
+ end
+end
+
+class RBTreeTest < Test::Unit::TestCase
+ def setup
+ @rbtree = RBTree[*%w(b B d D a A c C)]
+ end
+
+ def test_new
+ assert_nothing_raised {
+ RBTree.new
+ RBTree.new("a")
+ RBTree.new { "a" }
+ }
+ assert_raise(ArgumentError) { RBTree.new("a") {} }
+ assert_raise(ArgumentError) { RBTree.new("a", "a") }
+ end
+
+ def test_aref
+ assert_equal("A", @rbtree["a"])
+ assert_equal("B", @rbtree["b"])
+ assert_equal("C", @rbtree["c"])
+ assert_equal("D", @rbtree["d"])
+
+ assert_equal(nil, @rbtree["e"])
+ @rbtree.default = "E"
+ assert_equal("E", @rbtree["e"])
+ end
+
+ def test_size
+ assert_equal(4, @rbtree.size)
+ end
+
+ def test_create
+ rbtree = RBTree[]
+ assert_equal(0, rbtree.size)
+
+ rbtree = RBTree[@rbtree]
+ assert_equal(4, rbtree.size)
+ assert_equal("A", @rbtree["a"])
+ assert_equal("B", @rbtree["b"])
+ assert_equal("C", @rbtree["c"])
+ assert_equal("D", @rbtree["d"])
+
+ rbtree = RBTree[RBTree.new("e")]
+ assert_equal(nil, rbtree.default)
+ rbtree = RBTree[RBTree.new { "e" }]
+ assert_equal(nil, rbtree.default_proc)
+ @rbtree.readjust {|a,b| b <=> a }
+ assert_equal(nil, RBTree[@rbtree].cmp_proc)
+
+ assert_raise(ArgumentError) { RBTree["e"] }
+
+ rbtree = RBTree[Hash[*%w(b B d D a A c C)]]
+ assert_equal(4, rbtree.size)
+ assert_equal("A", @rbtree["a"])
+ assert_equal("B", @rbtree["b"])
+ assert_equal("C", @rbtree["c"])
+ assert_equal("D", @rbtree["d"])
+ end
+
+ def test_clear
+ @rbtree.clear
+ assert_equal(0, @rbtree.size)
+ end
+
+ def test_aset
+ @rbtree["e"] = "E"
+ assert_equal(5, @rbtree.size)
+ assert_equal("E", @rbtree["e"])
+
+ @rbtree["c"] = "E"
+ assert_equal(5, @rbtree.size)
+ assert_equal("E", @rbtree["c"])
+
+ if VERSION >= "1.8.0"
+ assert_raise(ArgumentError) { @rbtree[100] = 100 }
+ assert_equal(5, @rbtree.size)
+ end
+
+
+ key = "f"
+ @rbtree[key] = "F"
+ cloned_key = @rbtree.last[0]
+ assert_equal("f", cloned_key)
+ assert_not_same(key, cloned_key)
+ assert_equal(true, cloned_key.frozen?)
+
+ @rbtree["f"] = "F"
+ assert_same(cloned_key, @rbtree.last[0])
+
+ rbtree = RBTree.new
+ key = ["g"]
+ rbtree[key] = "G"
+ assert_same(key, rbtree.first[0])
+ assert_equal(false, key.frozen?)
+ end
+
+ def test_clone
+ clone = @rbtree.clone
+ assert_equal(4, @rbtree.size)
+ assert_equal("A", @rbtree["a"])
+ assert_equal("B", @rbtree["b"])
+ assert_equal("C", @rbtree["c"])
+ assert_equal("D", @rbtree["d"])
+
+ rbtree = RBTree.new("e")
+ clone = rbtree.clone
+ assert_equal("e", clone.default)
+
+ rbtree = RBTree.new { "e" }
+ clone = rbtree.clone
+ assert_equal("e", clone.default(nil))
+
+ rbtree = RBTree.new
+ rbtree.readjust {|a, b| a <=> b }
+ clone = rbtree.clone
+ assert_equal(rbtree.cmp_proc, clone.cmp_proc)
+ end
+
+ def test_default
+ assert_equal(nil, @rbtree.default)
+
+ rbtree = RBTree.new("e")
+ assert_equal("e", rbtree.default)
+ assert_equal("e", rbtree.default("f"))
+ assert_raise(ArgumentError) { rbtree.default("e", "f") }
+
+ rbtree = RBTree.new {|rbtree, key| @rbtree[key || "c"] }
+ assert_equal("C", rbtree.default(nil))
+ assert_equal("B", rbtree.default("b"))
+ end
+
+ def test_set_default
+ rbtree = RBTree.new { "e" }
+ rbtree.default = "f"
+ assert_equal("f", rbtree.default)
+ end
+
+ def test_default_proc
+ rbtree = RBTree.new("e")
+ assert_equal(nil, rbtree.default_proc)
+
+ rbtree = RBTree.new { "e" }
+ assert_equal("e", rbtree.default_proc.call)
+ end
+
+ def test_equal
+ assert_equal(RBTree.new, RBTree.new)
+ assert_equal(@rbtree, @rbtree)
+ assert_not_equal(@rbtree, RBTree.new)
+
+ rbtree = RBTree[*%w(b B d D a A c C)]
+ assert_equal(@rbtree, rbtree)
+ rbtree["d"] = "A"
+ assert_not_equal(@rbtree, rbtree)
+ rbtree["d"] = "D"
+ rbtree["e"] = "E"
+ assert_not_equal(@rbtree, rbtree)
+ @rbtree["e"] = "E"
+ assert_equal(@rbtree, rbtree)
+
+ rbtree.default = "e"
+ assert_equal(@rbtree, rbtree)
+ @rbtree.default = "f"
+ assert_equal(@rbtree, rbtree)
+
+ a = RBTree.new("e")
+ b = RBTree.new { "f" }
+ assert_equal(a, b)
+ assert_equal(b, b.clone)
+
+ a = RBTree.new
+ b = RBTree.new
+ a.readjust {|a, b| a <=> b }
+ assert_not_equal(a, b)
+ b.readjust(a.cmp_proc)
+ assert_equal(a, b)
+ end
+
+ def test_fetch
+ assert_equal("A", @rbtree.fetch("a"))
+ assert_equal("B", @rbtree.fetch("b"))
+ assert_equal("C", @rbtree.fetch("c"))
+ assert_equal("D", @rbtree.fetch("d"))
+
+ assert_raise(IndexError) { @rbtree.fetch("e") }
+
+ assert_equal("E", @rbtree.fetch("e", "E"))
+ assert_equal("E", @rbtree.fetch("e") { "E" })
+ assert_equal("E", @rbtree.fetch("e", "F") { "E" })
+
+ assert_raise(ArgumentError) { @rbtree.fetch }
+ assert_raise(ArgumentError) { @rbtree.fetch("e", "E", "E") }
+ end
+
+ def test_index
+ assert_equal("a", @rbtree.index("A"))
+ assert_equal(nil, @rbtree.index("E"))
+ end
+
+ def test_empty_p
+ assert_equal(false, @rbtree.empty?)
+ @rbtree.clear
+ assert_equal(true, @rbtree.empty?)
+ end
+
+ def test_each
+ ret = []
+ @rbtree.each {|key, val| ret << key << val }
+ assert_equal(%w(a A b B c C d D), ret)
+
+ assert_raise(TypeError) {
+ @rbtree.each { @rbtree["e"] = "E" }
+ }
+ assert_equal(4, @rbtree.size)
+
+ @rbtree.each {
+ @rbtree.each {}
+ assert_raise(TypeError) {
+ @rbtree["e"] = "E"
+ }
+ break
+ }
+ assert_equal(4, @rbtree.size)
+ end
+
+ def test_each_pair
+ ret = []
+ @rbtree.each_pair {|key, val| ret << key << val }
+ assert_equal(%w(a A b B c C d D), ret)
+
+ assert_raise(TypeError) {
+ @rbtree.each_pair { @rbtree["e"] = "E" }
+ }
+ assert_equal(4, @rbtree.size)
+
+ @rbtree.each_pair {
+ @rbtree.each_pair {}
+ assert_raise(TypeError) {
+ @rbtree["e"] = "E"
+ }
+ break
+ }
+ assert_equal(4, @rbtree.size)
+ end
+
+ def test_each_key
+ ret = []
+ @rbtree.each_key {|val| ret.push(val) }
+ assert_equal(%w(a b c d), ret)
+
+ assert_raise(TypeError) {
+ @rbtree.each_key { @rbtree["e"] = "E" }
+ }
+ assert_equal(4, @rbtree.size)
+
+ @rbtree.each_key {
+ @rbtree.each_key {}
+ assert_raise(TypeError) {
+ @rbtree["e"] = "E"
+ }
+ break
+ }
+ assert_equal(4, @rbtree.size)
+ end
+
+ def test_each_value
+ ret = []
+ @rbtree.each_value {|key| ret.push(key) }
+ assert_equal(%w(A B C D), ret)
+
+ assert_raise(TypeError) {
+ @rbtree.each_value { @rbtree["e"] = "E" }
+ }
+ assert_equal(4, @rbtree.size)
+
+ @rbtree.each_value {
+ @rbtree.each_value {}
+ assert_raise(TypeError) {
+ @rbtree["e"] = "E"
+ }
+ break
+ }
+ assert_equal(4, @rbtree.size)
+ end
+
+ def test_shift
+ ret = @rbtree.shift
+ assert_equal(3, @rbtree.size)
+ assert_equal(["a", "A"], ret)
+ assert_equal(nil, @rbtree["a"])
+
+ 3.times { @rbtree.shift }
+ assert_equal(0, @rbtree.size)
+ assert_equal(nil, @rbtree.shift)
+ @rbtree.default = "e"
+ assert_equal("e", @rbtree.shift)
+
+ rbtree = RBTree.new { "e" }
+ assert_equal("e", rbtree.shift)
+ end
+
+ def test_pop
+ ret = @rbtree.pop
+ assert_equal(3, @rbtree.size)
+ assert_equal(["d", "D"], ret)
+ assert_equal(nil, @rbtree["d"])
+
+ 3.times { @rbtree.pop }
+ assert_equal(0, @rbtree.size)
+ assert_equal(nil, @rbtree.pop)
+ @rbtree.default = "e"
+ assert_equal("e", @rbtree.pop)
+
+ rbtree = RBTree.new { "e" }
+ assert_equal("e", rbtree.pop)
+ end
+
+ def test_delete
+ ret = @rbtree.delete("c")
+ assert_equal("C", ret)
+ assert_equal(3, @rbtree.size)
+ assert_equal(nil, @rbtree["c"])
+
+ assert_equal(nil, @rbtree.delete("e"))
+ assert_equal("E", @rbtree.delete("e") { "E" })
+ end
+
+ def test_delete_if
+ @rbtree.delete_if {|key, val| val == "A" || val == "B" }
+ assert_equal(RBTree[*%w(c C d D)], @rbtree)
+
+ assert_raise(ArgumentError) {
+ @rbtree.delete_if {|key, val| key == "c" or raise ArgumentError }
+ }
+ assert_equal(2, @rbtree.size)
+
+ assert_raise(TypeError) {
+ @rbtree.delete_if { @rbtree["e"] = "E" }
+ }
+ assert_equal(2, @rbtree.size)
+
+ @rbtree.delete_if {
+ @rbtree.each {
+ assert_equal(2, @rbtree.size)
+ }
+ assert_raise(TypeError) {
+ @rbtree["e"] = "E"
+ }
+ true
+ }
+ assert_equal(0, @rbtree.size)
+ end
+
+ def test_reject_bang
+ ret = @rbtree.reject! { false }
+ assert_equal(nil, ret)
+ assert_equal(4, @rbtree.size)
+
+ ret = @rbtree.reject! {|key, val| val == "A" || val == "B" }
+ assert_same(@rbtree, ret)
+ assert_equal(RBTree[*%w(c C d D)], ret)
+ end
+
+ def test_reject
+ ret = @rbtree.reject { false }
+ assert_equal(nil, ret)
+ assert_equal(4, @rbtree.size)
+
+ ret = @rbtree.reject {|key, val| val == "A" || val == "B" }
+ assert_equal(RBTree[*%w(c C d D)], ret)
+ assert_equal(4, @rbtree.size)
+ end
+
+ def test_select
+ ret = @rbtree.select {|key, val| val == "A" || val == "B" }
+ assert_equal(%w(a A b B), ret.flatten)
+ assert_raise(ArgumentError) { @rbtree.select("c") }
+ end
+
+ def test_values_at
+ ret = @rbtree.values_at("d", "a", "e")
+ assert_equal(["D", "A", nil], ret)
+ end
+
+ def test_invert
+ assert_equal(RBTree[*%w(A a B b C c D d)], @rbtree.invert)
+ end
+
+ def test_update
+ rbtree = RBTree.new
+ rbtree["e"] = "E"
+ @rbtree.update(rbtree)
+ assert_equal(RBTree[*%w(a A b B c C d D e E)], @rbtree)
+
+ @rbtree.clear
+ @rbtree["d"] = "A"
+ rbtree.clear
+ rbtree["d"] = "B"
+
+ @rbtree.update(rbtree) {|key, val1, val2|
+ val1 + val2 if key == "d"
+ }
+ assert_equal(RBTree[*%w(d AB)], @rbtree)
+
+ assert_raise(TypeError) { @rbtree.update("e") }
+ end
+
+ def test_merge
+ rbtree = RBTree.new
+ rbtree["e"] = "E"
+
+ ret = @rbtree.merge(rbtree)
+ assert_equal(RBTree[*%w(a A b B c C d D e E)], ret)
+
+ assert_equal(4, @rbtree.size)
+ end
+
+ def test_has_key
+ assert_equal(true, @rbtree.has_key?("a"))
+ assert_equal(true, @rbtree.has_key?("b"))
+ assert_equal(true, @rbtree.has_key?("c"))
+ assert_equal(true, @rbtree.has_key?("d"))
+ assert_equal(false, @rbtree.has_key?("e"))
+ end
+
+ def test_has_value
+ assert_equal(true, @rbtree.has_value?("A"))
+ assert_equal(true, @rbtree.has_value?("B"))
+ assert_equal(true, @rbtree.has_value?("C"))
+ assert_equal(true, @rbtree.has_value?("D"))
+ assert_equal(false, @rbtree.has_value?("E"))
+ end
+
+ def test_keys
+ assert_equal(%w(a b c d), @rbtree.keys)
+ end
+
+ def test_values
+ assert_equal(%w(A B C D), @rbtree.values)
+ end
+
+ def test_to_a
+ assert_equal([%w(a A), %w(b B), %w(c C), %w(d D)], @rbtree.to_a)
+ end
+
+ def test_to_s
+ assert_equal("aAbBcCdD", @rbtree.to_s)
+ end
+
+ def test_to_hash
+ @rbtree.default = "e"
+ hash = @rbtree.to_hash
+ assert_equal(@rbtree.to_a.flatten, hash.to_a.flatten)
+ assert_equal("e", hash.default)
+
+ rbtree = RBTree.new { "e" }
+ hash = rbtree.to_hash
+ if (hash.respond_to?(:default_proc))
+ assert_equal(rbtree.default_proc, hash.default_proc)
+ else
+ assert_equal(rbtree.default_proc, hash.default)
+ end
+ end
+
+ def test_to_rbtree
+ assert_same(@rbtree, @rbtree.to_rbtree)
+ end
+
+ def test_inspect
+ @rbtree.default = "e"
+ @rbtree.readjust {|a, b| a <=> b}
+ re = /#/
+
+ assert_match(re, @rbtree.inspect)
+ match = re.match(@rbtree.inspect)
+ tree, default, cmp_proc = match.to_a[1..-1]
+ assert_equal(%({"a"=>"A", "b"=>"B", "c"=>"C", "d"=>"D"}), tree)
+ assert_equal(%("e"), default)
+ assert_match(/#/, cmp_proc)
+
+ rbtree = RBTree.new
+ assert_match(re, rbtree.inspect)
+ match = re.match(rbtree.inspect)
+ tree, default, cmp_proc = match.to_a[1..-1]
+ assert_equal("{}", tree)
+ assert_equal("nil", default)
+ assert_equal("nil", cmp_proc)
+
+ rbtree = RBTree.new
+ rbtree["e"] = rbtree
+ assert_match(re, rbtree.inspect)
+ match = re.match(rbtree.inspect)
+ assert_equal(%({"e"=>#}), match[1])
+ end
+
+ def test_lower_bound
+ rbtree = RBTree[*%w(a A c C e E)]
+ assert_equal(["c", "C"], rbtree.lower_bound("c"))
+ assert_equal(["c", "C"], rbtree.lower_bound("b"))
+ assert_equal(nil, rbtree.lower_bound("f"))
+ end
+
+ def test_upper_bound
+ rbtree = RBTree[*%w(a A c C e E)]
+ assert_equal(["c", "C"], rbtree.upper_bound("c"))
+ assert_equal(["c", "C"], rbtree.upper_bound("d"))
+ assert_equal(nil, rbtree.upper_bound("Z"))
+ end
+
+ def test_bound
+ rbtree = RBTree[*%w(a A c C e E)]
+ assert_equal(%w(a A c C), rbtree.bound("a", "c").flatten)
+ assert_equal(%w(a A), rbtree.bound("a").flatten)
+ assert_equal(%w(c C e E), rbtree.bound("b", "f").flatten)
+
+ assert_equal([], rbtree.bound("b", "b"))
+ assert_equal([], rbtree.bound("Y", "Z"))
+ assert_equal([], rbtree.bound("f", "g"))
+ assert_equal([], rbtree.bound("f", "Z"))
+ end
+
+ def test_bound_block
+ ret = []
+ @rbtree.bound("b", "c") {|key, val|
+ ret.push(key)
+ }
+ assert_equal(%w(b c), ret)
+
+ assert_raise(TypeError) {
+ @rbtree.bound("a", "d") {
+ @rbtree["e"] = "E"
+ }
+ }
+ assert_equal(4, @rbtree.size)
+
+ @rbtree.bound("b", "c") {
+ @rbtree.bound("b", "c") {}
+ assert_raise(TypeError) {
+ @rbtree["e"] = "E"
+ }
+ break
+ }
+ assert_equal(4, @rbtree.size)
+ end
+
+ def test_first
+ assert_equal(["a", "A"], @rbtree.first)
+
+ rbtree = RBTree.new("e")
+ assert_equal("e", rbtree.first)
+
+ rbtree = RBTree.new { "e" }
+ assert_equal("e", rbtree.first)
+ end
+
+ def test_last
+ assert_equal(["d", "D"], @rbtree.last)
+
+ rbtree = RBTree.new("e")
+ assert_equal("e", rbtree.last)
+
+ rbtree = RBTree.new { "e" }
+ assert_equal("e", rbtree.last)
+ end
+
+ def test_readjust
+ assert_equal(nil, @rbtree.cmp_proc)
+
+ @rbtree.readjust {|a, b| b <=> a }
+ assert_equal(%w(d c b a), @rbtree.keys)
+ assert_not_equal(nil, @rbtree.cmp_proc)
+
+ proc = Proc.new {|a,b| a.to_s <=> b.to_s }
+ @rbtree.readjust(proc)
+ assert_equal(%w(a b c d), @rbtree.keys)
+ assert_equal(proc, @rbtree.cmp_proc)
+
+ @rbtree[0] = nil
+ if VERSION >= "1.8.0"
+ assert_raise(ArgumentError) { @rbtree.readjust(nil) }
+ assert_equal(5, @rbtree.size)
+ assert_equal(proc, @rbtree.cmp_proc)
+
+ @rbtree.delete(0)
+ @rbtree.readjust(nil)
+ assert_raise(ArgumentError) { @rbtree[0] = nil }
+ end
+
+
+ rbtree = RBTree.new
+ key = ["a"]
+ rbtree[key] = nil
+ rbtree[["e"]] = nil
+ key[0] = "f"
+
+ assert_equal([["f"], ["e"]], rbtree.keys)
+ rbtree.readjust
+ assert_equal([["e"], ["f"]], rbtree.keys)
+
+ if VERSION >= "1.8.0"
+ assert_raise(ArgumentError) { @rbtree.readjust { "e" } }
+ end
+ assert_raise(TypeError) { @rbtree.readjust("e") }
+ assert_raise(ArgumentError) {
+ @rbtree.readjust(proc) {|a,b| a <=> b }
+ }
+ assert_raise(ArgumentError) { @rbtree.readjust(proc, proc) }
+ end
+
+ def test_replace
+ rbtree = RBTree.new { "e" }
+ rbtree.readjust {|a, b| a <=> b}
+ rbtree["a"] = "A"
+ rbtree["e"] = "E"
+
+ @rbtree.replace(rbtree)
+ assert_equal(%w(a A e E), @rbtree.to_a.flatten)
+ assert_equal(rbtree.default, @rbtree.default)
+ assert_equal(rbtree.cmp_proc, @rbtree.cmp_proc)
+
+ assert_raise(TypeError) { @rbtree.replace("e") }
+ end
+
+ def test_reverse_each
+ ret = []
+ @rbtree.reverse_each { |key, val| ret.push([key, val]) }
+ assert_equal(%w(d D c C b B a A), ret.flatten)
+ end
+
+ def test_marshal
+ assert_equal(@rbtree, Marshal.load(Marshal.dump(@rbtree)))
+
+ @rbtree.default = "e"
+ assert_equal(@rbtree, Marshal.load(Marshal.dump(@rbtree)))
+
+ assert_raise(TypeError) {
+ Marshal.dump(RBTree.new { "e" })
+ }
+
+ assert_raise(TypeError) {
+ @rbtree.readjust {|a, b| a <=> b}
+ Marshal.dump(@rbtree)
+ }
+ end
+
+ begin
+ require "pp"
+
+ def test_pp
+ assert_equal(%(#\n),
+ PP.pp(RBTree[], ""))
+ assert_equal(%(#"A", "b"=>"B"}, default=nil, cmp_proc=nil>\n),
+ PP.pp(RBTree[*%w(a A b B)], ""))
+
+ rbtree = RBTree[*("a".."z").to_a]
+ rbtree.default = "a"
+ rbtree.readjust {|a, b| a <=> b }
+ expected = <"b",
+ "c"=>"d",
+ "e"=>"f",
+ "g"=>"h",
+ "i"=>"j",
+ "k"=>"l",
+ "m"=>"n",
+ "o"=>"p",
+ "q"=>"r",
+ "s"=>"t",
+ "u"=>"v",
+ "w"=>"x",
+ "y"=>"z"},
+ default="a",
+ cmp_proc=#{rbtree.cmp_proc}>
+EOS
+ assert_equal(expected, PP.pp(rbtree, ""))
+
+ rbtree = RBTree[]
+ rbtree[rbtree] = rbtree
+ rbtree.default = rbtree
+ expected = <"=>"#"},
+ default="#",
+ cmp_proc=nil>
+EOS
+ assert_equal(expected, PP.pp(rbtree, ""))
+ end
+ rescue LoadError
+ end
+end
+
+
+class MultiRBTreeTest < Test::Unit::TestCase
+ def setup
+ @rbtree = MultiRBTree[*%w(a A b B b C b D c C)]
+ end
+
+ def test_create
+ assert_equal(%w(a A b B b C b D c C), @rbtree.to_a.flatten)
+
+ assert_equal(MultiRBTree[*%w(a A)], MultiRBTree[RBTree[*%w(a A)]])
+ assert_raise(ArgumentError) {
+ RBTree[MultiRBTree[*%w(a A)]]
+ }
+ end
+
+ def test_size
+ assert_equal(5, @rbtree.size)
+ end
+
+ def test_clear
+ @rbtree.clear
+ assert_equal(0, @rbtree.size)
+ end
+
+ def test_empty
+ assert_equal(false, @rbtree.empty?)
+ @rbtree.clear
+ assert_equal(true, @rbtree.empty?)
+ end
+
+ def test_to_a
+ assert_equal([%w(a A), %w(b B), %w(b C), %w(b D), %w(c C)],
+ @rbtree.to_a)
+ end
+
+ def test_to_s
+ assert_equal("aAbBbCbDcC", @rbtree.to_s)
+ end
+
+ def test_to_hash
+ assert_equal(Hash[*%w(a A b D c C)], @rbtree.to_hash)
+ end
+
+ def test_to_rbtree
+ assert_equal(@rbtree, @rbtree.to_rbtree)
+ end
+
+ def test_aref
+ assert_equal("B", @rbtree["b"])
+ end
+
+ def test_aset
+ @rbtree["b"] = "A"
+ assert_equal("B", @rbtree["b"])
+ assert_equal(%w(a A b B b C b D b A c C), @rbtree.to_a.flatten)
+ end
+
+ def test_equal
+ assert_equal(true, MultiRBTree[*%w(a A b B b C b D c C)] == @rbtree)
+ assert_equal(true, RBTree[*%w(a A)] == MultiRBTree[*%w(a A)])
+ assert_equal(true, MultiRBTree[*%w(a A)] == RBTree[*%w(a A)])
+ end
+
+ def test_replace
+ assert_equal(RBTree[*%w(a A)],
+ MultiRBTree[*%w(a A)].replace(RBTree[*%w(a A)]))
+ assert_raise(TypeError) {
+ RBTree[*%w(a A)].replace(MultiRBTree[*%w(a A)])
+ }
+ end
+
+ def test_update
+ assert_equal(MultiRBTree[*%w(a A b B)],
+ MultiRBTree[*%w(a A)].update(RBTree[*%w(b B)]))
+ assert_raise(TypeError) {
+ RBTree[*%w(a A)].update(MultiRBTree[*%w(b B)])
+ }
+ end
+
+ def test_clone
+ assert_equal(@rbtree, @rbtree.clone)
+ end
+
+ def test_each
+ ret = []
+ @rbtree.each {|k, v|
+ ret << k << v
+ }
+ assert_equal(%w(a A b B b C b D c C), ret)
+ end
+
+ def test_delete
+ @rbtree.delete("b")
+ assert_equal(4, @rbtree.size)
+ assert_equal(%w(a A b C b D c C), @rbtree.to_a.flatten)
+
+ @rbtree.delete("b")
+ assert_equal(3, @rbtree.size)
+ assert_equal(%w(a A b D c C), @rbtree.to_a.flatten)
+
+ @rbtree.delete("b")
+ assert_equal(2, @rbtree.size)
+ assert_equal(%w(a A c C), @rbtree.to_a.flatten)
+ end
+
+ def test_delete_if
+ @rbtree.delete_if {|k, v| k == "b" }
+ assert_equal(%w(a A c C), @rbtree.to_a.flatten)
+ end
+
+ def test_inspect
+ assert_equal(%(#"A", "b"=>"B", "b"=>"C", "b"=>"D", "c"=>"C"}, default=nil, cmp_proc=nil>),
+ @rbtree.inspect)
+ end
+
+ def test_readjust
+ @rbtree.readjust {|a, b| b <=> a }
+ assert_equal(%w(c C b B b C b D a A), @rbtree.to_a.flatten)
+ end
+
+ def test_marshal
+ assert_equal(@rbtree, Marshal.load(Marshal.dump(@rbtree)))
+ end
+
+ def test_lower_bound
+ assert_equal(%w(b B), @rbtree.lower_bound("b"))
+ end
+
+ def test_upper_bound
+ assert_equal(%w(b D), @rbtree.upper_bound("b"))
+ end
+
+ def test_bound
+ assert_equal(%w(b B b C b D), @rbtree.bound("b").flatten)
+ end
+
+ def test_first
+ assert_equal(%w(a A), @rbtree.first)
+ end
+
+ def test_last
+ assert_equal(%w(c C), @rbtree.last)
+ end
+
+ def test_shift
+ assert_equal(%w(a A), @rbtree.shift)
+ assert_equal(4, @rbtree.size)
+ assert_equal(nil, @rbtree["a"])
+ end
+
+ def test_pop
+ assert_equal(%w(c C), @rbtree.pop)
+ assert_equal(4, @rbtree.size)
+ assert_equal(nil, @rbtree["c"])
+ end
+
+ def test_has_key
+ assert_equal(true, @rbtree.has_key?("b"))
+ end
+
+ def test_has_value
+ assert_equal(true, @rbtree.has_value?("B"))
+ assert_equal(true, @rbtree.has_value?("C"))
+ assert_equal(true, @rbtree.has_value?("D"))
+ end
+
+ def test_select
+ assert_equal(%w(b B b C b D), @rbtree.select {|k, v| k == "b"}.flatten)
+ assert_equal(%w(b C c C), @rbtree.select {|k, v| v == "C"}.flatten)
+ end
+
+ def test_values_at
+ assert_equal(%w(A B), @rbtree.values_at("a", "b"))
+ end
+
+ def test_invert
+ assert_equal(MultiRBTree[*%w(A a B b C b C c D b)], @rbtree.invert)
+ end
+
+ def test_keys
+ assert_equal(%w(a b b b c), @rbtree.keys)
+ end
+
+ def test_values
+ assert_equal(%w(A B C D C), @rbtree.values)
+ end
+
+ def test_index
+ assert_equal("b", @rbtree.index("B"))
+ assert_equal("b", @rbtree.index("C"))
+ assert_equal("b", @rbtree.index("D"))
+ end
+end
From nobody at rubyforge.org Fri Mar 9 05:54:02 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Fri, 9 Mar 2007 05:54:02 -0500 (EST)
Subject: [Archipelago-submits] [237] trunk/rbtree-0.2.0/rbtree.c: ehm
actually added the patch
Message-ID: <20070309105402.5E6B15240A8C@rubyforge.org>
Revision: 237
Author: zond
Date: 2007-03-09 05:54:02 -0500 (Fri, 09 Mar 2007)
Log Message:
-----------
ehm actually added the patch
Modified Paths:
--------------
trunk/rbtree-0.2.0/rbtree.c
Modified: trunk/rbtree-0.2.0/rbtree.c
===================================================================
--- trunk/rbtree-0.2.0/rbtree.c 2007-03-09 10:28:46 UTC (rev 236)
+++ trunk/rbtree-0.2.0/rbtree.c 2007-03-09 10:54:02 UTC (rev 237)
@@ -477,6 +477,7 @@
each_callback_func func;
void* arg;
int reverse;
+ void* start;
} rbtree_each_arg_t;
static VALUE
@@ -492,15 +493,21 @@
VALUE self = arg->self;
dict_t* dict = DICT(self);
dnode_t* node;
- dnode_t* first_node;
+ dnode_t* first_node = NULL;
dnode_t* (*next_func)(dict_t*, dnode_t*);
if (arg->reverse) {
- first_node = dict_last(dict);
- next_func = dict_prev;
+ if (arg->start != NULL)
+ first_node = dict_upper_bound(dict, TO_KEY(arg->start));
+ if (first_node == NULL)
+ first_node = dict_last(dict);
+ next_func = dict_prev;
} else {
- first_node = dict_first(dict);
- next_func = dict_next;
+ if (arg->start != NULL)
+ first_node = dict_lower_bound(dict, TO_KEY(arg->start));
+ if (first_node == NULL)
+ first_node = dict_first(dict);
+ next_func = dict_next;
}
ITER_LEV(self)++;
@@ -515,25 +522,27 @@
}
static VALUE
-rbtree_for_each(VALUE self, each_callback_func func, void* arg)
+rbtree_for_each(VALUE self, each_callback_func func, void* arg, void* start)
{
rbtree_each_arg_t each_arg;
each_arg.self = self;
each_arg.func = func;
each_arg.arg = arg;
each_arg.reverse = 0;
+ each_arg.start = start;
return rb_ensure(rbtree_each_body, (VALUE)&each_arg,
rbtree_each_ensure, self);
}
static VALUE
-rbtree_reverse_for_each(VALUE self, each_callback_func func, void* arg)
+rbtree_reverse_for_each(VALUE self, each_callback_func func, void* arg, void *start)
{
rbtree_each_arg_t each_arg;
each_arg.self = self;
each_arg.func = func;
each_arg.arg = arg;
each_arg.reverse = 1;
+ each_arg.start = start;
return rb_ensure(rbtree_each_body, (VALUE)&each_arg,
rbtree_each_ensure, self);
}
@@ -549,15 +558,21 @@
/*
* call-seq:
- * rbtree.each {|key, value| block} => rbtree
+ * rbtree.each(start = nil) {|key, value| block} => rbtree
*
* Calls block once for each key in order, passing the key and value
- * as a two-element array parameters.
+ * as a two-element array parameters. Starts at +start+ if given.
*/
VALUE
-rbtree_each(VALUE self)
+rbtree_each(int argc, VALUE *argv, VALUE self)
{
- return rbtree_for_each(self, each_i, NULL);
+ if (argc > 1)
+ rbtree_argc_error();
+
+ if (argc == 0)
+ return rbtree_for_each(self, each_i, NULL, NULL);
+ else if (argc == 1)
+ return rbtree_for_each(self, each_i, NULL, argv[0]);
}
static each_return_t
@@ -577,7 +592,7 @@
VALUE
rbtree_each_pair(VALUE self)
{
- return rbtree_for_each(self, each_pair_i, NULL);
+ return rbtree_for_each(self, each_pair_i, NULL, NULL);
}
static each_return_t
@@ -597,7 +612,7 @@
VALUE
rbtree_each_key(VALUE self)
{
- return rbtree_for_each(self, each_key_i, NULL);
+ return rbtree_for_each(self, each_key_i, NULL, NULL);
}
static each_return_t
@@ -617,20 +632,26 @@
VALUE
rbtree_each_value(VALUE self)
{
- return rbtree_for_each(self, each_value_i, NULL);
+ return rbtree_for_each(self, each_value_i, NULL, NULL);
}
/*
* call-seq:
- * rbtree.reverse_each {|key, value| block} => rbtree
+ * rbtree.reverse_each(start = nil) {|key, value| block} => rbtree
*
* Calls block once for each key in reverse order, passing the key and
- * value as parameters.
+ * value as parameters. Starts at +start+ if given.
*/
VALUE
-rbtree_reverse_each(VALUE self)
+rbtree_reverse_each(int argc, VALUE *argv, VALUE self)
{
- return rbtree_reverse_for_each(self, each_pair_i, NULL);
+ if (argc > 1)
+ rbtree_argc_error();
+
+ if (argc == 0)
+ return rbtree_reverse_for_each(self, each_i, NULL, NULL);
+ else if (argc == 1)
+ return rbtree_reverse_for_each(self, each_i, NULL, argv[0]);
}
static each_return_t
@@ -646,7 +667,7 @@
VALUE temp = rbtree_alloc(CLASS_OF(dest));
COMPARE(temp) = cmp;
CONTEXT(temp) = context;
- rbtree_for_each(src, aset_i, (void*)temp);
+ rbtree_for_each(src, aset_i, (void*)temp, NULL);
{
dict_t* t = DICT(temp);
DICT(temp) = DICT(dest);
@@ -722,7 +743,7 @@
rbtree_select(VALUE self)
{
VALUE ary = rb_ary_new();
- rbtree_for_each(self, select_i, (void*)ary);
+ rbtree_for_each(self, select_i, (void*)ary, NULL);
return ary;
}
@@ -746,7 +767,7 @@
VALUE arg[2];
arg[0] = Qnil;
arg[1] = value;
- rbtree_for_each(self, index_i, (void*)&arg);
+ rbtree_for_each(self, index_i, (void*)&arg, NULL);
return arg[0];
}
@@ -939,7 +960,7 @@
rbtree_invert(VALUE self)
{
VALUE rbtree = rbtree_alloc(CLASS_OF(self));
- rbtree_for_each(self, invert_i, (void*)rbtree);
+ rbtree_for_each(self, invert_i, (void*)rbtree, NULL);
return rbtree;
}
@@ -973,9 +994,9 @@
}
if (rb_block_given_p())
- rbtree_for_each(other, update_block_i, (void*)self);
+ rbtree_for_each(other, update_block_i, (void*)self, NULL);
else
- rbtree_for_each(other, aset_i, (void*)self);
+ rbtree_for_each(other, aset_i, (void*)self, NULL);
return self;
}
@@ -1017,7 +1038,7 @@
VALUE arg[2];
arg[0] = Qfalse;
arg[1] = value;
- rbtree_for_each(self, has_value_i, (void*)&arg);
+ rbtree_for_each(self, has_value_i, (void*)&arg, NULL);
return arg[0];
}
@@ -1035,7 +1056,7 @@
rbtree_keys(VALUE self)
{
VALUE ary = rb_ary_new();
- rbtree_for_each(self, keys_i, (void*)ary);
+ rbtree_for_each(self, keys_i, (void*)ary, NULL);
return ary;
}
@@ -1053,7 +1074,7 @@
rbtree_values(VALUE self)
{
VALUE ret = rb_ary_new();
- rbtree_for_each(self, values_i, (void*)ret);
+ rbtree_for_each(self, values_i, (void*)ret, NULL);
return ret;
}
@@ -1071,7 +1092,7 @@
rbtree_to_a(VALUE self)
{
VALUE ary = rb_ary_new();
- rbtree_for_each(self, to_a_i, (void*)ary);
+ rbtree_for_each(self, to_a_i, (void*)ary, NULL);
OBJ_INFECT(ary, self);
return ary;
}
@@ -1090,7 +1111,7 @@
rbtree_to_hash(VALUE self)
{
VALUE hash = rb_hash_new();
- rbtree_for_each(self, to_hash_i, (void*)hash);
+ rbtree_for_each(self, to_hash_i, (void*)hash, NULL);
RHASH(hash)->ifnone = IFNONE(self);
if (FL_TEST(self, RBTREE_PROC_DEFAULT))
FL_SET(hash, HASH_PROC_DEFAULT);
@@ -1165,7 +1186,7 @@
rb_str_cat2(ret, "{");
RSTRING(ret)->ptr[0] = '-';
- rbtree_for_each(self, inspect_i, (void*)ret);
+ rbtree_for_each(self, inspect_i, (void*)ret, NULL);
RSTRING(ret)->ptr[0] = '#';
rb_str_cat2(ret, "}");
@@ -1522,7 +1543,7 @@
rb_raise(rb_eTypeError, "cannot dump rbtree with compare proc");
ary = rb_ary_new2(dict_count(DICT(self)) * 2 + 1);
- rbtree_for_each(self, to_flatten_ary_i, (void*)ary);
+ rbtree_for_each(self, to_flatten_ary_i, (void*)ary, NULL);
rb_ary_push(ary, IFNONE(self));
ret = rb_marshal_dump(ary, limit);
@@ -1634,11 +1655,11 @@
rb_define_method(MultiRBTree, "size", rbtree_size, 0);
rb_define_method(MultiRBTree, "length", rbtree_size, 0);
- rb_define_method(MultiRBTree, "each", rbtree_each, 0);
+ rb_define_method(MultiRBTree, "each", rbtree_each, -1);
rb_define_method(MultiRBTree, "each_value", rbtree_each_value, 0);
rb_define_method(MultiRBTree, "each_key", rbtree_each_key, 0);
rb_define_method(MultiRBTree, "each_pair", rbtree_each_pair, 0);
- rb_define_method(MultiRBTree, "reverse_each", rbtree_reverse_each, 0);
+ rb_define_method(MultiRBTree, "reverse_each", rbtree_reverse_each, -1);
rb_define_method(MultiRBTree, "keys", rbtree_keys, 0);
rb_define_method(MultiRBTree, "values", rbtree_values, 0);
From nobody at rubyforge.org Fri Mar 9 09:34:50 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Fri, 9 Mar 2007 09:34:50 -0500 (EST)
Subject: [Archipelago-submits] [238] trunk/archipelago: made client take
more responsibility from sanitation and pirate.
Message-ID: <20070309143450.7A9155240ACD@rubyforge.org>
Revision: 238
Author: zond
Date: 2007-03-09 09:34:50 -0500 (Fri, 09 Mar 2007)
Log Message:
-----------
made client take more responsibility from sanitation and pirate. started using patched rbtrees instead of hashes to simplify and speed up service finding using client subclasses. updated README a tiny bit. updated tests.
Modified Paths:
--------------
trunk/archipelago/README
trunk/archipelago/lib/archipelago/client.rb
trunk/archipelago/lib/archipelago/dump.rb
trunk/archipelago/lib/archipelago/hashish.rb
trunk/archipelago/lib/archipelago/pirate.rb
trunk/archipelago/lib/archipelago/sanitation.rb
trunk/archipelago/tests/sanitation_benchmark.rb
trunk/archipelago/tests/sanitation_test.rb
Modified: trunk/archipelago/README
===================================================================
--- trunk/archipelago/README 2007-03-09 10:54:02 UTC (rev 237)
+++ trunk/archipelago/README 2007-03-09 14:34:50 UTC (rev 238)
@@ -4,7 +4,7 @@
== Dependencies:
Archipelago::Hashish::BerkeleyHashishProvider:: ruby bdb: http://moulon.inra.fr/ruby/bdb.html
-String:: ruby inline: http://www.zenspider.com/ZSS/Products/RubyInline/
+Archipelago::Client::Base:: rbtree: in this same repository is a patched version, original code is at http://www.geocities.co.jp/SiliconValley-PaloAlto/3388/rbtree/README.html
== Sub packages:
Archipelago::Disco:: A UDP multicast discovery service useful to find services in your network with a minimum of configuration.
Modified: trunk/archipelago/lib/archipelago/client.rb
===================================================================
--- trunk/archipelago/lib/archipelago/client.rb 2007-03-09 10:54:02 UTC (rev 237)
+++ trunk/archipelago/lib/archipelago/client.rb 2007-03-09 14:34:50 UTC (rev 238)
@@ -16,6 +16,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'archipelago/disco'
+require 'rbtree'
module Archipelago
@@ -26,28 +27,43 @@
class Base
include Archipelago::Disco::Camel
- attr_reader :jockey
+ attr_reader :jockey, :services, :service_descriptions
+
#
- # Initialize an instance using Archipelago::Disco::MC or :jockey if given,
+ # Initialize a Client using Archipelago::Disco::MC or :jockey if given,
# or a new Archipelago::Disco::Jockey if none, that looks for new services
# :initial_service_update_interval or INITIAL_SERVICE_UPDATE_INTERVAL,
# when it starts and never slower than every :maximum_service_update_interval
# or MAXIMUM_SERVICE_UPDATE_INTERVAL.
#
- def initialize(options = {})
- setup(options)
- end
- #
- # Sets up this instance with the given +options+.
- #
- def setup(options = {})
+ def setup_client(options = {})
setup_jockey(options)
@initial_service_update_interval = options[:initial_service_update_interval] || INITIAL_SERVICE_UPDATE_INTERVAL
@maximum_service_update_interval = options[:maximum_service_update_interval] || MAXIMUM_SERVICE_UPDATE_INTERVAL
+ @service_descriptions = options[:service_descriptions]
+ @services = {}
+ @service_descriptions.each do |name, description|
+ t = RBTree.new
+ t.extend(Archipelago::Current::ThreadedCollection)
+ @services[name] = t
+ end
+
+ start_service_updater
end
#
+ # Finding our services dynamically.
+ #
+ def method_missing(meth, *args)
+ if @services.include?(meth)
+ return @services[meth]
+ else
+ super
+ end
+ end
+
+ #
# Stops the service update thread for this Pirate and also unpublishes and/or
# stops the Jockey.
#
@@ -59,20 +75,84 @@
end
#
- # Override this to do whatever you want to when finding services.
+ # Override this if you want to do something special before or
+ # after calling update_services!
#
+ def around_update_services(&block)
+ yield
+ end
+
+ #
+ # Make our @jockey lookup all our services.
+ #
def update_services!
- # /moo
+ around_update_services do
+ @service_descriptions.each do |name, description|
+ t = RBTree.new
+ t.extend(Archipelago::Current::ThreadedCollection)
+ @services[name] = @jockey.lookup(Archipelago::Disco::Query.new(description), 0).values.inject(t) do |sum, desc|
+ sum[desc[:service_id]] = desc
+ sum
+ end
+ end
+ end
end
private
#
+ # Gets the +n+ smallest keys from services denoted +service_name+ that
+ # are greater than +o+.
+ #
+ # Will loop to the beginning if the number of elements run out.
+ #
+ def get_least_greater_than(service_name, o, n)
+ rval = []
+ self.send(service_name).each(o) do |id, desc|
+ rval << desc if id > o
+ break if rval.size == n
+ end
+ return rval if rval.size == n
+ fill(self.send(service_name), rval, :each, n)
+ end
+
+ #
+ # Gets the +n+ values for the largest keys from +hash+ that
+ # are less than +o+.
+ #
+ # Will loop to the end if the number of elements run out.
+ #
+ def get_greatest_less_than(service_name, o, n)
+ rval = []
+ self.send(service_name).reverse_each(o) do |id, desc|
+ rval << desc if id < o
+ break if rval.size == n
+ end
+ return rval if rval.size == n
+ fill(self.send(service_name), rval, :reverse_each, n)
+ end
+
+ #
+ # Will fill +receiver+ up with the return values of +collection+.send(+meth+)
+ # until +receiver+ is of size +n+.
+ #
+ def fill(collection, receiver, meth, n)
+ while receiver.size < n
+ collection.send(meth) do |id, desc|
+ receiver << desc
+ break if receiver.size == n
+ end
+ end
+ return receiver
+ end
+
+ #
# Start a thread looking up existing chests between every
# +initial+ and +maximum+ seconds.
#
def start_service_updater
update_services!
+ @service_update_thread.kill if defined?(@service_update_thread)
@service_update_thread = Thread.start do
standoff = @initial_service_update_interval
loop do
Modified: trunk/archipelago/lib/archipelago/dump.rb
===================================================================
--- trunk/archipelago/lib/archipelago/dump.rb 2007-03-09 10:54:02 UTC (rev 237)
+++ trunk/archipelago/lib/archipelago/dump.rb 2007-03-09 14:34:50 UTC (rev 238)
@@ -133,7 +133,7 @@
#
def around_publish(&publish_block)
yield
- @jockey.subscribe(:lost, @officer.site_description, service_id) do |record|
+ @jockey.subscribe(:lost, @officer.service_descriptions[:sites], service_id) do |record|
lost_peer(record)
end
start_edge_check
@@ -144,7 +144,7 @@
# changes and kill our @edge_check_thread.
#
def around_stop(&block)
- @jockey.unsubscribe(:lost, @officer.site_description, service_id)
+ @jockey.unsubscribe(:lost, @officer.service_descriptions[:sites], service_id)
@edge_check_thread.kill
yield
end
Modified: trunk/archipelago/lib/archipelago/hashish.rb
===================================================================
--- trunk/archipelago/lib/archipelago/hashish.rb 2007-03-09 10:54:02 UTC (rev 237)
+++ trunk/archipelago/lib/archipelago/hashish.rb 2007-03-09 14:34:50 UTC (rev 238)
@@ -289,7 +289,7 @@
def close!
end
def officer_key(serialized_key, space)
- Digest::SHA1.hexdigest("DumpHashish:#{space}:#{serialized_key}")
+ Digest::SHA1.hexdigest("Archipelago::Hashish::DumpHashish:#{space}:#{serialized_key}")
end
def db_include?(key)
return !@officer[officer_key(Marshal.dump(key), "content")].nil?
Modified: trunk/archipelago/lib/archipelago/pirate.rb
===================================================================
--- trunk/archipelago/lib/archipelago/pirate.rb 2007-03-09 10:54:02 UTC (rev 237)
+++ trunk/archipelago/lib/archipelago/pirate.rb 2007-03-09 14:34:50 UTC (rev 238)
@@ -73,7 +73,6 @@
# Archipelago::Treasure:Dubloons in them.
#
class Captain < Archipelago::Client::Base
- attr_reader :chests, :trannies
#
# Will look for Archipelago::Treasure::Chests matching :chest_description or CHEST_DESCRIPTION and
# Archipelago::Tranny::Managers matching :tranny_description or TRANNY_DESCRIPTION.
@@ -82,29 +81,28 @@
# of required classes and modules at the chest.
#
def initialize(options = {})
- super(options)
-
@transaction = nil
-
- start_service_updater
-
+ setup(options)
end
#
# Sets up this instance with the given +options+.
#
def setup(options = {})
- super(options)
-
- @chest_description = CHEST_DESCRIPTION.merge(options[:chest_description] || {})
- @tranny_description = TRANNY_DESCRIPTION.merge(options[:tranny_description] || {})
-
@chest_eval_files ||= []
@chest_eval_files += options[:chest_eval_files] || []
-
@chests_having_evaluated ||= {}
@yar_counter = 0
+
+ options.merge!({
+ :service_descriptions => {
+ :chests => CHEST_DESCRIPTION.merge(options[:chest_description] || {}),
+ :trannies => TRANNY_DESCRIPTION.merge(options[:tranny_description] || {})
+ }
+ })
+
+ setup_client(options)
end
#
@@ -150,11 +148,11 @@
# Return a clone of this instance bound to a newly created transaction.
#
def begin
- raise NoTransactionManagerAvailableException.new(self) if @trannies.empty?
+ raise NoTransactionManagerAvailableException.new(self) if self.trannies.empty?
rval = self.clone
rval.instance_eval do
- @transaction = @trannies.values.first[:service].begin
+ @transaction = self.trannies.values.first[:service].begin
end
return rval
@@ -176,7 +174,7 @@
# Will abort! the transaction if any exception is raised.
#
def transaction(&block) #:yields: a clone of this Archipelago::Pirate::Captain with the given transaction as default @transaction.
- raise NoTransactionManagerAvailableException.new(self) if @trannies.empty?
+ raise NoTransactionManagerAvailableException.new(self) if self.trannies.empty?
my_clone = self.begin
transa = my_clone.active_transaction
@@ -242,7 +240,7 @@
# for management or rescue!
#
def each(callable)
- @chests.t_each do |service_id, chest|
+ self.chests.t_each do |service_id, chest|
chest[:service].each(callable)
end
end
@@ -250,24 +248,18 @@
#
# Does an immediate update of our service lists.
#
- def update_services!
- @chests = @jockey.lookup(Archipelago::Disco::Query.new(@chest_description), 0)
+ def around_update_services(&block)
+ yield
evaluate_in_chests
- @trannies = @jockey.lookup(Archipelago::Disco::Query.new(@tranny_description), 0)
end
#
# Get the chest responsible for +key+.
#
def responsible_chest(key)
- raise NoRemoteDatabaseAvailableException.new(self) if @chests.empty?
+ raise NoRemoteDatabaseAvailableException.new(self) if self.chests.empty?
- key_id = Digest::SHA1.hexdigest(Marshal.dump(key))
- sorted_chest_ids = @chests.keys.sort
- sorted_chest_ids.each do |id|
- return @chests[id] if id > key_id
- end
- return @chests[sorted_chest_ids.first]
+ return get_least_greater_than(:chests, Digest::SHA1.hexdigest(Marshal.dump(key)), 1).first
end
private
@@ -280,7 +272,7 @@
#
# For all chests
#
- @chests.values.each do |chest|
+ self.chests.values.each do |chest|
#
# Ensure that this chest has a Set of evaluated files
#
Modified: trunk/archipelago/lib/archipelago/sanitation.rb
===================================================================
--- trunk/archipelago/lib/archipelago/sanitation.rb 2007-03-09 10:54:02 UTC (rev 237)
+++ trunk/archipelago/lib/archipelago/sanitation.rb 2007-03-09 14:34:50 UTC (rev 238)
@@ -81,20 +81,22 @@
# work.
#
class Officer < Archipelago::Client::Base
- attr_reader :sites, :site_description
def initialize(options = {})
- super(options)
-
- start_service_updater
+ setup(options)
end
def setup(options = {})
- super(options)
@minimum_recoverable_size = options[:minimum_recoverable_size] || MINIMUM_RECOVERABLE_SIZE
@minimum_nr_of_chunks = options[:minimum_nr_of_chunks] || MINIMUM_NR_OF_CHUNKS
@minimum_redundancy_ratio = options[:minimum_redundancy_ratio] || MINIMUM_REDUNDANCY_RATIO
@metadata_overhead = options[:metadata_overhead] || METADATA_OVERHEAD
- @site_description = Archipelago::Disco::Query.new(SITE_DESCRIPTION.merge(options[:site_description] || {}))
+
+ options.merge!({
+ :service_descriptions => {
+ :sites => Archipelago::Disco::Query.new(SITE_DESCRIPTION.merge(options[:site_description] || {}))
+ }
+ })
+ setup_client(options)
end
#
@@ -113,18 +115,18 @@
dump_hash = responsible_sites(key)
super_string.encode(8)
dump_hash.t_each do |dump_id, nr_of_chunks_needed|
- @sites[dump_id][:service].insert!(key,
- (0...nr_of_chunks_needed).collect do |nr_of_chunks_needed|
- super_string.encode(chunk_size)
- end,
- t)
+ self.sites[dump_id][:service].insert!(key,
+ (0...nr_of_chunks_needed).collect do |nr_of_chunks_needed|
+ super_string.encode(chunk_size)
+ end,
+ t)
end
end
def delete!(key)
dump_hash = responsible_sites(key)
dump_hash.t_each do |dump_id, nr_of_chunks_available|
- @sites[dump_id][:service].delete!(key)
+ self.sites[dump_id][:service].delete!(key)
end
end
@@ -138,27 +140,20 @@
end
#
- # Updates our cache of the available sites using our jockey.
- #
- def update_services!
- @sites = @jockey.lookup(site_description, 0)
- end
-
- #
# Returns {service_id => nr_of_chunks_it_should_have}
# where sum(nr_of_chunks_it_should_have) == +n+
- # from @sites having service_id > +key+.
+ # from self.sites having service_id > +key+.
#
# Will loop to the beginning if the number of elements run out.
#
def responsible_sites(key)
- raise NoRemoteDatabaseAvailableException.new(self) if @sites.empty?
+ raise NoRemoteDatabaseAvailableException.new(self) if self.sites.empty?
rval = {}
rval.extend(Archipelago::Current::ThreadedCollection)
- get_least_greater_than(@sites, key, @minimum_nr_of_chunks).each do |id|
- rval[id] ||= 0
- rval[id] += 1
+ get_least_greater_than(:sites, key, @minimum_nr_of_chunks).each do |desc|
+ rval[desc[:service_id]] ||= 0
+ rval[desc[:service_id]] += 1
end
return rval
end
@@ -189,10 +184,10 @@
# order in the site array if both existed.
#
def next_to?(service_id1, service_id2)
- if @sites.include?(service_id1)
- return get_least_greater_than(@sites, service_id1, 1).first <= service_id2
- elsif @sites.include?(service_id2)
- return get_greatest_less_than(@sites, service_id2, 1).first >= service_id1
+ if self.sites.include?(service_id1)
+ return get_least_greater_than(:sites, service_id1, 1).first[:service_id] <= service_id2
+ elsif self.sites.include?(service_id2)
+ return get_greatest_less_than(:sites, service_id2, 1).first[:service_id] >= service_id1
else
return false
end
@@ -203,14 +198,14 @@
# will be stored in the site identified by +service_id+.
#
def second_master_to(service_id)
- return get_greatest_less_than(@sites, service_id, @minimum_nr_of_chunks - 1).first
+ return get_greatest_less_than(:sites, service_id, @minimum_nr_of_chunks - 1).first[:service_id]
end
#
# Gets the predecessor of +service_id+ in the array of services.
#
def predecessor(service_id)
- return get_greatest_less_than(@sites, service_id, 1).first
+ return get_greatest_less_than(:sites, service_id, 1).first[:service_id]
end
private
@@ -227,7 +222,7 @@
lock = Archipelago::Current::Lock.new
dump_hash.t_each do |dump_id, nr_of_chunks_available|
- site = @sites[dump_id][:service]
+ site = self.sites[dump_id][:service]
begin
chunks = site.fetch(key)
lock.mon_synchronize do
@@ -244,7 +239,7 @@
end
end
rescue DRb::DRbConnError => e
- @sites.delete(dump_id)
+ self.sites.delete(dump_id)
end
end
@@ -257,57 +252,6 @@
end
- #
- # Gets the +n+ smallest keys from +hash+ that
- # are greater than +o+.
- #
- # Will loop to the beginning if the number of elements run out.
- #
- def get_least_greater_than(hash, o, n)
- return get_matching(hash.keys.sort, o, n, :>)
- end
-
- #
- # Gets the +n+ largest keys from +hash+ that
- # are less than +o+.
- #
- # Will loop to the end if the number of elements run out.
- #
- def get_greatest_less_than(hash, o, n)
- return get_matching(hash.keys.sort.reverse, o, n, :<)
- end
-
- #
- # Will get the +n+ consecutive elements of +list+ where
- # [the first one].send(+operator+, +o+) returns true.
- #
- # If none matches, will return the +n+ first elements.
- #
- def get_matching(list, o, n, operator)
- list.size.times do |index|
- key = list[index]
- if key.send(operator, o)
- return get_some(list, index, n)
- end
- end
- return get_some(list, 0, n)
- end
-
- #
- # Gets +n+ values of +array+ starting at +index+.
- #
- # Will loop to the beginning if the elements run out.
- #
- def get_some(array, index, n)
- rval = []
- while rval.size < n
- index = 0 if index > array.size - 1
- rval << array[index]
- index += 1
- end
- return rval
- end
-
end
#
Modified: trunk/archipelago/tests/sanitation_benchmark.rb
===================================================================
--- trunk/archipelago/tests/sanitation_benchmark.rb 2007-03-09 10:54:02 UTC (rev 237)
+++ trunk/archipelago/tests/sanitation_benchmark.rb 2007-03-09 14:34:50 UTC (rev 238)
@@ -10,8 +10,7 @@
@c = Archipelago::Sanitation::Officer.new
assert_within(10) do
@c.update_services!
- s = @c.instance_eval do @sites end
- s.keys == [@d.service_id]
+ @c.sites.keys == [@d.service_id]
end
end
Modified: trunk/archipelago/tests/sanitation_test.rb
===================================================================
--- trunk/archipelago/tests/sanitation_test.rb 2007-03-09 10:54:02 UTC (rev 237)
+++ trunk/archipelago/tests/sanitation_test.rb 2007-03-09 14:34:50 UTC (rev 238)
@@ -10,8 +10,7 @@
@c = Archipelago::Sanitation::Officer.new
assert_within(10) do
@c.update_services!
- s = @c.instance_eval do @sites end
- s.keys == [@d.service_id]
+ @c.sites.keys == [@d.service_id]
end
end
@@ -97,6 +96,11 @@
assert(dumps[0].instance_eval do right_after?(dumps[n].service_id) end,
"#{dumps[0].service_id} is supposed to be right_after? #{dumps[n].service_id}, but isnt. services are #{cleaner2.sites.keys.inspect}")
end
+ if n > 0
+ assert_equal(dumps[n - 1].service_id, cleaner2.predecessor(dumps[n].service_id))
+ else
+ assert_equal(dumps[9].service_id, cleaner2.predecessor(dumps[0].service_id))
+ end
end
#
# Check that only fresh data is returned
@@ -115,12 +119,12 @@
#
assert_equal([dumps[9].service_id, dumps[0].service_id, dumps[1].service_id].sort,
cleaner2.instance_eval do
- get_greatest_less_than(@sites, dumps[2].service_id, 3)
- end.sort)
+ get_greatest_less_than(:sites, dumps[2].service_id, 3)
+ end.collect do |d| d[:service_id] end.sort)
assert_equal([dumps[8].service_id, dumps[9].service_id, dumps[0].service_id].sort,
cleaner2.instance_eval do
- get_least_greater_than(@sites, dumps[7].service_id, 3)
- end.sort)
+ get_least_greater_than(:sites, dumps[7].service_id, 3)
+ end.collect do |d| d[:service_id] end.sort)
assert_equal(dumps[3].service_id,
cleaner2.second_master_to(dumps[4].service_id))
assert_equal(dumps[9].service_id,
@@ -164,13 +168,17 @@
chunks_by_id(cleaner2, "a"))
dumps[1].instance_eval do @valid = true end
- assert_within(10) do
- cleaner2.update_services!
- ["0","1","2","3","4","5","6","7","8","9"].sort == cleaner2.sites.keys.sort
+ begin
+ assert_within(10) do
+ cleaner2.update_services!
+ ["0","1","2","3","4","5","6","7","8","9"].sort == cleaner2.sites.keys.sort
+ end
+ rescue Test::Unit::AssertionFailedError => e
+ pp chunks_by_id(cleaner2, "a")
+ raise e
end
assert_equal({"0"=>1, "1"=>1, "2"=>1},
cleaner2.responsible_sites("a"))
-# dumps[3].instance_eval do @debug_callable = Proc.new do |m| puts m end end
dumps[3].instance_eval do @edge_check_thread.wakeup end
begin
assert_within(20) do
From nobody at rubyforge.org Fri Mar 9 11:11:13 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Fri, 9 Mar 2007 11:11:13 -0500 (EST)
Subject: [Archipelago-submits] [239] trunk/archipelago: made sanitation_test
more time-generous.
Message-ID: <20070309161113.410D95240ADF@rubyforge.org>
Revision: 239
Author: zond
Date: 2007-03-09 11:11:12 -0500 (Fri, 09 Mar 2007)
Log Message:
-----------
made sanitation_test more time-generous. made disco_benchmark more correct. added TODO. commented client.rb more. added an initial lookup timeout to client.rb. added a big comment about the strangest thing so far in client.rb.
Modified Paths:
--------------
trunk/archipelago/TODO
trunk/archipelago/lib/archipelago/client.rb
trunk/archipelago/lib/archipelago/current.rb
trunk/archipelago/tests/disco_benchmark.rb
trunk/archipelago/tests/sanitation_test.rb
Modified: trunk/archipelago/TODO
===================================================================
--- trunk/archipelago/TODO 2007-03-09 14:34:50 UTC (rev 238)
+++ trunk/archipelago/TODO 2007-03-09 16:11:12 UTC (rev 239)
@@ -17,3 +17,10 @@
* Create a file server services that handles really big files within transactions
and allows the POSTing and GETing of them via HTTP.
+
+ * Make Chests able to get rid of keys they shouldnt have in order to save memory.
+ * Maybe use RBTree instead of Hash in the CachedHashish to be able to find these
+ keys quickly.
+
+ * Make Chests guard against Dubloons calling home to the wrong chest.
+ * Especially now that any Chest can serve any Dubloon.
\ No newline at end of file
Modified: trunk/archipelago/lib/archipelago/client.rb
===================================================================
--- trunk/archipelago/lib/archipelago/client.rb 2007-03-09 14:34:50 UTC (rev 238)
+++ trunk/archipelago/lib/archipelago/client.rb 2007-03-09 16:11:12 UTC (rev 239)
@@ -22,8 +22,20 @@
module Client
+ #
+ # The initial interval between calling update_services!
+ #
INITIAL_SERVICE_UPDATE_INTERVAL = 1
+ #
+ # The initial interval will be doubled each time update_services! is called,
+ # but will never be greater than the maximum interval.
+ #
MAXIMUM_SERVICE_UPDATE_INTERVAL = 60
+ #
+ # The timeout that will be used in the first lookup to ensure that we actually
+ # have any services.
+ #
+ INITIAL_LOOKUP_TIMEOUT = 5
class Base
include Archipelago::Disco::Camel
@@ -42,6 +54,7 @@
@initial_service_update_interval = options[:initial_service_update_interval] || INITIAL_SERVICE_UPDATE_INTERVAL
@maximum_service_update_interval = options[:maximum_service_update_interval] || MAXIMUM_SERVICE_UPDATE_INTERVAL
@service_descriptions = options[:service_descriptions]
+ @initial_lookup_timeout = options[:initial_lookup_timeout] || INITIAL_LOOKUP_TIMEOUT
@services = {}
@service_descriptions.each do |name, description|
t = RBTree.new
@@ -85,12 +98,25 @@
#
# Make our @jockey lookup all our services.
#
- def update_services!
+ def update_services!(timeout = 0)
around_update_services do
@service_descriptions.each do |name, description|
+ #
+ # This sure sounds inefficient, but hey, listen up:
+ #
+ # * RBTrees are nice and fast when it comes to looking up ordered stuff.
+ # * They are horribly slow in all other ways.
+ # * As an example: It takes (as of this writing) 10x the time to insert 10k elements in an RBTree
+ # as it takes to sort 10k elements in an Array.
+ #
+ # This means that using them in Archipelago::Disco::ServiceLocker will be inefficient as hell, since they
+ # are merging and creating new maps all the time. But in here, I expect us to not renew our service lists
+ # more than on average once every MAXIMUM_SERVICE_UPDATE_INTERVAL, so that MAY make it worthwhile to do
+ # the RBTree song and dance in here. Hopefully.
+ #
t = RBTree.new
t.extend(Archipelago::Current::ThreadedCollection)
- @services[name] = @jockey.lookup(Archipelago::Disco::Query.new(description), 0).values.inject(t) do |sum, desc|
+ @services[name] = @jockey.lookup(Archipelago::Disco::Query.new(description), timeout).values.inject(t) do |sum, desc|
sum[desc[:service_id]] = desc
sum
end
@@ -151,7 +177,7 @@
# +initial+ and +maximum+ seconds.
#
def start_service_updater
- update_services!
+ update_services!(@initial_lookup_timeout)
@service_update_thread.kill if defined?(@service_update_thread)
@service_update_thread = Thread.start do
standoff = @initial_service_update_interval
Modified: trunk/archipelago/lib/archipelago/current.rb
===================================================================
--- trunk/archipelago/lib/archipelago/current.rb 2007-03-09 14:34:50 UTC (rev 238)
+++ trunk/archipelago/lib/archipelago/current.rb 2007-03-09 16:11:12 UTC (rev 239)
@@ -213,7 +213,7 @@
sync_initialize
end
end
-
+
#
# Just a convenience empty class with locking functionality.
#
Modified: trunk/archipelago/tests/disco_benchmark.rb
===================================================================
--- trunk/archipelago/tests/disco_benchmark.rb 2007-03-09 14:34:50 UTC (rev 238)
+++ trunk/archipelago/tests/disco_benchmark.rb 2007-03-09 16:11:12 UTC (rev 239)
@@ -8,8 +8,8 @@
dj2 = TestJockey.new
x = 0
bm("Jockey#publish/lookup", :n => 10) do
- dj1.publish(Archipelago::Disco::Record.new({:service_id => x, :validator => Archipelago::Disco::MockValidator.new}))
- assert(!dj2.lookup(Archipelago::Disco::Query.new({:service_id => x})).empty?)
+ dj1.publish(Archipelago::Disco::Record.new({:service_id => x.to_s, :validator => Archipelago::Disco::MockValidator.new}))
+ assert(!dj2.lookup(Archipelago::Disco::Query.new({:service_id => x.to_s})).empty?)
x += 1
end
dj1.publish(Archipelago::Disco::Record.new({:service_id => "brappa", :validator => Archipelago::Disco::MockValidator.new}))
Modified: trunk/archipelago/tests/sanitation_test.rb
===================================================================
--- trunk/archipelago/tests/sanitation_test.rb 2007-03-09 14:34:50 UTC (rev 238)
+++ trunk/archipelago/tests/sanitation_test.rb 2007-03-09 16:11:12 UTC (rev 239)
@@ -169,7 +169,7 @@
dumps[1].instance_eval do @valid = true end
begin
- assert_within(10) do
+ assert_within(20) do
cleaner2.update_services!
["0","1","2","3","4","5","6","7","8","9"].sort == cleaner2.sites.keys.sort
end
From nobody at rubyforge.org Fri Mar 9 11:31:49 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Fri, 9 Mar 2007 11:31:49 -0500 (EST)
Subject: [Archipelago-submits] [240]
trunk/archipelago/lib/archipelago/disco.rb: made disco
respect timeout more
Message-ID: <20070309163149.96FEE5240ACC@rubyforge.org>
Revision: 240
Author: zond
Date: 2007-03-09 11:31:49 -0500 (Fri, 09 Mar 2007)
Log Message:
-----------
made disco respect timeout more
Modified Paths:
--------------
trunk/archipelago/lib/archipelago/disco.rb
Modified: trunk/archipelago/lib/archipelago/disco.rb
===================================================================
--- trunk/archipelago/lib/archipelago/disco.rb 2007-03-09 16:11:12 UTC (rev 239)
+++ trunk/archipelago/lib/archipelago/disco.rb 2007-03-09 16:31:49 UTC (rev 240)
@@ -591,12 +591,12 @@
@outgoing << [nil, match]
known_services = @remote_services.get_services(match).merge(@local_services.get_services(match))
- return known_services unless known_services.empty?
+ return known_services if timeout == 0 || !known_services.empty?
- @new_service_semaphore.wait(standoff)
+ t = Time.new
+ @new_service_semaphore.wait([standoff, timeout].min)
standoff *= 2
- t = Time.new
while Time.new < t + timeout
known_services = @remote_services.get_services(match).merge(@local_services.get_services(match))
return known_services unless known_services.empty?
From nobody at rubyforge.org Fri Mar 9 13:04:31 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Fri, 9 Mar 2007 13:04:31 -0500 (EST)
Subject: [Archipelago-submits] [241] trunk/archipelago: made error reporting
in sanitation_test even more verbose.
Message-ID: <20070309180431.585C85240AEC@rubyforge.org>
Revision: 241
Author: zond
Date: 2007-03-09 13:04:31 -0500 (Fri, 09 Mar 2007)
Log Message:
-----------
made error reporting in sanitation_test even more verbose. made client.fill more failsafe. made chest able to notify dubloons when they are fucking up by connecting to the wrong chest.
Modified Paths:
--------------
trunk/archipelago/lib/archipelago/client.rb
trunk/archipelago/lib/archipelago/pirate.rb
trunk/archipelago/lib/archipelago/treasure.rb
trunk/archipelago/tests/sanitation_test.rb
Modified: trunk/archipelago/lib/archipelago/client.rb
===================================================================
--- trunk/archipelago/lib/archipelago/client.rb 2007-03-09 16:31:49 UTC (rev 240)
+++ trunk/archipelago/lib/archipelago/client.rb 2007-03-09 18:04:31 UTC (rev 241)
@@ -163,10 +163,12 @@
# until +receiver+ is of size +n+.
#
def fill(collection, receiver, meth, n)
- while receiver.size < n
- collection.send(meth) do |id, desc|
- receiver << desc
- break if receiver.size == n
+ unless collection.empty?
+ while receiver.size < n
+ collection.send(meth) do |id, desc|
+ receiver << desc
+ break if receiver.size == n
+ end
end
end
return receiver
Modified: trunk/archipelago/lib/archipelago/pirate.rb
===================================================================
--- trunk/archipelago/lib/archipelago/pirate.rb 2007-03-09 16:31:49 UTC (rev 240)
+++ trunk/archipelago/lib/archipelago/pirate.rb 2007-03-09 18:04:31 UTC (rev 241)
@@ -262,6 +262,14 @@
return get_least_greater_than(:chests, Digest::SHA1.hexdigest(Marshal.dump(key)), 1).first
end
+ #
+ # Gets the predecessor of +service_id+ in the array of services.
+ #
+ def successor(service_id)
+ return nil if self.chests.empty?
+ return get_least_greater_than(:chests, service_id, 1).first[:service]
+ end
+
private
#
Modified: trunk/archipelago/lib/archipelago/treasure.rb
===================================================================
--- trunk/archipelago/lib/archipelago/treasure.rb 2007-03-09 16:31:49 UTC (rev 240)
+++ trunk/archipelago/lib/archipelago/treasure.rb 2007-03-09 18:04:31 UTC (rev 241)
@@ -23,7 +23,7 @@
require 'set'
require 'archipelago/hashish'
require 'archipelago/tranny'
-require 'archipelago/disco'
+require 'archipelago/pirate'
module Archipelago
@@ -52,6 +52,16 @@
end
#
+ # Raised when a Dubloon tries to connect to this Archipelago::Treasure::Chest while
+ # we know that we are not the proper destination.
+ #
+ class WrongChestException < RuntimeError
+ def initialize(chest_id, predecessor_id, key)
+ super("#{key.inspect} is not between #{predecessor_id} and #{chest_id}, and should not be here")
+ end
+ end
+
+ #
# Raised whenever a method is called on an object that we dont know about.
#
class UnknownObjectException < RuntimeError
@@ -211,6 +221,25 @@
def method_missing(meth, *args, &block)
begin
return @chest.call_instance_method(@key, meth, @transaction, *args, &block)
+ rescue WrongChestException => e
+ #
+ # We just have to find the new chest we belong at now...
+ #
+ new_chest_record = nil
+ if defined?(Archipelago::Pirate::BLACKBEARD)
+ Archipelago::Pirate::BLACKBEARD.update_services!
+ new_chest_record = Archipelago::Pirate::BLACKBEARD.responsible_chest(@key)
+ new_chest_record = nil unless new_chest_record[:service].include?(@key, @transaction)
+ end
+ raise e unless new_chest_record
+
+ @chest = new_chest_record[:service]
+
+ #
+ # And for all the future dubloons that we unmarshal, lets remember this.
+ #
+ CHEST_BY_SERVICE_ID[new_chest_record[:service_id]] = @chest
+
rescue DRb::DRbConnError => e
#
# Here is some fancy rescuing being done.
@@ -235,12 +264,13 @@
end
raise e unless new_chest_record
+ @chest = new_chest_record[:service]
+
#
# And for all the future dubloons that we unmarshal, lets remember this.
#
- @chest = new_chest_record[:service]
CHEST_BY_SERVICE_ID[new_chest_record[:service_id]] = @chest
-
+
retry
end
end
@@ -316,9 +346,35 @@
initialize_prepared(options[:transaction_recovery_interval] || TRANSACTION_RECOVERY_INTERVAL)
CHEST_BY_SERVICE_ID[self.service_id] = self
+
+ #
+ # The provider of Chest index and other magic.
+ #
+ @captain = options[:captain] || (defined?(Archipelago::Pirate::BLACKBEARD) ? Archipelago::Pirate::BLACKBEARD : Archipelago::Pirate::Captain.new)
+
+ #
+ # Our predecessor. We will NOT accept remote calls for keys before this one.
+ #
+ @predecessor_id = nil
+ @predecessor_validator = nil
end
+ def notify!(predecessor_id, predecessor_validator)
+ @predecessor_id = predecessor_id
+ @predecessor_validator = predecessor_validator
+ end
+
#
+ # We don't want to publish until we have notified our successor of our id.
+ #
+ def around_publish(&block)
+ @captain.update_services!
+ successor = @captain.successor(service_id)
+ successor.notify!(service_id, self) if successor
+ yield
+ end
+
+ #
# The transactions active in this Chest.
#
def active_transactions
@@ -455,6 +511,10 @@
# will be wrapped within the block sent to with_transaction(transaction, &block).
#
def call_instance_method(key, method, transaction, *arguments, &block)
+ if @predecessor_id && !between?(key, @predecessor_id, service_id) && !@predecessor_validator.valid?
+ raise WrongChestException(service_id, @predecessor_id, key)
+ end
+
if transaction
return call_with_transaction(key, method, transaction, *arguments, &block)
else
@@ -601,6 +661,19 @@
private
#
+ # Returns whether key is between id1 and id2 on the big service circle.
+ #
+ def between?(key, id1, id2)
+ if id1 == id2
+ return true
+ elsif id1 < id2
+ return key < id2 && key >= id1
+ elsif id1 > id2
+ return key < id2 || key >= id1
+ end
+ end
+
+ #
# Evaluates all data we have been told to evaluate in earlier runs.
#
def initialize_seen_data
Modified: trunk/archipelago/tests/sanitation_test.rb
===================================================================
--- trunk/archipelago/tests/sanitation_test.rb 2007-03-09 16:31:49 UTC (rev 240)
+++ trunk/archipelago/tests/sanitation_test.rb 2007-03-09 18:04:31 UTC (rev 241)
@@ -7,10 +7,15 @@
DRb.start_service
@d = Archipelago::Dump::Site.new(:persistence_directory => Pathname.new(__FILE__).parent.join("site.db"))
@d.publish!
- @c = Archipelago::Sanitation::Officer.new
- assert_within(10) do
- @c.update_services!
- @c.sites.keys == [@d.service_id]
+ @c = Archipelago::Sanitation::Officer.new(:initial_lookup_timeout => 0)
+ begin
+ assert_within(10) do
+ @c.update_services!
+ @c.sites.keys == [@d.service_id]
+ end
+ rescue Test::Unit::AssertionFailedError => e
+ puts "#{@c.sites.keys.inspect} != #{[@d.service_id].inspect}"
+ raise e
end
end
From nobody at rubyforge.org Fri Mar 9 17:29:04 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Fri, 9 Mar 2007 17:29:04 -0500 (EST)
Subject: [Archipelago-submits] [242] trunk/archipelago: cleaned up and
(hopefully) optimized the tests a bit.
Message-ID: <20070309222904.3C9135240AEF@rubyforge.org>
Revision: 242
Author: zond
Date: 2007-03-09 17:29:03 -0500 (Fri, 09 Mar 2007)
Log Message:
-----------
cleaned up and (hopefully) optimized the tests a bit. made disco a slight bit more threadsafe (by fixing a probable bug).
Modified Paths:
--------------
trunk/archipelago/lib/archipelago/disco.rb
trunk/archipelago/tests/disco_test.rb
trunk/archipelago/tests/dump_test.rb
trunk/archipelago/tests/pirate_test.rb
trunk/archipelago/tests/sanitation_benchmark.rb
trunk/archipelago/tests/sanitation_test.rb
trunk/archipelago/tests/test_helper.rb
trunk/archipelago/tests/tranny_test.rb
trunk/archipelago/tests/treasure_benchmark.rb
trunk/archipelago/tests/treasure_test.rb
Modified: trunk/archipelago/lib/archipelago/disco.rb
===================================================================
--- trunk/archipelago/lib/archipelago/disco.rb 2007-03-09 18:04:31 UTC (rev 241)
+++ trunk/archipelago/lib/archipelago/disco.rb 2007-03-09 22:29:03 UTC (rev 242)
@@ -391,7 +391,7 @@
#
def get_services(match)
rval = ServiceLocker.new
- self.clone.each do |service_id, service_data|
+ @hash.clone.each do |service_id, service_data|
if service_data.matches?(match)
if service_data.valid?
rval[service_id] = service_data
@@ -406,7 +406,7 @@
# Remove all non-valid services.
#
def validate!
- self.clone.each do |service_id, service_data|
+ @hash.clone.each do |service_id, service_data|
unless service_data.valid?
self.delete(service_id)
end
@@ -641,9 +641,11 @@
# Will notify all subscribers to +event_type+ looking for +record+.
#
def notify_subscribers(event_type, record)
- @service_change_subscribers_by_event_type[event_type].each do |query_and_identity, proc|
+ @service_change_subscribers_by_event_type[event_type].clone.each do |query_and_identity, proc|
query = query_and_identity.first
- proc.call(record) if record.matches?(query)
+ Thread.new do
+ proc.call(record)
+ end if record.matches?(query)
end
end
Modified: trunk/archipelago/tests/disco_test.rb
===================================================================
--- trunk/archipelago/tests/disco_test.rb 2007-03-09 18:04:31 UTC (rev 241)
+++ trunk/archipelago/tests/disco_test.rb 2007-03-09 22:29:03 UTC (rev 242)
@@ -15,7 +15,6 @@
class DiscoTest < Test::Unit::TestCase
def setup
- DRb.start_service
@d1 = TestJockey.new(:thrifty_publishing => false,
:thrifty_replying => false,
:thrifty_caching => false)
@@ -60,7 +59,6 @@
def teardown
@d1.stop!
@d2.stop!
- DRb.stop_service
@lt.kill
@listener.close
end
Modified: trunk/archipelago/tests/dump_test.rb
===================================================================
--- trunk/archipelago/tests/dump_test.rb 2007-03-09 18:04:31 UTC (rev 241)
+++ trunk/archipelago/tests/dump_test.rb 2007-03-09 22:29:03 UTC (rev 242)
@@ -4,13 +4,14 @@
class DumpTest < Test::Unit::TestCase
def setup
- DRb.start_service
- @d = Archipelago::Dump::Site.new(:persistence_directory => Pathname.new(__FILE__).parent.join("site.db"))
+ @c = Archipelago::Sanitation::Officer.new(:initial_lookup_timeout => 0)
+ @d = Archipelago::Dump::Site.new(:officer => @c,
+ :persistence_directory => Pathname.new(__FILE__).parent.join("site.db"))
end
def teardown
@d.instance_eval do @persistence_provider.unlink! end
- DRb.stop_service
+ @c.stop!
end
def test_insert_fetch
Modified: trunk/archipelago/tests/pirate_test.rb
===================================================================
--- trunk/archipelago/tests/pirate_test.rb 2007-03-09 18:04:31 UTC (rev 241)
+++ trunk/archipelago/tests/pirate_test.rb 2007-03-09 22:29:03 UTC (rev 242)
@@ -4,14 +4,17 @@
class PirateTest < Test::Unit::TestCase
def setup
- DRb.start_service("druby://localhost:#{rand(1000) + 5000}")
@p = Archipelago::Pirate::Captain.new(:chest_description => {:class => "TestChest"},
- :tranny_description => {:class => "TestManager"})
- @c = TestChest.new(:persistence_directory => Pathname.new(__FILE__).parent.join("chest.db"))
+ :tranny_description => {:class => "TestManager"},
+ :initial_lookup_timeout => 0)
+ @c = TestChest.new(:captain => @p,
+ :persistence_directory => Pathname.new(__FILE__).parent.join("chest.db"))
@c.publish!
- @c2 = TestChest.new(:persistence_directory => Pathname.new(__FILE__).parent.join("chest2.db"))
+ @c2 = TestChest.new(:captain => @p,
+ :persistence_directory => Pathname.new(__FILE__).parent.join("chest2.db"))
@c2.publish!
- @tm = TestManager.new(:persistence_directory => Pathname.new(__FILE__).parent.join("tranny.db"))
+ @tm = TestManager.new(:captain => @p,
+ :persistence_directory => Pathname.new(__FILE__).parent.join("tranny.db"))
@tm.publish!
assert_within(10) do
@p.update_services!
@@ -30,7 +33,7 @@
@c2.persistence_provider.unlink!
@tm.stop!
@tm.persistence_provider.unlink!
- DRb.stop_service
+ @p.stop!
end
def test_include
Modified: trunk/archipelago/tests/sanitation_benchmark.rb
===================================================================
--- trunk/archipelago/tests/sanitation_benchmark.rb 2007-03-09 18:04:31 UTC (rev 241)
+++ trunk/archipelago/tests/sanitation_benchmark.rb 2007-03-09 22:29:03 UTC (rev 242)
@@ -4,7 +4,6 @@
class SanitationBenchmark < Test::Unit::TestCase
def setup
- DRb.start_service
@d = Archipelago::Dump::Site.new(:persistence_directory => Pathname.new(__FILE__).parent.join("site.db"))
@d.publish!
@c = Archipelago::Sanitation::Officer.new
@@ -17,7 +16,7 @@
def teardown
@d.stop!
@d.instance_eval do @persistence_provider.unlink! end
- DRb.stop_service
+ @c.stop!
end
def test_set_get
Modified: trunk/archipelago/tests/sanitation_test.rb
===================================================================
--- trunk/archipelago/tests/sanitation_test.rb 2007-03-09 18:04:31 UTC (rev 241)
+++ trunk/archipelago/tests/sanitation_test.rb 2007-03-09 22:29:03 UTC (rev 242)
@@ -4,10 +4,10 @@
class SanitationTest < Test::Unit::TestCase
def setup
- DRb.start_service
- @d = Archipelago::Dump::Site.new(:persistence_directory => Pathname.new(__FILE__).parent.join("site.db"))
+ @c = Archipelago::Sanitation::Officer.new(:initial_lookup_timeout => 0)
+ @d = Archipelago::Dump::Site.new(:officer => @c,
+ :persistence_directory => Pathname.new(__FILE__).parent.join("site.db"))
@d.publish!
- @c = Archipelago::Sanitation::Officer.new(:initial_lookup_timeout => 0)
begin
assert_within(10) do
@c.update_services!
@@ -22,7 +22,7 @@
def teardown
@d.stop!
@d.instance_eval do @persistence_provider.unlink! end
- DRb.stop_service
+ @c.stop!
end
def test_sanitation_backed_chest
@@ -71,7 +71,8 @@
#
@d.stop!
dumps = []
- cleaner2 = Archipelago::Sanitation::Officer.new(:minimum_nr_of_chunks => 3)
+ cleaner2 = Archipelago::Sanitation::Officer.new(:minimum_nr_of_chunks => 3,
+ :initial_lookup_timeout => 0)
begin
10.times do |n|
dumps[n] = Archipelago::Dump::Site.new(:officer => cleaner2,
@@ -165,14 +166,17 @@
}
assert_equal(healthy_chunks,
chunks_by_id(cleaner2, "a"))
- dumps[1].instance_eval do @valid = false end
+ dumps[1].stop!
cleaner2.update_services!
dumps[0].instance_eval do lost_peer({:service_id => dumps[1].service_id}) end
dumps[2].instance_eval do lost_peer({:service_id => dumps[1].service_id}) end
assert_equal({"6"=>0, "7"=>0, "8"=>0, "9"=>0, "0"=>1, "2"=>1, "3"=>1, "4"=>0, "5"=>0},
chunks_by_id(cleaner2, "a"))
- dumps[1].instance_eval do @valid = true end
+ dumps[1] = Archipelago::Dump::Site.new(:officer => cleaner2,
+ :service_description => {:service_id => "1"},
+ :persistence_directory => Pathname.new(__FILE__).parent.join("master_test_1.db"))
+ dumps[1].publish!
begin
assert_within(20) do
cleaner2.update_services!
Modified: trunk/archipelago/tests/test_helper.rb
===================================================================
--- trunk/archipelago/tests/test_helper.rb 2007-03-09 18:04:31 UTC (rev 241)
+++ trunk/archipelago/tests/test_helper.rb 2007-03-09 22:29:03 UTC (rev 242)
@@ -2,12 +2,14 @@
home = File.expand_path(File.dirname(__FILE__))
$: << File.join(home, "..", "lib")
-MC_DISABLED = true unless defined?(MC_ENABLED) && MC_ENABLED
-BLACKBEARD_DISABLED = true unless defined?(BLACKBEARD_ENABLED) && BLACKBEARD_ENABLED
-CLEANER_DISABLED = true unless defined?(CLEANER_ENABLED) && CLEANER_ENABLED
+MC_DISABLED = true
+CLEANER_DISABLED = true
+BLACKBEARD_DISABLED = true
+require 'drb'
+DRb.start_service
+
require 'pp'
-require 'drb'
require 'test/unit'
require 'archipelago'
require 'archipelago/treasure'
Modified: trunk/archipelago/tests/tranny_test.rb
===================================================================
--- trunk/archipelago/tests/tranny_test.rb 2007-03-09 18:04:31 UTC (rev 241)
+++ trunk/archipelago/tests/tranny_test.rb 2007-03-09 22:29:03 UTC (rev 242)
@@ -4,13 +4,11 @@
class TrannyTest < Test::Unit::TestCase
def setup
- DRb.start_service
@tm = TestManager.new(:db_path => Pathname.new(__FILE__).parent.join("tranny.db"))
end
def teardown
@tm.persistence_provider.unlink!
- DRb.stop_service
end
class Participant
Modified: trunk/archipelago/tests/treasure_benchmark.rb
===================================================================
--- trunk/archipelago/tests/treasure_benchmark.rb 2007-03-09 18:04:31 UTC (rev 241)
+++ trunk/archipelago/tests/treasure_benchmark.rb 2007-03-09 22:29:03 UTC (rev 242)
@@ -4,13 +4,16 @@
class TreasureBenchmark < Test::Unit::TestCase
def setup
- DRb.start_service
- @c = TestChest.new(:persistence_directory => Pathname.new(__FILE__).parent.join("chest.db"))
+ @p = Archipelago::Pirate::Captain.new(:chest_description => {:class => "TestChest"},
+ :tranny_description => {:class => "TestManager"},
+ :initial_lookup_timeout => 0)
+ @c = TestChest.new(:captain => @p,
+ :persistence_directory => Pathname.new(__FILE__).parent.join("chest.db"))
end
def teardown
@c.persistence_provider.unlink!
- DRb.stop_service
+ @p.stop!
end
def test_outside_transaction
Modified: trunk/archipelago/tests/treasure_test.rb
===================================================================
--- trunk/archipelago/tests/treasure_test.rb 2007-03-09 18:04:31 UTC (rev 241)
+++ trunk/archipelago/tests/treasure_test.rb 2007-03-09 22:29:03 UTC (rev 242)
@@ -23,10 +23,15 @@
class TreasureTest < Test::Unit::TestCase
def setup
- DRb.start_service
- @c = TestChest.new(:persistence_directory => Pathname.new(__FILE__).parent.join("chest.db"))
- @c2 = TestChest.new(:persistence_directory => Pathname.new(__FILE__).parent.join("chest2.db"))
- @tm = TestManager.new(:persistence_directory => Pathname.new(__FILE__).parent.join("tranny1.db"))
+ @p = Archipelago::Pirate::Captain.new(:chest_description => {:class => "TestChest"},
+ :tranny_description => {:class => "TestManager"},
+ :initial_lookup_timeout => 0)
+ @c = TestChest.new(:captain => @p,
+ :persistence_directory => Pathname.new(__FILE__).parent.join("chest.db"))
+ @c2 = TestChest.new(:captain => @p,
+ :persistence_directory => Pathname.new(__FILE__).parent.join("chest2.db"))
+ @tm = TestManager.new(:captain => @p,
+ :persistence_directory => Pathname.new(__FILE__).parent.join("tranny1.db"))
$BURKMAT = 0
$BURKMAT2 = 0
$BURKMAT3 = 0
@@ -37,10 +42,13 @@
end
def teardown
+ @c.stop!
@c.persistence_provider.unlink!
+ @c2.stop!
@c2.persistence_provider.unlink!
+ @tm.stop!
@tm.persistence_provider.unlink!
- DRb.stop_service
+ @p.stop!
end
def test_with_transaction
From nobody at rubyforge.org Fri Mar 9 21:13:18 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Fri, 9 Mar 2007 21:13:18 -0500 (EST)
Subject: [Archipelago-submits] [243] trunk/archipelago: made disco not
validate EVERY time it fetches, just now and then.
Message-ID: <20070310021318.B14B85240AF5@rubyforge.org>
Revision: 243
Author: zond
Date: 2007-03-09 21:13:17 -0500 (Fri, 09 Mar 2007)
Log Message:
-----------
made disco not validate EVERY time it fetches, just now and then. adjusted tests accordingly. adjusted client. moved around public/private state for a bunch of classes. made validate threaded. commented hashish.
Modified Paths:
--------------
trunk/archipelago/lib/archipelago/client.rb
trunk/archipelago/lib/archipelago/disco.rb
trunk/archipelago/lib/archipelago/dump.rb
trunk/archipelago/lib/archipelago/hashish.rb
trunk/archipelago/lib/archipelago/pirate.rb
trunk/archipelago/tests/disco_test.rb
trunk/archipelago/tests/sanitation_test.rb
Modified: trunk/archipelago/lib/archipelago/client.rb
===================================================================
--- trunk/archipelago/lib/archipelago/client.rb 2007-03-09 22:29:03 UTC (rev 242)
+++ trunk/archipelago/lib/archipelago/client.rb 2007-03-10 02:13:17 UTC (rev 243)
@@ -98,7 +98,9 @@
#
# Make our @jockey lookup all our services.
#
- def update_services!(timeout = 0)
+ def update_services!(options = {})
+ timeout = options[:timeout] || 0
+ validate = options[:validate] || false
around_update_services do
@service_descriptions.each do |name, description|
#
@@ -114,12 +116,9 @@
# more than on average once every MAXIMUM_SERVICE_UPDATE_INTERVAL, so that MAY make it worthwhile to do
# the RBTree song and dance in here. Hopefully.
#
- t = RBTree.new
- t.extend(Archipelago::Current::ThreadedCollection)
- @services[name] = @jockey.lookup(Archipelago::Disco::Query.new(description), timeout).values.inject(t) do |sum, desc|
- sum[desc[:service_id]] = desc
- sum
- end
+ @services[name] = @jockey.lookup(Archipelago::Disco::Query.new(description), timeout)
+ @services[name].convert_to_tree!
+ @services[name].validate! if validate
end
end
end
@@ -179,7 +178,7 @@
# +initial+ and +maximum+ seconds.
#
def start_service_updater
- update_services!(@initial_lookup_timeout)
+ update_services!(:timeout => @initial_lookup_timeout, :validate => true)
@service_update_thread.kill if defined?(@service_update_thread)
@service_update_thread = Thread.start do
standoff = @initial_service_update_interval
Modified: trunk/archipelago/lib/archipelago/disco.rb
===================================================================
--- trunk/archipelago/lib/archipelago/disco.rb 2007-03-09 22:29:03 UTC (rev 242)
+++ trunk/archipelago/lib/archipelago/disco.rb 2007-03-10 02:13:17 UTC (rev 243)
@@ -78,6 +78,7 @@
# can include this for simplicity.
#
module Camel
+ private
#
# Setup our @jockey as a Archipelago::Disco::Jockey with given options.
#
@@ -153,48 +154,6 @@
end
#
- # Will initialize this instance with @service_description and @jockey_options
- # and merge these with the optionally given :service_description and
- # :jockey_options.
- #
- def initialize_publishable(options = {})
- #
- # The provider of happy magic persistent hashes of different kinds.
- #
- @persistence_provider ||= Archipelago::Hashish::BerkeleyHashishProvider.new(options[:persistence_directory] || Pathname.new(File.expand_path(__FILE__)).parent.join(self.class.name + ".db"))
- #
- # Stuff that didnt fit in any of the other databases.
- #
- @metadata ||= @persistence_provider.get_hashish("metadata")
- #
- # Our service_description that is supposed to define and describe
- # us in the discovery network.
- #
- @service_description = {
- :service_id => service_id || Digest::SHA1.hexdigest("#{HOST}:#{Time.new.to_f}:#{self.object_id}:#{rand(1 << 32)}").to_s,
- :validator => self,
- :service => self,
- :class => self.class.name
- }.merge(options[:service_description] || {})
- #
- # Our service_id that is supposed to be unique and persistent.
- #
- @metadata["service_id"] = @service_description[:service_id]
- #
- # Setup our Archipelago::Disco::Jockey.
- #
- @jockey_options = options[:jockey_options] || {}
- end
-
- #
- # Override this if you want to do something magical before or after you
- # get published.
- #
- def around_publish(&block)
- yield
- end
-
- #
# Create an Archipelago::Disco::Jockey for this instance using @jockey_options
# or optionally given :jockey_options.
#
@@ -220,14 +179,6 @@
end
#
- # Override this if you want to do something magical before or after you
- # get stopped.
- #
- def around_stop(&block)
- yield
- end
-
- #
# Stops the publishing of this Publishable.
#
def stop!
@@ -253,6 +204,58 @@
return @service_id ||= @metadata["service_id"]
end
+ private
+
+ #
+ # Will initialize this instance with @service_description and @jockey_options
+ # and merge these with the optionally given :service_description and
+ # :jockey_options.
+ #
+ def initialize_publishable(options = {})
+ #
+ # The provider of happy magic persistent hashes of different kinds.
+ #
+ @persistence_provider ||= Archipelago::Hashish::BerkeleyHashishProvider.new(options[:persistence_directory] || Pathname.new(File.expand_path(__FILE__)).parent.join(self.class.name + ".db"))
+ #
+ # Stuff that didnt fit in any of the other databases.
+ #
+ @metadata ||= @persistence_provider.get_hashish("metadata")
+ #
+ # Our service_description that is supposed to define and describe
+ # us in the discovery network.
+ #
+ @service_description = {
+ :service_id => service_id || Digest::SHA1.hexdigest("#{HOST}:#{Time.new.to_f}:#{self.object_id}:#{rand(1 << 32)}").to_s,
+ :validator => self,
+ :service => self,
+ :class => self.class.name
+ }.merge(options[:service_description] || {})
+ #
+ # Our service_id that is supposed to be unique and persistent.
+ #
+ @metadata["service_id"] = @service_description[:service_id]
+ #
+ # Setup our Archipelago::Disco::Jockey.
+ #
+ @jockey_options = options[:jockey_options] || {}
+ end
+
+ #
+ # Override this if you want to do something magical before or after you
+ # get published.
+ #
+ def around_publish(&block)
+ yield
+ end
+
+ #
+ # Override this if you want to do something magical before or after you
+ # get stopped.
+ #
+ def around_stop(&block)
+ yield
+ end
+
end
#
@@ -281,12 +284,6 @@
@attributes = hash
end
#
- # Returns the hash of our @attributes.
- #
- def hash
- @attributes.hash
- end
- #
# Returns whether our @attributes are equal to that of +o+.
#
def eql?(o)
@@ -351,14 +348,23 @@
class ServiceLocker
extend Forwardable
attr_reader :hash
- include Archipelago::Current::Synchronized
include Archipelago::Current::ThreadedCollection
- def_delegators :@hash, :[], :size, :each, :empty?, :values, :keys, :include?
+ def_delegators :@hash, :[], :size, :empty?, :values, :keys, :include?
def initialize(options = {})
- super
@hash = options[:hash] || {}
+ @hash.extend(Archipelago::Current::ThreadedCollection)
@jockey = options[:jockey]
end
+ def each(*args, &block)
+ clone = @hash.clone
+ clone.extend(Archipelago::Current::ThreadedCollection)
+ clone.each(*args, &block)
+ end
+ def reverse_each(*args, &block)
+ clone = @hash.clone
+ clone.extend(Archipelago::Current::ThreadedCollection)
+ clone.reverse_each(*args, &block)
+ end
#
# Set +key+ to +value+.
#
@@ -379,11 +385,20 @@
@jockey.instance_eval do notify_subscribers(:lost, value) end if @jockey && value
end
#
+ # Will make this ServiceLocker convert its Hash into an RBTree.
+ #
+ def convert_to_tree!
+ t = RBTree.new
+ @hash.each do |k,v|
+ t[k] = v
+ end
+ @hash = t
+ end
+ #
# Merge this locker with another.
#
def merge(sd)
- rval = @hash.clone
- rval.merge!(sd.hash)
+ rval = @hash.merge(sd.hash)
ServiceLocker.new(:hash => rval)
end
#
@@ -391,13 +406,9 @@
#
def get_services(match)
rval = ServiceLocker.new
- @hash.clone.each do |service_id, service_data|
+ self.t_each do |service_id, service_data|
if service_data.matches?(match)
- if service_data.valid?
- rval[service_id] = service_data
- else
- self.delete(service_id)
- end
+ rval[service_id] = service_data
end
end
return rval
@@ -406,11 +417,12 @@
# Remove all non-valid services.
#
def validate!
- @hash.clone.each do |service_id, service_data|
+ self.t_each do |service_id, service_data|
unless service_data.valid?
self.delete(service_id)
end
end
+ return self
end
end
@@ -461,17 +473,6 @@
end
#
- # Start all our threads.
- #
- def start!(options = {})
- start_listener
- start_unilistener
- start_shouter
- start_picker
- start_validator(options[:validation_interval] || @validation_interval)
- end
-
- #
# Will listen for +event_type+s matching the Query +match+
# and do +block+.call with the matching Record.
#
@@ -635,9 +636,28 @@
end
end
+ #
+ # Validate all our known services.
+ #
+ def validate!
+ @local_services.validate!
+ @remote_services.validate!
+ end
+
private
#
+ # Start all our threads.
+ #
+ def start!(options = {})
+ start_listener
+ start_unilistener
+ start_shouter
+ start_picker
+ start_validator(options[:validation_interval] || @validation_interval)
+ end
+
+ #
# Will notify all subscribers to +event_type+ looking for +record+.
#
def notify_subscribers(event_type, record)
@@ -656,8 +676,7 @@
@validator_thread = Thread.new do
loop do
begin
- @local_services.validate!
- @remote_services.validate!
+ validate!
sleep(validation_interval)
rescue Exception => e
puts e
Modified: trunk/archipelago/lib/archipelago/dump.rb
===================================================================
--- trunk/archipelago/lib/archipelago/dump.rb 2007-03-09 22:29:03 UTC (rev 242)
+++ trunk/archipelago/lib/archipelago/dump.rb 2007-03-10 02:13:17 UTC (rev 243)
@@ -195,7 +195,7 @@
break if belongs_here?(key)
keys_to_check << key
end
- @officer.update_services! unless keys_to_check.empty?
+ @officer.update_services!(:validate => true) unless keys_to_check.empty?
keys_to_check.each do |key|
redistribute_and_delete(key)
end
@@ -251,7 +251,6 @@
# it was master to.
#
def lost_peer(record)
- @officer.update_services!
@debug_callable.call("#{service_id}.lost_peer(#{record[:service_id]}) called") if @debug_callable
keys_to_redistribute = Set.new
if right_before?(record[:service_id])
@@ -267,6 +266,7 @@
break if key > record[:service_id]
end
end
+ @officer.update_services!(:validate => true)
keys_to_redistribute.each do |key|
redistribute(key)
end
Modified: trunk/archipelago/lib/archipelago/hashish.rb
===================================================================
--- trunk/archipelago/lib/archipelago/hashish.rb 2007-03-09 22:29:03 UTC (rev 242)
+++ trunk/archipelago/lib/archipelago/hashish.rb 2007-03-10 02:13:17 UTC (rev 243)
@@ -370,6 +370,10 @@
@berkeley_hashishes = []
@bdb_dbs = []
end
+ #
+ # Get a CachedHashish either backed by a Berkeley Database (if +:name+ is given)
+ # or by an Archipelago::Sanitation network (if +:officer+ is given).
+ #
def get_cached_hashish(options)
if options[:officer]
hashish = DumpHashish.new(options[:officer])
@@ -381,6 +385,9 @@
return hashish
end
end
+ #
+ # Get a BDB::Btree allowing duplicates with +name+ and +flags+.
+ #
def get_dup_tree(name, flags = BDB::CREATE | BDB::NOMMAP)
db = BDB::Btree.open(Pathname.new(File.join(@env.home, name)).expand_path,
nil,
@@ -391,11 +398,17 @@
@bdb_dbs << db
return db
end
+ #
+ # Get a BDB::Hash with +name+ and +flags+.
+ #
def get_hashish(name, flags = BDB::CREATE | BDB::NOMMAP)
db = @env.open_db(BDB::HASH, name, nil, flags)
@bdb_dbs << db
return db
end
+ #
+ # Close all our databases.
+ #
def close!
@berkeley_hashishes.each do |h|
h.close!
@@ -404,6 +417,9 @@
d.close
end
end
+ #
+ # Close and remove all our databases.
+ #
def unlink!
close!
home = Pathname.new(@env.home).expand_path
Modified: trunk/archipelago/lib/archipelago/pirate.rb
===================================================================
--- trunk/archipelago/lib/archipelago/pirate.rb 2007-03-09 22:29:03 UTC (rev 242)
+++ trunk/archipelago/lib/archipelago/pirate.rb 2007-03-10 02:13:17 UTC (rev 243)
@@ -246,14 +246,6 @@
end
#
- # Does an immediate update of our service lists.
- #
- def around_update_services(&block)
- yield
- evaluate_in_chests
- end
-
- #
# Get the chest responsible for +key+.
#
def responsible_chest(key)
@@ -273,6 +265,14 @@
private
#
+ # Does an immediate update of our service lists.
+ #
+ def around_update_services(&block)
+ yield
+ evaluate_in_chests
+ end
+
+ #
# Make sure all our known chests have evaluated
# all files we need them to.
#
Modified: trunk/archipelago/tests/disco_test.rb
===================================================================
--- trunk/archipelago/tests/disco_test.rb 2007-03-09 22:29:03 UTC (rev 242)
+++ trunk/archipelago/tests/disco_test.rb 2007-03-10 02:13:17 UTC (rev 243)
@@ -110,8 +110,10 @@
end
@d2.unsubscribe(:lost, Archipelago::Disco::Query.new(:epa => "blarx"), 2)
- assert(@d2.lookup(Archipelago::Disco::Query.new(:epa => "blar"), 0).empty?)
+ assert(@d2.lookup(Archipelago::Disco::Query.new(:epa => "blar"), 0).validate!.empty?)
+ @d2.validate!
+
assert(lost_it)
assert(!lost_wrong)
end
Modified: trunk/archipelago/tests/sanitation_test.rb
===================================================================
--- trunk/archipelago/tests/sanitation_test.rb 2007-03-09 22:29:03 UTC (rev 242)
+++ trunk/archipelago/tests/sanitation_test.rb 2007-03-10 02:13:17 UTC (rev 243)
@@ -39,7 +39,7 @@
assert_equal("KLADD", klibb.upcase)
assert_equal("kladd", klibb)
@d.stop!
- @c.update_services!
+ @c.update_services!(:validate => true)
assert_raise(Archipelago::Sanitation::NoRemoteDatabaseAvailableException) do
klibb.upcase!
end
@@ -167,7 +167,7 @@
assert_equal(healthy_chunks,
chunks_by_id(cleaner2, "a"))
dumps[1].stop!
- cleaner2.update_services!
+ cleaner2.update_services!(:validate => true)
dumps[0].instance_eval do lost_peer({:service_id => dumps[1].service_id}) end
dumps[2].instance_eval do lost_peer({:service_id => dumps[1].service_id}) end
assert_equal({"6"=>0, "7"=>0, "8"=>0, "9"=>0, "0"=>1, "2"=>1, "3"=>1, "4"=>0, "5"=>0},
@@ -189,6 +189,7 @@
assert_equal({"0"=>1, "1"=>1, "2"=>1},
cleaner2.responsible_sites("a"))
dumps[3].instance_eval do @edge_check_thread.wakeup end
+ cleaner2.update_services!(:validate => true)
begin
assert_within(20) do
chunks_by_id(cleaner2, "a") == healthy_chunks
From nobody at rubyforge.org Fri Mar 9 21:16:17 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Fri, 9 Mar 2007 21:16:17 -0500 (EST)
Subject: [Archipelago-submits] [244] trunk/archipelago/TODO: more todo
Message-ID: <20070310021617.4FE1C5240AF8@rubyforge.org>
Revision: 244
Author: zond
Date: 2007-03-09 21:16:16 -0500 (Fri, 09 Mar 2007)
Log Message:
-----------
more todo
Modified Paths:
--------------
trunk/archipelago/TODO
Modified: trunk/archipelago/TODO
===================================================================
--- trunk/archipelago/TODO 2007-03-10 02:13:17 UTC (rev 243)
+++ trunk/archipelago/TODO 2007-03-10 02:16:16 UTC (rev 244)
@@ -22,5 +22,4 @@
* Maybe use RBTree instead of Hash in the CachedHashish to be able to find these
keys quickly.
- * Make Chests guard against Dubloons calling home to the wrong chest.
- * Especially now that any Chest can serve any Dubloon.
\ No newline at end of file
+ * Test the protection of Dubloon calling the wrong chest.
From nobody at rubyforge.org Mon Mar 12 10:53:17 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Mon, 12 Mar 2007 10:53:17 -0400 (EDT)
Subject: [Archipelago-submits] [245] trunk/archipelago: added tests for
WrongChestException.
Message-ID: <20070312145318.813B65240B70@rubyforge.org>
Revision: 245
Author: zond
Date: 2007-03-12 10:53:17 -0400 (Mon, 12 Mar 2007)
Log Message:
-----------
added tests for WrongChestException. added tests for predecessor and successor in pirate_test. split Publishable#stop! into close! and unpublish!. made clients subscribe to changes in their services so that they have the same info others do when subscription notifications arrive. made the rescues in dump more generic.
Modified Paths:
--------------
trunk/archipelago/lib/archipelago/client.rb
trunk/archipelago/lib/archipelago/disco.rb
trunk/archipelago/lib/archipelago/dump.rb
trunk/archipelago/lib/archipelago/pirate.rb
trunk/archipelago/lib/archipelago/treasure.rb
trunk/archipelago/tests/pirate_test.rb
trunk/archipelago/tests/sanitation_benchmark.rb
trunk/archipelago/tests/sanitation_test.rb
trunk/archipelago/tests/test_helper.rb
trunk/archipelago/tests/treasure_test.rb
Modified: trunk/archipelago/lib/archipelago/client.rb
===================================================================
--- trunk/archipelago/lib/archipelago/client.rb 2007-03-10 02:16:16 UTC (rev 244)
+++ trunk/archipelago/lib/archipelago/client.rb 2007-03-12 14:53:17 UTC (rev 245)
@@ -63,6 +63,7 @@
end
start_service_updater
+ start_subscriptions
end
#
@@ -82,6 +83,7 @@
#
def stop!
@service_update_thread.kill if @service_update_thread
+ stop_subscriptions
unless defined?(Archipelago::Disco::MC) && @jockey && @jockey == Archipelago::Disco::MC
@jockey.stop!
end
@@ -126,6 +128,38 @@
private
#
+ # Subscribe to our service changes.
+ #
+ def start_subscriptions
+ @service_descriptions.each do |name, description|
+ @jockey.subscribe(:found,
+ Archipelago::Disco::Query.new(description),
+ object_id) do |record|
+ @services[name][record[:service_id]] = record
+ end
+ @jockey.subscribe(:lost,
+ Archipelago::Disco::Query.new(description),
+ object_id) do |record|
+ @services[name].delete(record[:service_id])
+ end
+ end
+ end
+
+ #
+ # Stop our subscriptions to our service changes.
+ #
+ def stop_subscriptions
+ @service_descriptions.each do |name, description|
+ @jockey.unsubscribe(:found,
+ Archipelago::Disco::Query.new(description),
+ object_id)
+ @jockey.subscribe(:lost,
+ Archipelago::Disco::Query.new(description),
+ object_id)
+ end
+ end
+
+ #
# Gets the +n+ smallest keys from services denoted +service_name+ that
# are greater than +o+.
#
Modified: trunk/archipelago/lib/archipelago/disco.rb
===================================================================
--- trunk/archipelago/lib/archipelago/disco.rb 2007-03-10 02:16:16 UTC (rev 244)
+++ trunk/archipelago/lib/archipelago/disco.rb 2007-03-12 14:53:17 UTC (rev 245)
@@ -161,8 +161,8 @@
# :service_description.
#
def publish!(options = {})
+ setup_jockey(options)
around_publish do
- setup_jockey(options)
@jockey.publish(Archipelago::Disco::Record.new(@service_description.merge(options[:service_description] || {})))
end
end
@@ -181,10 +181,10 @@
#
# Stops the publishing of this Publishable.
#
- def stop!
- around_stop do
- if defined?(@jockey)
- if valid?
+ def unpublish!
+ if defined?(@jockey)
+ if valid?
+ around_unpublish do
@valid = false
if defined?(Archipelago::Disco::MC) && @jockey == Archipelago::Disco::MC
@jockey.unpublish(self.service_id)
@@ -193,6 +193,15 @@
end
end
end
+ end
+ end
+
+ #
+ # Closes the persistence backend of this Publishable.
+ #
+ def close!
+ unpublish!
+ around_close do
@persistence_provider.close!
end
end
@@ -250,12 +259,28 @@
#
# Override this if you want to do something magical before or after you
- # get stopped.
+ # get unpublished.
#
def around_stop(&block)
yield
end
+ #
+ # Override this if you want to do something magical before or after you
+ # get closed.
+ #
+ def around_close(&block)
+ yield
+ end
+
+ #
+ # Override this if you want to do something magical before or after you
+ # get unpublished.
+ #
+ def around_unpublish(&block)
+ yield
+ end
+
end
#
@@ -664,7 +689,13 @@
@service_change_subscribers_by_event_type[event_type].clone.each do |query_and_identity, proc|
query = query_and_identity.first
Thread.new do
- proc.call(record)
+ begin
+ proc.call(record)
+ rescue Exception => e
+ @service_change_subscribers_by_event_type[event_type].delete(query_and_identity)
+ puts e
+ pp e.backtrace
+ end
end if record.matches?(query)
end
end
Modified: trunk/archipelago/lib/archipelago/dump.rb
===================================================================
--- trunk/archipelago/lib/archipelago/dump.rb 2007-03-10 02:16:16 UTC (rev 244)
+++ trunk/archipelago/lib/archipelago/dump.rb 2007-03-12 14:53:17 UTC (rev 245)
@@ -161,7 +161,7 @@
@officer.redistribute(key)
@db.delete(key)
@debug_callable.call("#{service_id}.redistribute_and_delete(#{key}) done redistributing") if @debug_callable
- rescue Archipelago::Sanitation::NotEnoughDataException => e
+ rescue Exception => e
@debug_callable.call("#{service_id}.redistribute_and_delete(#{key}) got #{e}: #{PP.pp(e.backtrace, "")}") if @debug_callable
# What shall we do in this case?
# * Nothing, and wait for the admins to make a manual check?
@@ -229,7 +229,7 @@
def redistribute(key)
begin
@officer.redistribute(key)
- rescue Archipelago::Sanitation::NotEnoughDataException => e
+ rescue Exception => e
@debug_callable.call("#{service_id}.redistribute(#{key}) got #{e}: #{PP.pp(e.backtrace, "")}") if @debug_callable
# What shall we do in this case?
# * Nothing, and wait for the admins to make a manual check?
Modified: trunk/archipelago/lib/archipelago/pirate.rb
===================================================================
--- trunk/archipelago/lib/archipelago/pirate.rb 2007-03-10 02:16:16 UTC (rev 244)
+++ trunk/archipelago/lib/archipelago/pirate.rb 2007-03-12 14:53:17 UTC (rev 245)
@@ -255,11 +255,11 @@
end
#
- # Gets the predecessor of +service_id+ in the array of services.
+ # Gets the successor of +service_id+ in the array of services.
#
def successor(service_id)
return nil if self.chests.empty?
- return get_least_greater_than(:chests, service_id, 1).first[:service]
+ return get_least_greater_than(:chests, service_id, 1).first
end
private
Modified: trunk/archipelago/lib/archipelago/treasure.rb
===================================================================
--- trunk/archipelago/lib/archipelago/treasure.rb 2007-03-10 02:16:16 UTC (rev 244)
+++ trunk/archipelago/lib/archipelago/treasure.rb 2007-03-12 14:53:17 UTC (rev 245)
@@ -199,7 +199,7 @@
end
#
# This Dubloon will always have the same object_id, based on
- # @chest_id and @key and possibly @transaction.
+ # @chest_id, @key and possibly @transaction.
#
def object_id
id = "#{@chest_id}:#{@key}"
@@ -233,13 +233,8 @@
end
raise e unless new_chest_record
- @chest = new_chest_record[:service]
+ chest = new_chest_record
- #
- # And for all the future dubloons that we unmarshal, lets remember this.
- #
- CHEST_BY_SERVICE_ID[new_chest_record[:service_id]] = @chest
-
rescue DRb::DRbConnError => e
#
# Here is some fancy rescuing being done.
@@ -258,22 +253,25 @@
}))[@chest_id]
end
if new_chest_record.nil? && defined?(Archipelago::Pirate::BLACKBEARD)
- Archipelago::Pirate::BLACKBEARD.update_services!
+ Archipelago::Pirate::BLACKBEARD.update_services!(:validate => true)
new_chest_record = Archipelago::Pirate::BLACKBEARD.responsible_chest(@key)
new_chest_record = nil unless new_chest_record[:service].include?(@key, @transaction)
end
raise e unless new_chest_record
- @chest = new_chest_record[:service]
+ chest = new_chest_record
- #
- # And for all the future dubloons that we unmarshal, lets remember this.
- #
- CHEST_BY_SERVICE_ID[new_chest_record[:service_id]] = @chest
-
retry
end
end
+
+ private
+
+ def chest=(record)
+ @chest = record[:service]
+ @chest_id = record[:service_id]
+ CHEST_BY_SERVICE_ID[@chest_id] = @chest
+ end
end
@@ -356,22 +354,13 @@
# Our predecessor. We will NOT accept remote calls for keys before this one.
#
@predecessor_id = nil
- @predecessor_validator = nil
end
-
- def notify!(predecessor_id, predecessor_validator)
- @predecessor_id = predecessor_id
- @predecessor_validator = predecessor_validator
- end
-
+
#
- # We don't want to publish until we have notified our successor of our id.
+ # Our predecessor is making itself public.
#
- def around_publish(&block)
- @captain.update_services!
- successor = @captain.successor(service_id)
- successor.notify!(service_id, self) if successor
- yield
+ def notify!(predecessor_id)
+ @predecessor_id = predecessor_id
end
#
@@ -511,9 +500,7 @@
# will be wrapped within the block sent to with_transaction(transaction, &block).
#
def call_instance_method(key, method, transaction, *arguments, &block)
- if @predecessor_id && !between?(key, @predecessor_id, service_id) && !@predecessor_validator.valid?
- raise WrongChestException(service_id, @predecessor_id, key)
- end
+ assert_mine(key)
if transaction
return call_with_transaction(key, method, transaction, *arguments, &block)
@@ -661,6 +648,49 @@
private
#
+ # Asserts that +key+ (as far as I know) belongs here.
+ #
+ # Raises WrongChestException if not.
+ #
+ def assert_mine(key)
+ if @predecessor_id && !between?(key, @predecessor_id, service_id)
+ raise WrongChestException.new(service_id, @predecessor_id, key)
+ end
+ end
+
+ #
+ # We don't want to publish until we have notified our successor of our id,
+ # and we need to repeat that when chests are lost or found.
+ #
+ def around_publish(&block)
+ notify_successor!
+ @jockey.subscribe(:lost, @captain.service_descriptions[:chests], service_id) do |record|
+ notify_successor!
+ end
+ @jockey.subscribe(:found, @captain.service_descriptions[:chests], service_id) do |record|
+ notify_successor!
+ end
+ yield
+ end
+
+ #
+ # We want to unsubscribe from the lost and found channel.
+ #
+ def around_stop(&block)
+ @jockey.unsubscribe(:lost, @captain.service_descriptions[:chests], service_id)
+ @jockey.unsubscribe(:found, @captain.service_descriptions[:chests], service_id)
+ yield
+ end
+
+ #
+ # We want to notify our successor when stuff happens.
+ #
+ def notify_successor!
+ successor = (@captain.successor(service_id) || {:service => self})[:service]
+ successor.notify!(service_id)
+ end
+
+ #
# Returns whether key is between id1 and id2 on the big service circle.
#
def between?(key, id1, id2)
Modified: trunk/archipelago/tests/pirate_test.rb
===================================================================
--- trunk/archipelago/tests/pirate_test.rb 2007-03-10 02:16:16 UTC (rev 244)
+++ trunk/archipelago/tests/pirate_test.rb 2007-03-12 14:53:17 UTC (rev 245)
@@ -4,36 +4,72 @@
class PirateTest < Test::Unit::TestCase
def setup
+ @j = Archipelago::Disco::Jockey.new
@p = Archipelago::Pirate::Captain.new(:chest_description => {:class => "TestChest"},
:tranny_description => {:class => "TestManager"},
+ :jockey => @j,
:initial_lookup_timeout => 0)
@c = TestChest.new(:captain => @p,
:persistence_directory => Pathname.new(__FILE__).parent.join("chest.db"))
- @c.publish!
+ @c.publish!(:jockey => @j)
+ assert(@j.instance_eval do
+ @service_change_subscribers_by_event_type
+ end[:lost].include?([@p.instance_eval do
+ @service_descriptions
+ end[:chests], @c.service_id]))
+ assert(@j.instance_eval do
+ @service_change_subscribers_by_event_type
+ end[:found].include?([@p.instance_eval do
+ @service_descriptions
+ end[:chests], @c.service_id]))
+ assert_within(10) do
+ @p.update_services!
+ @p.chests.keys.include?(@c.service_id)
+ end
+ assert_equal(@c.service_id, @c.instance_eval do @predecessor_id end)
+
@c2 = TestChest.new(:captain => @p,
:persistence_directory => Pathname.new(__FILE__).parent.join("chest2.db"))
- @c2.publish!
+ @c2.publish!(:jockey => @j)
+ assert_within(10) do
+ @p.update_services!
+ @p.chests.keys.include?(@c2.service_id)
+ end
+ assert_within(10) do
+ @c2.service_id == @c.instance_eval do @predecessor_id end
+ end
+ assert_within(10) do
+ @c.service_id == @c2.instance_eval do @predecessor_id end
+ end
+
@tm = TestManager.new(:captain => @p,
:persistence_directory => Pathname.new(__FILE__).parent.join("tranny.db"))
@tm.publish!
assert_within(10) do
@p.update_services!
+ @p.trannies.keys.include?(@tm.service_id)
+ end
+
+
+ assert_within(10) do
+ @p.update_services!
@p.chests.keys.sort == [@c.service_id, @c2.service_id].sort
end
assert_within(10) do
+ @p.update_services!
@p.trannies.keys == [@tm.service_id]
end
end
def teardown
- @p.stop!
- @c.stop!
+ @c.close!
@c.persistence_provider.unlink!
- @c2.stop!
+ @c2.close!
@c2.persistence_provider.unlink!
- @tm.stop!
+ @tm.close!
@tm.persistence_provider.unlink!
@p.stop!
+ @j.stop!
end
def test_include
@@ -148,6 +184,20 @@
assert_equal("hoj", @p["hehu"])
end
+ def test_wrong_chest_exception
+ chests = [@c, @c2].sort do |a,b|
+ a.service_id <=> b.service_id
+ end
+ begin
+ $DO_ASSERT_MINE = true
+ assert_raise(Archipelago::Treasure::WrongChestException) do
+ chests.last.call_instance_method(chests.last.service_id.next, :bruhuf, nil)
+ end
+ ensure
+ $DO_ASSERT_MINE = false
+ end
+ end
+
def test_write_read_transaction
$T = true
@p["hej"] = "haha"
Modified: trunk/archipelago/tests/sanitation_benchmark.rb
===================================================================
--- trunk/archipelago/tests/sanitation_benchmark.rb 2007-03-10 02:16:16 UTC (rev 244)
+++ trunk/archipelago/tests/sanitation_benchmark.rb 2007-03-12 14:53:17 UTC (rev 245)
@@ -14,7 +14,7 @@
end
def teardown
- @d.stop!
+ @d.close!
@d.instance_eval do @persistence_provider.unlink! end
@c.stop!
end
Modified: trunk/archipelago/tests/sanitation_test.rb
===================================================================
--- trunk/archipelago/tests/sanitation_test.rb 2007-03-10 02:16:16 UTC (rev 244)
+++ trunk/archipelago/tests/sanitation_test.rb 2007-03-12 14:53:17 UTC (rev 245)
@@ -20,7 +20,7 @@
end
def teardown
- @d.stop!
+ @d.close!
@d.instance_eval do @persistence_provider.unlink! end
@c.stop!
end
@@ -38,13 +38,13 @@
assert_equal("kladd", klibb.downcase!)
assert_equal("KLADD", klibb.upcase)
assert_equal("kladd", klibb)
- @d.stop!
+ @d.close!
@c.update_services!(:validate => true)
assert_raise(Archipelago::Sanitation::NoRemoteDatabaseAvailableException) do
klibb.upcase!
end
ensure
- chest.stop!
+ chest.close!
chest.instance_eval do @persistence_provider.unlink! end
end
end
@@ -69,7 +69,7 @@
#
# Setup the network
#
- @d.stop!
+ @d.close!
dumps = []
cleaner2 = Archipelago::Sanitation::Officer.new(:minimum_nr_of_chunks => 3,
:initial_lookup_timeout => 0)
@@ -166,7 +166,7 @@
}
assert_equal(healthy_chunks,
chunks_by_id(cleaner2, "a"))
- dumps[1].stop!
+ dumps[1].close!
cleaner2.update_services!(:validate => true)
dumps[0].instance_eval do lost_peer({:service_id => dumps[1].service_id}) end
dumps[2].instance_eval do lost_peer({:service_id => dumps[1].service_id}) end
@@ -201,7 +201,7 @@
ensure
dumps.extend(Archipelago::Current::ThreadedCollection)
dumps.t_each do |dump|
- dump.stop!
+ dump.close!
dump.instance_eval do @persistence_provider.unlink! end
end
end
Modified: trunk/archipelago/tests/test_helper.rb
===================================================================
--- trunk/archipelago/tests/test_helper.rb 2007-03-10 02:16:16 UTC (rev 244)
+++ trunk/archipelago/tests/test_helper.rb 2007-03-12 14:53:17 UTC (rev 245)
@@ -43,6 +43,10 @@
class TestChest < Archipelago::Treasure::Chest
attr_reader :persistence_provider
+ alias :old_assert_mine :assert_mine
+ def assert_mine(key)
+ old_assert_mine(key) if defined?($DO_ASSERT_MINE) && $DO_ASSERT_MINE
+ end
end
def bm(label = "", options = {})
Modified: trunk/archipelago/tests/treasure_test.rb
===================================================================
--- trunk/archipelago/tests/treasure_test.rb 2007-03-10 02:16:16 UTC (rev 244)
+++ trunk/archipelago/tests/treasure_test.rb 2007-03-12 14:53:17 UTC (rev 245)
@@ -42,11 +42,11 @@
end
def teardown
- @c.stop!
+ @c.close!
@c.persistence_provider.unlink!
- @c2.stop!
+ @c2.close!
@c2.persistence_provider.unlink!
- @tm.stop!
+ @tm.close!
@tm.persistence_provider.unlink!
@p.stop!
end
@@ -95,12 +95,12 @@
def test_around_load
@c["brunis"] = A.new("hirr")
@c.persistence_provider.close!
- @c = TestChest.new(:persistence_directory => Pathname.new(__FILE__).parent.join("chest.db"))
+ @c = TestChest.new(:persistence_directory => Pathname.new(__FILE__).parent.join("chest.db"), :captain => @p)
a = @c["brunis"]
assert_equal(1, $BURKMAT3)
assert_equal(1, $BURKMAT4)
end
-
+
def test_around_save
s = A.new("hehu")
@c["oj"] = s
From nobody at rubyforge.org Mon Mar 12 12:25:39 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Mon, 12 Mar 2007 12:25:39 -0400 (EDT)
Subject: [Archipelago-submits] [246] trunk/archipelago: made CachedHashish
use RBTree instead of Hash as backer.
Message-ID: <20070312162539.365965240B66@rubyforge.org>
Revision: 246
Author: zond
Date: 2007-03-12 12:25:38 -0400 (Mon, 12 Mar 2007)
Log Message:
-----------
made CachedHashish use RBTree instead of Hash as backer. made Chests able to tell its CachedHashish to forget keys they shouldnt remember about.
Modified Paths:
--------------
trunk/archipelago/TODO
trunk/archipelago/lib/archipelago/hashish.rb
trunk/archipelago/lib/archipelago/treasure.rb
Modified: trunk/archipelago/TODO
===================================================================
--- trunk/archipelago/TODO 2007-03-12 14:53:17 UTC (rev 245)
+++ trunk/archipelago/TODO 2007-03-12 16:25:38 UTC (rev 246)
@@ -18,8 +18,8 @@
* Create a file server services that handles really big files within transactions
and allows the POSTing and GETing of them via HTTP.
- * Make Chests able to get rid of keys they shouldnt have in order to save memory.
- * Maybe use RBTree instead of Hash in the CachedHashish to be able to find these
- keys quickly.
+ * Test the ability of Dubloons to reconnect to the proper chest.
+ * If the first chest is disconnected and doesnt reappear.
+ * If the first chest is disconnected and then reappears.
+ * If a new chest that takes responsibility for the Dubloon appears.
- * Test the protection of Dubloon calling the wrong chest.
Modified: trunk/archipelago/lib/archipelago/hashish.rb
===================================================================
--- trunk/archipelago/lib/archipelago/hashish.rb 2007-03-12 14:53:17 UTC (rev 245)
+++ trunk/archipelago/lib/archipelago/hashish.rb 2007-03-12 16:25:38 UTC (rev 246)
@@ -17,6 +17,7 @@
require 'archipelago/current'
require 'bdb'
+require 'rbtree'
module Archipelago
@@ -26,7 +27,7 @@
module Hashish
#
- # In essence a persistence backed Hash.
+ # In essence a persistence backed Hash (with certain BTree behaviours).
#
# Will cache everything including timestamps for last
# modification in normal Hashes, but keep everything stored
@@ -91,6 +92,25 @@
raise "You have to implement me!"
end
#
+ # Get the first key/value pair from our cache tree.
+ #
+ def first
+ @content.first
+ end
+ #
+ # Get the last key/value pair from our cache tree.
+ #
+ def last
+ @content.last
+ end
+ #
+ # Forget the given +key+ in the cache tree.
+ #
+ def forget(key)
+ @content.delete(key)
+ @timestamps.delete(key)
+ end
+ #
# Simply get the value for the +key+.
#
# Will call value.load_hook and send it
@@ -171,7 +191,7 @@
# Initializes the cache hashes for this CachedHashish.
#
def initialize_cached_hashish
- @content = {}
+ @content = RBTree.new
@timestamps = {}
@lock = Archipelago::Current::Lock.new
end
Modified: trunk/archipelago/lib/archipelago/treasure.rb
===================================================================
--- trunk/archipelago/lib/archipelago/treasure.rb 2007-03-12 14:53:17 UTC (rev 245)
+++ trunk/archipelago/lib/archipelago/treasure.rb 2007-03-12 16:25:38 UTC (rev 246)
@@ -357,10 +357,17 @@
end
#
- # Our predecessor is making itself public.
+ # Our predecessor is making itself public,
+ # so make sure we dont know about any keys we shouldnt.
#
def notify!(predecessor_id)
@predecessor_id = predecessor_id
+ while (to_check = @db.first) && !between?(to_check.first, @predecessor_id, service_id)
+ @db.forget(to_check.first)
+ end
+ while (to_check = @db.last) && !between?(to_check.first, @predecessor_id, service_id)
+ @db.forget(to_check.first)
+ end
end
#
From nobody at rubyforge.org Tue Mar 13 05:09:49 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Tue, 13 Mar 2007 05:09:49 -0400 (EDT)
Subject: [Archipelago-submits] [247] trunk/archipelago: added transaction
locking and recovery tests to treasure_test.
Message-ID: <20070313090949.D1A1D524097C@rubyforge.org>
Revision: 247
Author: zond
Date: 2007-03-13 05:09:49 -0400 (Tue, 13 Mar 2007)
Log Message:
-----------
added transaction locking and recovery tests to treasure_test. fixed bugs in transaction locking and recovery :)
Modified Paths:
--------------
trunk/archipelago/lib/archipelago/hashish.rb
trunk/archipelago/lib/archipelago/treasure.rb
trunk/archipelago/tests/test_helper.rb
trunk/archipelago/tests/treasure_test.rb
Modified: trunk/archipelago/lib/archipelago/hashish.rb
===================================================================
--- trunk/archipelago/lib/archipelago/hashish.rb 2007-03-12 16:25:38 UTC (rev 246)
+++ trunk/archipelago/lib/archipelago/hashish.rb 2007-03-13 09:09:49 UTC (rev 247)
@@ -119,7 +119,7 @@
# if the value didnt exist in the live hash yet.
#
def [](key)
- @lock.synchronize_on(key) do
+ synchronize_on(key) do
value = @content[key]
return value if value
@@ -128,6 +128,14 @@
end
end
+ def without_lock(&block)
+ @unlocked_thread = Thread.current
+ begin
+ yield
+ ensure
+ @unlocked_thread = nil
+ end
+ end
#
# Insert +value+ under +key+.
#
@@ -136,7 +144,7 @@
# value.respond_to?(:save_hook).
#
def []=(key, value)
- @lock.synchronize_on(key) do
+ synchronize_on(key, @unlocked_thread != Thread.current) do
@content[key] = value
@@ -157,7 +165,7 @@
# a save is actually performed.
#
def store_if_changed(key)
- @lock.synchronize_on(key) do
+ synchronize_on(key) do
value = @content[key]
@@ -193,13 +201,13 @@
def initialize_cached_hashish
@content = RBTree.new
@timestamps = {}
- @lock = Archipelago::Current::Lock.new
+ @unlocked_thread = nil
end
#
# Delete +key+ and its value and timestamp.
#
def delete(key)
- @lock.synchronize_on(key) do
+ synchronize_on(key, @unlocked_thread != Thread.current) do
serialized_key = Marshal.dump(key)
@@ -241,7 +249,7 @@
# Returns the last time the value under +key+ was changed.
#
def timestamp(key)
- @lock.synchronize_on(key) do
+ synchronize_on(key) do
timestamp = @timestamps[key]
return timestamp if timestamp
Modified: trunk/archipelago/lib/archipelago/treasure.rb
===================================================================
--- trunk/archipelago/lib/archipelago/treasure.rb 2007-03-12 16:25:38 UTC (rev 246)
+++ trunk/archipelago/lib/archipelago/treasure.rb 2007-03-13 09:09:49 UTC (rev 247)
@@ -637,10 +637,12 @@
# Copy each key and value from our private space to the real space
#
snapshot.each do |key, value|
- if value == :deleted
- @db.delete(key)
- else
- @db[key] = value
+ @db.without_lock do
+ if value == :deleted
+ @db.delete(key)
+ else
+ @db[key] = value
+ end
end
end
@@ -838,10 +840,13 @@
#
@snapshot_by_transaction_db = @persistence_provider.get_hashish("prepared")
@snapshot_by_transaction_db.each do |serialized_transaction, serialized_snapshot|
- transaction = Marshal.load(transaction)
+ transaction = Marshal.load(serialized_transaction)
@crashed << transaction
@snapshot_by_transaction[transaction] = Marshal.load(serialized_snapshot)
+ @snapshot_by_transaction[transaction].each do |key, value|
+ @db.lock_on(key)
+ end
end
start_recovery_thread(transaction_recovery_interval)
end
Modified: trunk/archipelago/tests/test_helper.rb
===================================================================
--- trunk/archipelago/tests/test_helper.rb 2007-03-12 16:25:38 UTC (rev 246)
+++ trunk/archipelago/tests/test_helper.rb 2007-03-13 09:09:49 UTC (rev 247)
@@ -20,6 +20,7 @@
require 'ipaddr'
require 'thread'
require 'rubygems'
+require 'timeout'
class TestTransaction
def join(o)
Modified: trunk/archipelago/tests/treasure_test.rb
===================================================================
--- trunk/archipelago/tests/treasure_test.rb 2007-03-12 16:25:38 UTC (rev 246)
+++ trunk/archipelago/tests/treasure_test.rb 2007-03-13 09:09:49 UTC (rev 247)
@@ -349,6 +349,34 @@
end
assert(@c.active_transactions.empty?)
end
+
+ def test_transaction_locking_and_recovery
+ t = @tm.begin
+ @c["oj", t] = "blar"
+ @c2["bga", t] = "kggkgk"
+ @c.prepare!(t)
+ @c2.prepare!(t)
+ assert_raise(Timeout::Error) do
+ timeout(1) do
+ Thread.new do
+ @c2["bga"] = "ojoj"
+ end.join
+ end
+ end
+ @c2.close!
+ @c2 = TestChest.new(:captain => @p,
+ :persistence_directory => Pathname.new(__FILE__).parent.join("chest2.db"))
+ assert(@c2.instance_eval do
+ @snapshot_by_transaction[t].include?("bga")
+ end)
+ assert_raise(Timeout::Error) do
+ timeout(1) do
+ Thread.new do
+ @c2["bga"] = "ojoj"
+ end.join
+ end
+ end
+ end
private
From nobody at rubyforge.org Tue Mar 13 09:02:15 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Tue, 13 Mar 2007 09:02:15 -0400 (EDT)
Subject: [Archipelago-submits] [248] trunk/archipelago/TODO: fixed todo
Message-ID: <20070313130215.99C595240BA3@rubyforge.org>
Revision: 248
Author: zond
Date: 2007-03-13 09:02:15 -0400 (Tue, 13 Mar 2007)
Log Message:
-----------
fixed todo
Modified Paths:
--------------
trunk/archipelago/TODO
Modified: trunk/archipelago/TODO
===================================================================
--- trunk/archipelago/TODO 2007-03-13 09:09:49 UTC (rev 247)
+++ trunk/archipelago/TODO 2007-03-13 13:02:15 UTC (rev 248)
@@ -7,15 +7,13 @@
* This is now done. But it is not yet used to provide intelligence for the
transaction mechanism. How should it compare dirty state before and after?
- * Test the transaction recovery mechanism of Chest.
-
* Create a memcached-starter that publishes the address to the started memcached
instance on the Disco network.
* Create a memcached-client that uses Disco instance to find all memcached instances
in the network and distribute requests among them.
- * Create a file server services that handles really big files within transactions
+ * Create a file server service that handles really big files within transactions
and allows the POSTing and GETing of them via HTTP.
* Test the ability of Dubloons to reconnect to the proper chest.
From nobody at rubyforge.org Tue Mar 13 09:48:20 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Tue, 13 Mar 2007 09:48:20 -0400 (EDT)
Subject: [Archipelago-submits] [249] trunk/archipelago: renamed a few
around_stop to around_unpublish.
Message-ID: <20070313134820.685E7524096F@rubyforge.org>
Revision: 249
Author: zond
Date: 2007-03-13 09:48:20 -0400 (Tue, 13 Mar 2007)
Log Message:
-----------
renamed a few around_stop to around_unpublish. made chests require a predecessor before allowing remote method invocation. fixed a test accordingly
Modified Paths:
--------------
trunk/archipelago/lib/archipelago/dump.rb
trunk/archipelago/lib/archipelago/treasure.rb
trunk/archipelago/tests/sanitation_test.rb
Modified: trunk/archipelago/lib/archipelago/dump.rb
===================================================================
--- trunk/archipelago/lib/archipelago/dump.rb 2007-03-13 13:02:15 UTC (rev 248)
+++ trunk/archipelago/lib/archipelago/dump.rb 2007-03-13 13:48:20 UTC (rev 249)
@@ -143,7 +143,7 @@
# Before we stop we will unsubscribe from other Archipelago::Dump::Site
# changes and kill our @edge_check_thread.
#
- def around_stop(&block)
+ def around_unpublish(&block)
@jockey.unsubscribe(:lost, @officer.service_descriptions[:sites], service_id)
@edge_check_thread.kill
yield
Modified: trunk/archipelago/lib/archipelago/treasure.rb
===================================================================
--- trunk/archipelago/lib/archipelago/treasure.rb 2007-03-13 13:02:15 UTC (rev 248)
+++ trunk/archipelago/lib/archipelago/treasure.rb 2007-03-13 13:48:20 UTC (rev 249)
@@ -235,6 +235,7 @@
chest = new_chest_record
+ retry
rescue DRb::DRbConnError => e
#
# Here is some fancy rescuing being done.
@@ -662,7 +663,7 @@
# Raises WrongChestException if not.
#
def assert_mine(key)
- if @predecessor_id && !between?(key, @predecessor_id, service_id)
+ unless between?(key, @predecessor_id, service_id)
raise WrongChestException.new(service_id, @predecessor_id, key)
end
end
@@ -685,7 +686,7 @@
#
# We want to unsubscribe from the lost and found channel.
#
- def around_stop(&block)
+ def around_unpublish(&block)
@jockey.unsubscribe(:lost, @captain.service_descriptions[:chests], service_id)
@jockey.unsubscribe(:found, @captain.service_descriptions[:chests], service_id)
yield
Modified: trunk/archipelago/tests/sanitation_test.rb
===================================================================
--- trunk/archipelago/tests/sanitation_test.rb 2007-03-13 13:02:15 UTC (rev 248)
+++ trunk/archipelago/tests/sanitation_test.rb 2007-03-13 13:48:20 UTC (rev 249)
@@ -28,6 +28,7 @@
def test_sanitation_backed_chest
chest = Archipelago::Treasure::Chest.new(:persistence_directory => Pathname.new(__FILE__).parent.join("site_chest.db"),
:officer => @c)
+ chest.publish!
begin
assert_equal(nil, chest["klibb"])
assert_equal("kladd", chest["klibb"] = "kladd")
From nobody at rubyforge.org Thu Mar 15 10:49:36 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Thu, 15 Mar 2007 10:49:36 -0400 (EDT)
Subject: [Archipelago-submits] [250] trunk/oneliner: fixed a problem with
splitting strings in superstring
Message-ID: <20070315144936.EC20D5240C95@rubyforge.org>
Revision: 250
Author: zond
Date: 2007-03-15 10:49:36 -0400 (Thu, 15 Mar 2007)
Log Message:
-----------
fixed a problem with splitting strings in superstring
Modified Paths:
--------------
trunk/oneliner/ext/extconf.rb
trunk/oneliner/lib/oneliner/superstring.rb
trunk/oneliner/lib/oneliner.rb
trunk/oneliner/tests/superstring_test.rb
Added Paths:
-----------
trunk/oneliner/ext/oneliner_ext.c
trunk/oneliner/tests/weird_string
Removed Paths:
-------------
trunk/oneliner/ext/superstring_ext.c
Property Changed:
----------------
trunk/oneliner/
Property changes on: trunk/oneliner
___________________________________________________________________
Name: svn:ignore
- pkg
Makefile
superstring_ext.bundle
mkmf.log
+ pkg
Makefile
oneliner_ext.bundle
mkmf.log
Modified: trunk/oneliner/ext/extconf.rb
===================================================================
--- trunk/oneliner/ext/extconf.rb 2007-03-13 13:48:20 UTC (rev 249)
+++ trunk/oneliner/ext/extconf.rb 2007-03-15 14:49:36 UTC (rev 250)
@@ -28,6 +28,6 @@
crash "libssl needed"
end
-dir_config("superstring_ext")
+dir_config("oneliner_ext")
-create_makefile("superstring_ext")
+create_makefile("oneliner_ext")
Copied: trunk/oneliner/ext/oneliner_ext.c (from rev 188, trunk/oneliner/ext/superstring_ext.c)
===================================================================
--- trunk/oneliner/ext/oneliner_ext.c (rev 0)
+++ trunk/oneliner/ext/oneliner_ext.c 2007-03-15 14:49:36 UTC (rev 250)
@@ -0,0 +1,283 @@
+// Archipelago - a distributed computing toolkit for ruby
+// Copyright (C) 2006 Martin Kihlgren
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+#include "ruby.h"
+#include
+#include
+
+//
+// The distribution of degrees.
+//
+double P[] = {0.0,0.00943264209782069,0.49551807459988,0.165172691533293,0.0825863457666466,0.049551807459988,0.0330345383066586,0.0235960987904705,0.0176970740928528,0.0137643909611078,0.0110115127688862,0.00900941953817963,0.00750784961514969,0.00635279582820358,0.00544525356703164,0.00471921975809409,0.00412931728833233,0.00364351525441088,0.003238680226143,0.00289776651812795,0.00260798986631516,0.00235960987904705,0.00214509989004277,0.00195856946482166,0.00179535534275319,0.00165172691533293,0.00152467099876886,0.00141173240626746,0.00131089437724836,0.00122048786847261,0.00113912201057444,0.00106563026795673,0.000999028376209435,0.000938481201893711,0.000883276425311728,0.000832803486722487,0.000786536626349015,0.000744021133032852,0.000704862126031123,0.000668715350337219,0.000635279582820358,0.000604290334877902,0.000575514604645621,0.000548746483499313,0.000523803461522071,0.000500523307676646,0.000478761424734183,0.00045838859814975,0.000439289073226844,0.000421358906972687,0.000404504550693779,0.00038864162713716,0.00037369387224727,0.000359592216690769,0.000346273986442963,0.000333682205117764,0.000321764983506415,0.000310474984085138,0.000299768950151167,0.000289607290824009,0.000279953714463209,0.00027077490415294,0.000262040229825425,0.00025372149237065,0.000245792695734067,0.000238229843557634,0.000231010757389221,0.000224114913885065,0.000217523298770799,0.000211218275618022,0.000205183467743221,0.000199403651750455,0.000193864661424053,0.000188553300837093,0.000183457265679333,0.000178565071927885,0.000173865991087677,0.000169349991319166,0.000165007683849444,0.000160830274131736,0.000156809517278443,0.000152937677345642,0.000149207490093309,0.000145612128886241,0.000142145173436569,0.000138800581120414,0.000135572660629242,0.000132456047741213,0.000129445683019822,0.000126536791266568,0.000123724862571755,0.000121005634822925,0.000118375077544166,0.000115829376951819,0.000113364922123056,0.000110978292183624,0.000108666244429798,0.000106425703307534,0.000104253750178809,0.00010214761381156,0.000100104661535329,9.81223910098771e-05,9.61984225587031e-05,9.43304920235826e-05,9.25164441000522e-05,9.07542261171941e-05,8.90418822281904e-05,8.73775479809345e-05,8.57594452405468e-05,8.4185877437968e-05,8.2655225120914e-05,8.11659417854021e-05,7.97165499678056e-05,7.83056375789949e-05,7.69318544635739e-05,7.55939091685552e-05,7.42905659070284e-05,7.30206417034895e-05,7.17830037085151e-05,7.05765666713972e-05,6.94002905602072e-05,6.82531783195426e-05,6.71342737569272e-05,6.60426595494975e-05,6.49774553632153e-05,6.39378160774038e-05,6.29229301079212e-05,6.19320178227571e-05,6.09643300442765e-05,6.00191466327373e-05,5.90957751460799e-05,5.81935495713305e-05,5.73118291232801e-05,5.64499971063886e-05,5.5607459836144e-05,5.47836456163493e-05,5.39780037690501e-05,5.31900037140274e-05,5.24191340949836e-05,5.1664901949732e-05,5.09268319218787e-05,5.02044655116393e-05,4.9497360363588e-05,4.88050895892721e-05,4.81272411227544e-05,4.74634171072682e-05,4.68132333112782e-05,4.61763185723492e-05,4.55523142673175e-05,4.49408738073535e-05,4.43416621565888e-05,4.37543553730578e-05,4.31786401707807e-05,4.26142135018816e-05,4.20607821577013e-05,4.15180623879246e-05,4.09857795367973e-05,4.04636676955642e-05,3.99514693703039e-05,3.94489351643882e-05,3.89558234748333e-05,3.8471900201854e-05,3.79969384709669e-05,3.75307183670287e-05,3.70730266796259e-05,3.66236566592668e-05,3.61824077838539e-05,3.57490855349455e-05,3.5323501183339e-05,3.49054715835362e-05,3.4494818976671e-05,3.40913708015053e-05,3.36949595131157e-05,3.33054224089178e-05,3.29226014616889e-05,3.25463431592696e-05,3.21764983506415e-05,3.18129220980919e-05,3.1455473535192e-05,3.11040157303295e-05,3.07584155555481e-05,3.04185435604592e-05,3.00842738510036e-05,2.97554839728505e-05,2.94320547992326e-05,2.91138704230246e-05,2.88008180528846e-05,2.84927879132816e-05,2.81896731482467e-05,2.78913697286885e-05,2.75977763631233e-05,2.7308794411677e-05,2.70243278032221e-05,2.67442829555203e-05,2.64685686982469e-05,2.61970961987777e-05,2.59297788906269e-05,2.56665324044276e-05,2.54072745013526e-05,2.51519250088767e-05,2.49004057587879e-05,2.46526405273572e-05,2.44085549775814e-05,2.4168076603418e-05,2.39311346759335e-05,2.36976601912903e-05,2.3467585820501e-05,2.32408458608827e-05,2.30173761891434e-05,2.27971142160416e-05,2.25799988425555e-05,2.23659704175075e-05,2.21549706965877e-05,2.1946942802723e-05,2.17418311877443e-05,2.15395815953001e-05,2.13401410249733e-05,2.11434576975542e-05,2.09494810214298e-05,2.07581615600469e-05,2.05694510004101e-05,2.03833021225783e-05,2.01996687701227e-05,2.00185058215117e-05,1.98397691623911e-05,1.96634156587254e-05,1.94894031307721e-05,1.93176903278578e-05,1.91482369039292e-05,1.89810033938512e-05,1.88159511904264e-05,1.8653042522111e-05,1.84922404314032e-05,1.83335087538804e-05,1.81768120978643e-05,1.8022115824691e-05,1.78693860295665e-05,1.77185895229879e-05,1.75696938127107e-05,1.74226670862445e-05,1.72774781938591e-05,1.71340966320844e-05,1.6992492527687e-05,1.68526366221093e-05,1.67145002563543e-05,1.65780553563024e-05,1.64432744184463e-05,1.63101304960297e-05,1.61785971855779e-05,1.60486486138062e-05,1.59202594248957e-05,1.57934047681237e-05,1.5668060285837e-05,1.55442021017592e-05,1.54218068096194e-05,1.53008514620929e-05,1.51813135600453e-05,1.50631710420683e-05,1.49464022743003e-05,1.4830986040522e-05,1.4716901532518e-05,1.46041283406979e-05,1.44926464449674e-05,1.43824362058421e-05,1.42734783557979e-05,1.41657539908485e-05,1.40592445623459e-05,1.39539318689949e-05,1.38497980490771e-05,1.37468255728758e-05,1.36449972352989e-05,1.35442961486915e-05,1.34447057358335e-05,1.33462097231168e-05,1.32487921338969e-05,1.31524372820141e-05,1.30571297654777e-05,1.29628544603118e-05,1.28695965145542e-05,1.27773413424068e-05,1.26860746185325e-05,1.25957822724931e-05,1.25064504833265e-05,1.24180656742571e-05,1.23306145075369e-05,1.22440838794139e-05,1.21584609152222e-05,1.20737329645934e-05,1.19898875967838e-05,1.1906912596114e-05,1.18247959575201e-05,1.17435258822107e-05,1.16630907734284e-05,1.15834792323129e-05,1.15046800538618e-05,1.14266822229881e-05,1.13494749106706e-05,1.12730474701947e-05,1.1197389433482e-05,1.11224905075055e-05,1.10483405707888e-05,1.09749296699863e-05,1.09022480165426e-05,1.08302859834301e-05,1.07590341019602e-05,1.06884830586687e-05,1.06186236922721e-05,1.05494469906938e-05,1.04809440881568e-05,1.04131062623435e-05,1.03459249316187e-05,1.02793916523157e-05,1.02134981160829e-05,1.01482361472901e-05,1.0083597700492e-05,1.00195748579492e-05,9.95615982720272e-06,9.89334493870302e-06,9.83112264349106e-06,9.76948551092998e-06,9.70842622648667e-06,9.64793758956152e-06,9.58801251136548e-06,9.5286440128431e-06,9.46982522264036e-06,9.41154937511642e-06,9.35380980839792e-06,9.29659996247499e-06,9.23991337733795e-06,9.18374369115352e-06,9.12808463847987e-06,9.07293004851926e-06,9.0182738434077e-06,8.96411003654039e-06,8.91043273093236e-06,8.85723611761336e-06,8.80451447405614e-06,8.75226216263741e-06,8.70047362913068e-06,8.6491434012302e-06,8.59826608710532e-06,8.54783637398447e-06,8.49784902676818e-06,8.44829888667041e-06,8.39918086988744e-06,8.35048996629389e-06,8.30222123816503e-06,8.25436981892488e-06,8.20693091191957e-06,8.15989978921516e-06,8.11327179041964e-06,8.06704232152836e-06,8.02120685379241e-06,7.97576092260944e-06,7.93070012643651e-06,7.88602012572419e-06,7.84171664187181e-06,7.79778545620306e-06,7.7542224089617e-06,7.71102339832682e-06,7.66818437944722e-06,7.62570136349461e-06,7.58357041673497e-06,7.54178765961797e-06,7.50034926588381e-06,7.45925146168718e-06,7.41849052473807e-06,7.37806278345885e-06,7.33796461615744e-06,7.29819245021621e-06,7.25874276129612e-06,7.21961207255598e-06,7.18079695388632e-06,7.14229402115771e-06,7.10409993548307e-06,7.06621140249383e-06,7.0286251716295e-06,6.99133803544048e-06,6.95434682890376e-06,6.91764842875123e-06,6.88123975281044e-06,6.84511775935736e-06,6.80927944648115e-06,6.77372185146036e-06,6.73844205015067e-06,6.70343715638365e-06,6.66870432137648e-06,6.63424073315232e-06,6.60004361597112e-06,6.56611022977075e-06,6.53243786961808e-06,6.49902386516991e-06,6.46586558014353e-06,6.43296041179674e-06,6.40030579041707e-06,6.36789917882002e-06,6.33573807185628e-06,6.30381999592753e-06,6.27214250851081e-06,6.24070319769121e-06,6.20949968170275e-06,6.1785296084773e-06,6.1477906552013e-06,6.1172805278802e-06,6.08699696091049e-06,6.05693771665908e-06,6.02710058504993e-06,5.99748338315779e-06,5.96808395480898e-06,5.93890017018888e-06,5.90992992545625e-06,5.88117114236401e-06,5.85262176788651e-06,5.82427977385317e-06,5.79614315658817e-06,5.76820993655642e-06,5.74047815801529e-06,5.71294588867229e-06,5.6856112193485e-06,5.65847226364755e-06,5.63152715763018e-06,5.60477405949417e-06,5.5782111492596e-06,5.55183662845932e-06,5.52564871983451e-06,5.49964566703529e-06,5.4738257343262e-06,5.44818720629657e-06,5.42272838757556e-06,5.3974476025519e-06,5.37234319509817e-06,5.34741352829957e-06,5.32265698418707e-06,5.29807196347489e-06,5.2736568853022e-06,5.24941018697897e-06,5.22533032373594e-06,5.20141576847857e-06,5.17766501154488e-06,5.15407656046723e-06,5.13064893973783e-06,5.10738069057802e-06,5.08427037071115e-06,5.06131655413909e-06,5.03851783092225e-06,5.01587280696305e-06,4.99338010379281e-06,4.97103835836197e-06,4.94884622283357e-06,4.92680236437997e-06,4.90490546498272e-06,4.88315422123557e-06,4.86154734415046e-06,4.84008355896657e-06,4.81876160496231e-06,4.79758023527017e-06,4.77653821669442e-06,4.75563432953165e-06,4.73486736739396e-06,4.71423613703494e-06,4.69373945817827e-06,4.67337616334886e-06,4.65314509770666e-06,4.63304511888287e-06,4.61307509681872e-06,4.5932339136066e-06,4.57352046333361e-06,4.55393365192747e-06,4.5344723970047e-06,4.5151356277211e-06,4.49592228462441e-06,4.47683131950923e-06,4.45786169527403e-06,4.43901238578027e-06,4.42028237571368e-06,4.40167066044752e-06,4.38317624590782e-06,4.36479814844071e-06,4.34653539468154e-06,4.32838702142608e-06,4.31035207550348e-06,4.29242961365107e-06,4.27461870239111e-06,4.25691841790916e-06,4.23932784593433e-06,4.22184608162119e-06,4.20447222943345e-06,4.18720540302921e-06,4.17004472514794e-06,4.15298932749907e-06,4.13603835065214e-06,4.11919094392851e-06,4.10244626529465e-06,4.08580348125694e-06,4.06926176675793e-06,4.05282030507406e-06,4.03647828771489e-06,4.02023491432368e-06,4.00408939257941e-06,3.98804093810013e-06,3.97208877434773e-06,3.95623213253397e-06,3.94047025152786e-06,3.92480237776433e-06,3.90922776515415e-06,3.89374567499512e-06,3.87835537588447e-06,3.86305614363246e-06,3.84784726117722e-06,3.83272801850069e-06,3.81769771254578e-06,3.80275564713464e-06,3.78790113288802e-06,3.77313348714577e-06,3.75845203388839e-06,3.7438561036597e-06,3.72934503349048e-06,3.7149181668232e-06,3.70057485343778e-06,3.68631444937829e-06,3.67213631688068e-06,3.65803982430149e-06,3.64402434604746e-06,3.63008926250617e-06,3.61623395997752e-06,3.60245783060618e-06,3.5887602723149e-06,3.57514068873875e-06,3.56159848916019e-06,3.54813308844503e-06,3.5347439069792e-06,3.5214303706064e-06,3.50819191056653e-06,3.49502796343494e-06,3.48193797106253e-06,3.4689213805165e-06,3.45597764402204e-06,3.44310621890464e-06,3.43030656753324e-06,3.4175781572641e-06,3.40492046038535e-06,3.3923329540623e-06,3.37981512028347e-06,3.36736644580729e-06,3.35498642210947e-06,3.34267454533108e-06,3.33043031622731e-06,3.31825324011678e-06,3.30614282683169e-06,3.29409859066837e-06,3.28212005033866e-06,3.27020672892183e-06,3.25835815381704e-06,3.24657385669654e-06,3.23485337345937e-06,3.22319624418564e-06,3.21160201309145e-06,3.2000702284843e-06,3.18860044271912e-06,3.17719221215483e-06,3.16584509711142e-06,3.1545586618276e-06,3.14333247441896e-06,3.13216610683666e-06,3.1210591348266e-06,3.11001113788916e-06,3.09902169923937e-06,3.08809040576763e-06,3.07721684800084e-06,3.06640062006411e-06,3.05564131964283e-06,3.04493854794531e-06,3.03429190966578e-06,3.02370101294792e-06,3.0131654693488e-06,3.00268489380324e-06,2.99225890458865e-06,2.98188712329024e-06,2.97156917476675e-06,2.96130468711643e-06,2.95109329164362e-06,2.94093462282557e-06,2.93082831827977e-06,2.92077401873164e-06,2.91077136798256e-06,2.90082001287835e-06,2.89091960327808e-06,2.8810697920233e-06,2.87127023490758e-06,2.86152059064643e-06,2.85182052084763e-06,2.84216968998182e-06,2.8325677653535e-06,2.82301441707237e-06,2.81350931802499e-06,2.80405214384676e-06,2.79464257289425e-06,2.78528028621789e-06,2.77596496753489e-06,2.76669630320255e-06,2.75747398219187e-06,2.74829769606145e-06,2.73916713893168e-06,2.73008200745927e-06,2.72104200081205e-06,2.71204682064408e-06,2.703096171071e-06,2.69418975864572e-06,2.68532729233439e-06,2.67650848349257e-06,2.66773304584177e-06,2.65900069544622e-06,2.65031115068986e-06,2.64166413225367e-06,2.63305936309324e-06,2.62449656841651e-06,2.61597547566191e-06,2.60749581447662e-06,2.59905731669515e-06,2.5906597163181e-06,2.58230274949127e-06,2.57398615448486e-06,2.56570967167301e-06,2.55747304351354e-06,2.54927601452792e-06,2.54111833128143e-06,2.5329997423636e-06,2.52491999836882e-06,2.5168788518772e-06,2.50887605743562e-06,2.500911371539e-06,2.49298455261178e-06,2.48509536098959e-06,2.47724355890115e-06,2.46942891045036e-06,2.46165118159855e-06,2.45391014014698e-06,2.44620555571952e-06,2.43853719974547e-06,2.43090484544267e-06,2.42330826780066e-06,2.41574724356416e-06,2.4082215512166e-06,2.40073097096399e-06,2.39327528471876e-06,2.38585427608397e-06,2.37846773033758e-06,2.37111543441691e-06,2.36379717690327e-06,2.35651274800681e-06,2.3492619395514e-06,2.34204454495985e-06,2.33486035923911e-06,2.32770917896579e-06,2.32059080227171e-06,2.31350502882965e-06,2.30645165983932e-06,2.29943049801333e-06,2.29244134756344e-06,2.28548401418692e-06,2.27855830505302e-06,2.27166402878962e-06,2.26480099547001e-06,2.25796901659982e-06,2.25116790510403e-06,2.24439747531425e-06,2.23765754295595e-06,2.23094792513599e-06,2.22426844033019e-06,2.21761890837106e-06,2.21099915043562e-06,2.20440898903343e-06,2.19784824799464e-06,2.19131675245825e-06,2.18481432886045e-06,2.17834080492309e-06,2.17189600964225e-06,2.16547977327699e-06,2.15909192733812e-06,2.15273230457718e-06,2.14640073897548e-06,2.14009706573326e-06,2.13382112125897e-06,2.12757274315865e-06,2.12135177022544e-06,2.11515804242916e-06,2.10899140090604e-06,2.10285168794853e-06,2.09673874699519e-06,2.09065242262075e-06,2.0845925605262e-06,2.07855900752902e-06,2.0725516115535e-06,2.06657022162116e-06,2.06061468784128e-06,2.05468486140145e-06,2.04878059455834e-06,2.04290174062847e-06,2.03704815397911e-06,2.03121969001922e-06,2.0254162051906e-06,2.01963755695895e-06,2.01388360380523e-06,2.00815420521688e-06,2.00244922167933e-06,1.99676851466747e-06,1.99111194663725e-06,1.98547938101734e-06,1.97987068220091e-06,1.97428571553744e-06,1.96872434732466e-06,1.96318644480054e-06,1.95767187613537e-06,1.95218051042391e-06,1.94671221767762e-06,1.94126686881699e-06,1.93584433566387e-06,1.93044449093398e-06,1.92506720822943e-06,1.91971236203129e-06,1.91437982769232e-06,1.90906948142965e-06,1.90378120031766e-06,1.89851486228082e-06,1.89327034608667e-06,1.88804753133884e-06,1.88284629847014e-06,1.8776665287357e-06,1.8725081042062e-06,1.86737090776119e-06,1.8622548230824e-06,1.85715973464715e-06,1.85208552772189e-06,1.84703208835566e-06,1.84199930337376e-06,1.83698706037139e-06,1.83199524770733e-06,1.82702375449782e-06,1.82207247061029e-06,1.81714128665735e-06,1.81223009399071e-06,1.80733878469519e-06,1.8024672515828e-06,1.79761538818689e-06,1.79278308875628e-06,1.78797024824955e-06,1.78317676232931e-06,1.77840252735654e-06,1.773647440385e-06,1.76891139915566e-06,1.76419430209125e-06,1.75949604829074e-06,1.75481653752401e-06,1.75015567022647e-06,1.74551334749377e-06,1.74088947107657e-06,1.73628394337531e-06,1.73169666743508e-06,1.72712754694053e-06,1.72257648621078e-06,1.71804339019444e-06,1.71352816446462e-06,1.70903071521406e-06,1.7045509492502e-06,1.70008877399038e-06,1.69564409745707e-06,1.69121682827311e-06,1.68680687565701e-06,1.68241414941832e-06,1.67803855995299e-06,1.67368001823883e-06,1.66933843583095e-06,1.66501372485729e-06,1.6607057980142e-06,1.65641456856196e-06,1.65213995032051e-06,1.64788185766505e-06,1.64364020552176e-06,1.63941490936361e-06,1.63520588520607e-06,1.63101304960297e-06,1.6268363196424e-06,1.62267561294255e-06,1.61853084764768e-06,1.61440194242409e-06,1.61028881645613e-06,1.60619138944224e-06,1.60210958159106e-06,1.59804331361748e-06,1.59399250673885e-06,1.58995708267116e-06,1.58593696362521e-06,1.58193207230293e-06,1.57794233189359e-06,1.57396766607018e-06,1.57000799898572e-06,1.56606325526968e-06,1.56213336002434e-06,1.55821823882127e-06,1.55431781769781e-06,1.55043202315357e-06,1.54656078214694e-06,1.54270402209171e-06,1.53886167085362e-06,1.53503365674702e-06,1.5312199085315e-06,1.5274203554086e-06,1.52363492701849e-06,1.51986355343676e-06,1.51610616517115e-06,1.51236269315839e-06,1.50863306876096e-06,1.50491722376401e-06,1.50121509037221e-06,1.49752660120668e-06,1.49385168930188e-06,1.49019028810261e-06,1.48654233146099e-06,1.48290775363345e-06,1.47928648927782e-06,1.47567847345031e-06,1.47208364160268e-06,1.46850192957932e-06,1.46493327361436e-06,1.46137761032889e-06,1.4578348767281e-06,1.45430501019849e-06,1.45078794850514e-06,1.44728362978895e-06,1.44379199256388e-06,1.44031297571433e-06,1.43684651849239e-06,1.43339256051525e-06,1.42995104176251e-06,1.42652190257363e-06,1.42310508364531e-06,1.41970052602894e-06,1.41630817112803e-06,1.41292796069574e-06,1.40955983683235e-06,1.40620374198274e-06,1.40285961893403e-06,1.39952741081305e-06,1.39620706108396e-06,1.39289851354584e-06,1.38960171233035e-06,1.38631660189931e-06,1.3830431270424e-06,1.37978123287485e-06,1.3765308648351e-06,1.37329196868255e-06,1.37006449049528e-06,1.36684837666782e-06,1.36364357390893e-06,1.36045002923936e-06,1.35726768998967e-06,1.35409650379811e-06,1.35093641860838e-06,1.34778738266757e-06,1.34464934452399e-06,1.3415222530251e-06,1.3384060573154e-06,1.33530070683439e-06,1.33220615131449e-06,1.32912234077904e-06,1.32604922554025e-06,1.3229867561972e-06,1.31993488363388e-06,1.31689355901721e-06,1.31386273379508e-06,1.3108423596944e-06,1.30783238871921e-06,1.30483277314876e-06,1.30184346553559e-06,1.2988644187037e-06,1.29589558574666e-06,1.29293692002578e-06,1.28998837516825e-06,1.28704990506536e-06,1.28412146387067e-06,1.28120300599824e-06,1.27829448612083e-06,1.27539585916818e-06,1.27250708032521e-06,1.26962810503036e-06,1.2667588889738e-06,1.26389938809575e-06,1.26104955858482e-06,1.2582093568763e-06,1.25537873965048e-06,1.25255766383104e-06,1.24974608658339e-06,1.24694396531302e-06,1.24415125766394e-06,1.24136792151704e-06,1.23859391498851e-06,1.23582919642827e-06,1.2330737244184e-06,1.23032745777159e-06,1.2275903555296e-06,1.22486237696176e-06,1.2221434815634e-06,1.21943362905439e-06,1.21673277937763e-06,1.21404089269759e-06,1.21135792939882e-06,1.20868385008447e-06,1.20601861557491e-06,1.20336218690624e-06,1.20071452532889e-06,1.19807559230619e-06,1.19544534951298e-06,1.19282375883423e-06,1.19021078236362e-06,1.18760638240221e-06,1.18501052145707e-06,1.18242316223991e-06,1.17984426766578e-06,1.17727380085169e-06,1.17471172511534e-06,1.17215800397379e-06,1.16961260114214e-06,1.16707548053229e-06,1.16454660625161e-06,1.16202594260172e-06,1.15951345407717e-06,1.15700910536426e-06,1.15451286133974e-06,1.15202468706962e-06,1.14954454780789e-06,1.1470724089954e-06,1.14460823625857e-06,1.14215199540823e-06,1.13970365243844e-06,1.13726317352529e-06,1.13483052502578e-06,1.13240567347657e-06,1.12998858559295e-06,1.1275792282676e-06,1.12517756856947e-06,1.12278357374273e-06,1.12039721120555e-06,1.11801844854906e-06,1.11564725353624e-06,1.11328359410078e-06,1.11092743834607e-06,1.10857875454407e-06,1.10623751113426e-06,1.10390367672258e-06,1.10157722008039e-06,1.09925811014337e-06,1.09694631601058e-06,1.09464180694333e-06,1.09234455236423e-06,1.09005452185613e-06,1.08777168516114e-06,1.08549601217963e-06,1.08322747296922e-06,1.08096603774382e-06,1.07871167687261e-06,1.07646436087913e-06,1.07422406044025e-06,1.07199074638528e-06,1.06976438969497e-06,1.06754496150058e-06,1.06533243308297e-06,1.06312677587161e-06,1.06092796144375e-06,1.05873596152341e-06,1.05655074798054e-06,1.05437229283006e-06,1.05220056823103e-06,1.0500355464857e-06,1.04787720003866e-06,1.04572550147595e-06,1.0435804235242e-06,1.04144193904977e-06,1.03931002105785e-06,1.03718464269168e-06,1.03506577723164e-06,1.03295339809443e-06,1.03084747883226e-06,1.02874799313199e-06,1.02665491481433e-06,1.024568217833e-06,1.02248787627395e-06,1.02041386435453e-06,1.0183461564227e-06,1.01628472695626e-06,1.01422955056201e-06,1.01218060197502e-06,1.01013785605781e-06,1.00810128779963e-06,1.00607087231564e-06,1.0040465848462e-06,1.00202840075605e-06,1.00001629553365e-06,9.98010244790354e-07,9.96010224259712e-07,9.9401620979673e-07,9.92028177377136e-07,9.90046103096662e-07,9.88069963170322e-07,9.86099733931697e-07,9.84135391832232e-07,9.82176913440526e-07,9.80224275441638e-07,9.78277454636391e-07,9.76336427940684e-07,9.74401172384806e-07,9.72471665112757e-07,9.70547883381574e-07,9.68629804560662e-07,9.66717406131125e-07,9.64810665685107e-07,9.62909560925136e-07,9.61014069663472e-07,9.5912416982146e-07,9.57239839428883e-07,9.55361056623331e-07,9.5348779964956e-07,9.51620046858865e-07,9.49757776708456e-07,9.47900967760835e-07,9.46049598683177e-07,9.44203648246722e-07,9.42363095326163e-07,9.40527918899043e-07,9.38698098045154e-07,9.36873611945941e-07,9.35054439883909e-07,9.3324056124204e-07,9.31431955503199e-07,9.29628602249562e-07,9.27830481162039e-07,9.26037572019697e-07,9.24249854699195e-07,9.22467309174221e-07,9.20689915514926e-07,9.18917653887371e-07,9.17150504552972e-07,9.15388447867951e-07,9.13631464282792e-07,9.11879534341694e-07,9.10132638682036e-07,9.0839075803384e-07,9.06653873219244e-07,9.04921965151968e-07,9.03195014836792e-07,9.01473003369039e-07,8.9975591193405e-07,8.98043721806678e-07,8.96336414350771e-07,8.94633971018671e-07,8.92936373350704e-07,8.91243602974684e-07,8.89555641605414e-07,8.87872471044192e-07,8.86194073178324e-07,8.84520429980632e-07,8.8285152350897e-07,8.81187335905748e-07,8.79527849397451e-07,8.77873046294164e-07,8.76222908989099e-07,8.74577419958134e-07,8.72936561759338e-07,8.71300317032517e-07,8.69668668498748e-07,8.68041598959929e-07,8.66419091298322e-07,8.64801128476103e-07,8.63187693534916e-07,8.61578769595428e-07,8.59974339856889e-07,8.58374387596691e-07,8.56778896169931e-07,8.55187849008984e-07,8.53601229623068e-07,8.52019021597816e-07,8.50441208594857e-07,8.48867774351389e-07,8.4729870267976e-07,8.45733977467054e-07,8.4417358267468e-07,8.42617502337952e-07,8.41065720565691e-07,8.39518221539811e-07,8.37974989514922e-07,8.36436008817925e-07,8.34901263847616e-07,8.33370739074294e-07,8.31844419039359e-07,8.30322288354932e-07,8.2880433170346e-07,8.27290533837335e-07,8.25780879578508e-07,8.2427535381811e-07,8.22773941516073e-07,8.21276627700757e-07,8.19783397468574e-07,8.18294235983618e-07,8.16809128477295e-07,8.15328060247962e-07,8.13851016660556e-07,8.12377983146238e-07,8.10908945202032e-07,8.09443888390465e-07,8.07982798339219e-07,8.06525660740771e-07,8.05072461352049e-07,8.0362318599408e-07,8.02177820551645e-07,8.00736350972935e-07,7.99298763269214e-07,7.97865043514471e-07,7.9643517784509e-07,7.95009152459512e-07,7.93586953617903e-07,7.92168567641821e-07,7.90753980913889e-07,7.89343179877468e-07,7.87936151036332e-07,7.86532880954344e-07,7.85133356255137e-07,7.83737563621794e-07,7.82345489796534e-07,7.80957121580391e-07,7.79572445832908e-07,7.78191449471822e-07,7.76814119472757e-07,7.75440442868915e-07,7.74070406750772e-07,7.72703998265775e-07,7.7134120461804e-07,7.69982013068052e-07,7.68626410932369e-07,7.67274385583323e-07,7.6592592444873e-07,7.64581015011595e-07,7.6323964480982e-07,7.6190180143592e-07,7.60567472536733e-07,7.59236645813134e-07,7.57909309019755e-07,7.56585449964698e-07,7.55265056509263e-07,7.5394811656766e-07,7.5263461810674e-07,7.51324549145719e-07,7.500178977559e-07,7.48714652060408e-07,7.47414800233914e-07,7.46118330502372e-07,7.4482523114275e-07,7.43535490482762e-07,7.42249096900612e-07,7.40966038824725e-07,7.39686304733491e-07,7.38409883155003e-07,7.37136762666805e-07,7.3586693189563e-07,7.34600379517153e-07,7.3333709425573e-07,7.32077064884157e-07,7.30820280223411e-07,7.29566729142411e-07,7.28316400557762e-07,7.27069283433519e-07,7.25825366780938e-07,7.24584639658236e-07,7.23347091170348e-07,7.22112710468693e-07,7.20881486750928e-07,7.19653409260722e-07,7.18428467287513e-07,7.17206650166275e-07,7.15987947277293e-07,7.14772348045922e-07,7.13559841942367e-07,7.12350418481447e-07,7.11144067222376e-07,7.09940777768531e-07,7.08740539767232e-07,7.07543342909517e-07,7.06349176929923e-07,7.05158031606264e-07,7.03969896759412e-07,7.02784762253083e-07,7.01602617993616e-07,7.00423453929762e-07,6.99247260052466e-07,6.98074026394659e-07,6.96903743031047e-07,6.95736400077897e-07,6.94571987692829e-07,6.93410496074614e-07,6.9225191546296e-07,6.91096236138314e-07,6.89943448421653e-07,6.88793542674283e-07,6.8764650929764e-07,6.86502338733085e-07,6.85361021461708e-07,6.84222548004131e-07,6.83086908920307e-07,6.81954094809328e-07,6.80824096309229e-07,6.79696904096797e-07,6.78572508887373e-07,6.77450901434666e-07,6.76332072530563e-07,6.75216013004935e-07,6.74102713725454e-07,6.72992165597406e-07,6.71884359563501e-07,6.70779286603692e-07,6.69676937734993e-07,6.6857730401129e-07,6.67480376523166e-07,6.66386146397719e-07,6.65294604798377e-07,6.6420574292473e-07,6.63119552012343e-07,6.62036023332585e-07,6.6095514819245e-07,6.59876917934387e-07,6.58801323936124e-07,6.57728357610495e-07,6.56658010405271e-07,6.55590273802985e-07,6.54525139320771e-07,6.53462598510185e-07,6.52402642957046e-07,6.51345264281265e-07,6.5029045413668e-07,6.49238204210893e-07,6.48188506225103e-07,6.47141351933948e-07,6.46096733125338e-07,6.45054641620297e-07,6.44015069272802e-07,6.42978007969625e-07,6.41943449630173e-07,6.4091138620633e-07,6.39881809682304e-07,6.38854712074468e-07,6.37830085431205e-07,6.36807921832757e-07,6.35788213391071e-07,6.34770952249646e-07,6.33756130583379e-07,6.32743740598422e-07,6.31733774532024e-07,6.30726224652387e-07,6.29721083258519e-07,6.28718342680082e-07,6.27717995277249e-07,6.26720033440561e-07,6.25724449590774e-07,6.24731236178725e-07,6.23740385685183e-07,6.22751890620705e-07,6.21765743525502e-07,6.20781936969291e-07,6.19800463551158e-07,6.18821315899418e-07,6.17844486671479e-07,6.16869968553701e-07,6.1589775426126e-07,6.14927836538014e-07,6.13960208156365e-07,6.12994861917125e-07,6.12031790649384e-07,6.11070987210374e-07,6.10112444485338e-07,6.09156155387399e-07,6.08202112857426e-07,6.07250309863909e-07,6.06300739402824e-07,6.05353394497507e-07,6.04408268198526e-07,6.03465353583552e-07,6.02524643757233e-07,6.01586131851069e-07,6.00649811023285e-07,5.99715674458708e-07,5.9878371536864e-07,5.97853926990738e-07,5.96926302588891e-07,5.96000835453094e-07,5.95077518899332e-07,5.94156346269457e-07,5.93237310931067e-07,5.92320406277387e-07,5.91405625727151e-07,5.90492962724486e-07,5.89582410738789e-07,5.88673963264615e-07,5.8776761382156e-07,5.86863355954142e-07,5.85961183231691e-07,5.85061089248232e-07,5.84163067622372e-07,5.83267111997184e-07,5.823732160401e-07,5.81481373442795e-07,5.80591577921077e-07,5.79703823214776e-07,5.78818103087633e-07,5.77934411327194e-07,5.77052741744696e-07,5.76173088174964e-07,5.75295444476297e-07,5.74419804530366e-07,5.73546162242107e-07,5.72674511539611e-07,5.71804846374023e-07,5.70937160719435e-07,5.70071448572779e-07,5.69207703953729e-07,5.68345920904594e-07,5.67486093490215e-07,5.66628215797864e-07,5.65772281937142e-07,5.64918286039879e-07,5.6406622226003e-07,5.63216084773579e-07,5.62367867778438e-07,5.61521565494347e-07,5.60677172162777e-07,5.5983468204683e-07,5.58994089431144e-07,5.58155388621795e-07,5.573185739462e-07,5.56483639753022e-07,5.55650580412074e-07,5.54819390314225e-07,5.53990063871304e-07,5.53162595516007e-07,5.52336979701804e-07,5.51513210902845e-07,5.5069128361387e-07,5.49871192350111e-07,5.49052931647209e-07,5.48236496061117e-07,5.47421880168009e-07,5.46609078564196e-07,5.4579808586603e-07,5.44988896709816e-07,5.44181505751728e-07,5.43375907667713e-07,5.42572097153412e-07,5.41770068924065e-07,5.40969817714428e-07,5.40171338278687e-07,5.39374625390371e-07,5.38579673842264e-07,5.37786478446325e-07,5.36995034033601e-07,5.3620533545414e-07,5.35417377576911e-07,5.3463115528972e-07,5.33846663499126e-07,5.33063897130359e-07,5.32282851127238e-07,5.31503520452088e-07,5.30725900085662e-07,5.29949985027058e-07,5.29175770293636e-07,5.28403250920945e-07,5.27632421962635e-07,5.26863278490387e-07,5.26095815593824e-07,5.25330028380441e-07,5.24565911975524e-07,5.23803461522071e-07,5.23042672180718e-07,5.22283539129657e-07,5.21526057564567e-07,5.20770222698531e-07,5.20016029761966e-07,5.19263474002542e-07,5.18512550685112e-07,5.17763255091637e-07,5.17015582521107e-07,5.16269528289475e-07,5.15525087729577e-07,5.14782256191062e-07,5.14041029040318e-07,5.13301401660404e-07,5.12563369450972e-07,5.11826927828197e-07,5.11092072224711e-07,5.10358798089525e-07,5.09627100887963e-07,5.0889697610159e-07,5.08168419228145e-07,5.07441425781467e-07,5.06715991291429e-07,5.0599211130387e-07,5.05269781380524e-07,5.04548997098954e-07,5.03829754052483e-07,5.03112047850129e-07,5.02395874116535e-07,5.01681228491903e-07,5.00968106631928e-07,5.00256504207735e-07,4.99546416905808e-07,4.98837840427927e-07,4.98130770491105e-07,4.9742520282752e-07,4.96721133184452e-07,4.9601855732422e-07,4.95317471024115e-07,4.94617870076341e-07,4.93919750287948e-07,4.93223107480772e-07,4.92527937491369e-07,4.91834236170959e-07,4.91141999385356e-07,4.90451223014913e-07,4.89761902954456e-07,4.89074035113228e-07,4.88387615414823e-07,4.87702639797131e-07,4.87019104212271e-07,4.8633700462654e-07,4.85656337020345e-07,4.84977097388148e-07,4.84299281738409e-07,4.83622886093523e-07,4.82947906489764e-07,4.82274338977226e-07,4.81602179619766e-07,4.80931424494948e-07,4.80262069693981e-07,4.79594111321666e-07,4.78927545496341e-07,4.78262368349818e-07,4.77598576027334e-07,4.7693616468749e-07,4.76275130502199e-07,4.75615469656628e-07,4.74957178349145e-07,4.74300252791263e-07,4.73644689207584e-07,4.72990483835751e-07,4.72337632926384e-07,4.71686132743038e-07,4.71035979562137e-07,4.70387169672933e-07,4.69739699377444e-07,4.69093564990405e-07,4.68448762839215e-07,4.67805289263887e-07,4.67163140616991e-07,4.66522313263607e-07,4.65882803581271e-07,4.65244607959927e-07,4.64607722801871e-07,4.63972144521704e-07,4.63337869546282e-07,4.62704894314661e-07,4.62073215278054e-07,4.61442828899776e-07,4.60813731655195e-07,4.60185920031687e-07,4.5955939052858e-07,4.58934139657113e-07,4.5831016394038e-07,4.57687459913287e-07,4.57066024122502e-07,4.56445853126406e-07,4.55826943495048e-07,4.55209291810096e-07,4.54592894664787e-07,4.53977748663888e-07,4.53363850423639e-07,4.52751196571715e-07,4.52139783747176e-07,4.51529608600418e-07,4.50920667793135e-07,4.50312957998266e-07,4.49706475899951e-07,4.49101218193491e-07,4.48497181585296e-07,4.47894362792842e-07,4.47292758544632e-07,4.46692365580142e-07,4.46093180649786e-07,4.45495200514867e-07,4.44898421947533e-07,4.44302841730736e-07,4.43708456658186e-07,4.43115263534312e-07,4.42523259174213e-07,4.41932440403619e-07,4.41342804058852e-07,4.40754346986773e-07,4.40167066044752e-07,4.39580958100618e-07,4.3899602003262e-07,4.38412248729385e-07,4.37829641089877e-07,4.37248194023357e-07,4.36667904449338e-07,4.36088769297548e-07,4.35510785507889e-07,4.34933950030396e-07,4.34358259825193e-07,4.33783711862462e-07,4.33210303122392e-07,4.3263803059515e-07,4.32066891280833e-07,4.31496882189433e-07,4.30928000340799e-07,4.30360242764592e-07,4.29793606500254e-07,4.29228088596964e-07,4.28663686113602e-07,4.28100396118709e-07,4.27538215690451e-07,4.26977141916579e-07,4.26417171894393e-07,4.25858302730705e-07,4.25300531541797e-07,4.24743855453392e-07,4.24188271600608e-07,4.23633777127927e-07,4.23080369189158e-07,4.22528044947397e-07,4.21976801574993e-07,4.21426636253513e-07,4.20877546173704e-07,4.20329528535457e-07,4.19782580547773e-07,4.19236699428725e-07,4.18691882405426e-07,4.1814812671399e-07,4.17605429599501e-07,4.17063788315973e-07,4.16523200126322e-07,4.15983662302324e-07,4.15445172124587e-07,4.14907726882511e-07,4.1437132387426e-07,4.13835960406722e-07,4.1330163379548e-07,4.12768341364776e-07,4.12236080447478e-07,4.11704848385046e-07,4.11174642527499e-07,4.10645460233384e-07,4.10117298869739e-07,4.09590155812066e-07,4.09064028444292e-07,4.08538914158741e-07,4.080148103561e-07,4.07491714445387e-07,4.0696962384392e-07,4.06448535977282e-07,4.05928448279294e-07,4.05409358191981e-07,4.04891263165537e-07,4.04374160658302e-07,4.03858048136721e-07,4.03342923075322e-07,4.02828782956679e-07,4.02315625271384e-07,4.01803447518015e-07,4.01292247203107e-07,4.00782021841119e-07,4.00272768954409e-07,3.99764486073197e-07,3.99257170735541e-07,3.98750820487303e-07,3.98245432882123e-07,3.97741005481385e-07,3.97237535854194e-07,3.96735021577338e-07,3.96233460235268e-07,3.95732849420063e-07,3.95233186731401e-07,3.94734469776535e-07,3.94236696170259e-07,3.93739863534884e-07,3.93243969500205e-07,3.92749011703477e-07,3.92254987789385e-07,3.91761895410014e-07,3.91269732224826e-07,3.90778495900626e-07,3.90288184111542e-07,3.89798794538988e-07,3.89310324871646e-07,3.88822772805432e-07,3.88336136043473e-07,3.87850412296076e-07,3.87365599280706e-07,3.86881694721955e-07,3.86398696351515e-07,3.85916601908157e-07,3.85435409137698e-07,3.84955115792978e-07,3.84475719633834e-07,3.83997218427071e-07,3.8351960994644e-07,3.8304289197261e-07,3.82567062293141e-07,3.82092118702461e-07,3.81618059001837e-07,3.81144880999355e-07,3.80672582509889e-07,3.80201161355078e-07,3.79730615363302e-07,3.79260942369655e-07,3.78792140215922e-07,3.78324206750554e-07,3.7785713982864e-07,3.77390937311886e-07,3.76925597068592e-07,3.76461116973621e-07,3.75997494908383e-07,3.75534728760803e-07,3.75072816425304e-07,3.74611755802777e-07,3.74151544800563e-07,3.73692181332422e-07,3.73233663318517e-07,3.72775988685386e-07,3.72319155365919e-07,3.71863161299334e-07,3.71408004431159e-07,3.709536827132e-07,3.70500194103527e-07,3.70047536566442e-07,3.69595708072466e-07,3.69144706598308e-07,3.68694530126847e-07,3.68245176647107e-07,3.67796644154236e-07,3.67348930649483e-07,3.66902034140177e-07,3.66455952639703e-07,3.66010684167479e-07,3.6556622674894e-07,3.65122578415507e-07,3.64679737204572e-07,3.64237701159476e-07,3.63796468329482e-07,3.63356036769761e-07,3.62916404541365e-07,3.62477569711206e-07,3.62039530352038e-07,3.61602284542434e-07,3.61165830366765e-07,3.60730165915176e-07,3.60295289283573e-07,3.59861198573592e-07,3.59427891892589e-07,3.58995367353608e-07,3.58563623075372e-07,3.58132657182253e-07,3.57702467804256e-07,3.57273053077e-07,3.56844411141695e-07,3.56416540145122e-07,3.55989438239615e-07,3.55563103583041e-07,3.55137534338776e-07,3.54712728675692e-07,3.5428868476813e-07,3.53865400795886e-07,3.53442874944189e-07,3.53021105403683e-07,3.52600090370405e-07,3.52179828045768e-07,3.51760316636541e-07,3.51341554354831e-07,3.50923539418062e-07,3.50506270048956e-07,3.50089744475517e-07,3.49673960931009e-07,3.49258917653939e-07,3.48844612888039e-07,3.48431044882244e-07,3.48018211890678e-07,3.47606112172631e-07,3.47194743992545e-07,3.46784105619993e-07,3.46374195329662e-07,3.45965011401334e-07,3.45556552119868e-07,3.45148815775184e-07,3.44741800662242e-07,3.44335505081025e-07,3.43929927336524e-07,3.43525065738718e-07,3.43120918602555e-07,3.42717484247937e-07,3.42314760999702e-07,3.41912747187606e-07,3.41511441146306e-07,3.41110841215343e-07,3.40710945739123e-07,3.40311753066904e-07,3.39913261552774e-07,3.39515469555638e-07,3.39118375439198e-07,3.3872197757194e-07,3.38326274327113e-07,3.37931264082715e-07,3.37536945221475e-07,3.37143316130838e-07,3.36750375202946e-07,3.36358120834626e-07,3.35966551427368e-07,3.35575665387313e-07,3.35185461125235e-07,3.34795937056524e-07,3.34407091601174e-07,3.34018923183761e-07,3.33631430233432e-07,3.33244611183886e-07,3.3285846447336e-07,3.32472988544613e-07,3.32088181844908e-07,3.31704042826002e-07,3.31320569944122e-07,3.30937761659958e-07,3.30555616438641e-07,3.30174132749734e-07,3.29793309067208e-07,3.29413143869436e-07,3.29033635639172e-07,3.28654782863536e-07,3.28276584034004e-07,3.27899037646386e-07,3.27522142200815e-07,3.27145896201733e-07,3.26770298157874e-07,3.26395346582248e-07,3.26021039992131e-07,3.25647376909045e-07,3.25274355858748e-07,3.24901975371217e-07,3.24530233980632e-07,3.24159130225366e-07,3.23788662647965e-07,3.2341882979514e-07,3.23049630217749e-07,3.2268106247078e-07,3.22313125113345e-07,3.21945816708658e-07,3.21579135824024e-07,3.21213081030826e-07,3.20847650904511e-07,3.20482844024574e-07,3.20118658974546e-07,3.1975509434198e-07,3.19392148718436e-07,3.1902982069947e-07,3.18668108884618e-07,3.18307011877384e-07,3.17946528285224e-07,3.17586656719536e-07,3.17227395795645e-07,3.16868744132789e-07,3.16510700354108e-07,3.16153263086628e-07,3.15796430961248e-07,3.1544020261273e-07,3.15084576679683e-07,3.14729551804551e-07,3.143751266336e-07,3.14021299816904e-07,3.13668070008337e-07,3.1331543586555e-07,3.12963396049971e-07,3.12611949226782e-07,3.12261094064911e-07,3.1191082923702e-07,3.1156115341949e-07,3.11212065292409e-07,3.10863563539562e-07,3.10515646848415e-07,3.10168313910106e-07,3.0982156341943e-07,3.09475394074827e-07,3.09129804578373e-07,3.08784793635763e-07,3.08440359956303e-07,3.08096502252896e-07,3.07753219242029e-07,3.07410509643764e-07,3.07068372181723e-07,3.06726805583078e-07,3.06385808578539e-07,3.06045379902341e-07,3.05705518292232e-07,3.05366222489466e-07,3.05027491238785e-07,3.04689323288409e-07,3.04351717390029e-07,3.04014672298788e-07,3.03678186773278e-07,3.0334225957552e-07,3.03006889470959e-07,3.02672075228449e-07,3.02337815620246e-07,3.02004109421989e-07,3.01670955412699e-07,3.01338352374757e-07,3.01006299093904e-07,3.00674794359219e-07,3.00343836963116e-07,3.00013425701331e-07,2.99683559372907e-07,2.99354236780189e-07,2.99025456728811e-07,2.98697218027681e-07,2.98369519488979e-07,2.98042359928135e-07,2.97715738163831e-07,2.97389653017978e-07,2.97064103315714e-07,2.9673908788539e-07,2.96414605558561e-07,2.96090655169972e-07,2.95767235557553e-07,2.95444345562403e-07,2.95121984028783e-07,2.94800149804106e-07,2.94478841738924e-07,2.94158058686921e-07,2.938377995049e-07,2.93518063052772e-07,2.93198848193552e-07,2.92880153793342e-07,2.92561978721323e-07,2.92244321849747e-07,2.91927182053926e-07,2.91610558212219e-07,2.91294449206026e-07,2.90978853919779e-07,2.90663771240928e-07,2.90349200059931e-07,2.9003513927025e-07,2.89721587768336e-07,2.89408544453622e-07,2.8909600822851e-07,2.88783977998366e-07,2.88472452671506e-07,2.88161431159192e-07,2.87850912375615e-07,2.87540895237893e-07,2.87231378666054e-07,2.86922361583035e-07,2.86613842914667e-07,2.86305821589664e-07,2.85998296539622e-07,2.85691266698999e-07,2.85384731005116e-07,2.8507868839814e-07,2.84773137821079e-07,2.84468078219771e-07,2.84163508542876e-07,2.83859427741867e-07,2.8355583477102e-07,2.83252728587406e-07,2.82950108150881e-07,2.82647972424078e-07,2.82346320372398e-07,2.82045150964001e-07,2.81744463169796e-07,2.81444255963435e-07,2.81144528321302e-07,2.80845279222503e-07,2.80546507648862e-07,2.80248212584908e-07,2.79950393017868e-07,2.79653047937658e-07,2.79356176336875e-07,2.79059777210788e-07,2.7876384955733e-07,2.78468392377089e-07,2.781734046733e-07,2.77878885451835e-07,2.77584833721198e-07,2.77291248492514e-07,2.7699812877952e-07,2.76705473598559e-07,2.76413281968571e-07,2.76121552911084e-07,2.75830285450207e-07,2.75539478612622e-07,2.75249131427571e-07,2.74959242926858e-07,2.74669812144829e-07,2.74380838118375e-07,2.74092319886915e-07,2.73804256492394e-07,2.73516646979272e-07,2.73229490394517e-07,2.72942785787597e-07,2.72656532210473e-07,2.7237072871759e-07,2.72085374365869e-07,2.71800468214701e-07,2.71516009325936e-07,2.71231996763879e-07,2.70948429595282e-07,2.7066530688933e-07,2.70382627717644e-07,2.70100391154265e-07,2.69818596275648e-07,2.69537242160658e-07,2.69256327890558e-07,2.68975852549005e-07,2.68695815222041e-07,2.68416214998085e-07,2.68137050967926e-07,2.67858322224716e-07,2.67580027863963e-07,2.67302166983523e-07,2.67024738683592e-07,2.667477420667e-07,2.66471176237704e-07,2.66195040303779e-07,2.65919333374412e-07,2.65644054561395e-07,2.65369202978817e-07,2.65094777743058e-07,2.64820777972781e-07,2.64547202788925e-07,2.64274051314698e-07,2.6400132267557e-07,2.63729015999267e-07,2.63457130415763e-07,2.63185665057272e-07,2.62914619058243e-07,2.62643991555352e-07,2.62373781687497e-07,2.62103988595787e-07,2.61834611423541e-07,2.61565649316275e-07,2.61297101421699e-07,2.61028966889712e-07,2.60761244872389e-07,2.60493934523981e-07,2.60227035000903e-07,2.59960545461732e-07,2.59694465067196e-07,2.5942879298017e-07,2.59163528365671e-07,2.58898670390847e-07,2.58634218224973e-07,2.58370171039444e-07,2.58106528007771e-07,2.57843288305571e-07,2.5758045111056e-07,2.57318015602551e-07,2.57055980963445e-07,2.56794346377222e-07,2.56533111029941e-07,2.56272274109728e-07,2.56011834806771e-07,2.55751792313316e-07,2.55492145823657e-07,2.55232894534136e-07,2.54974037643127e-07,2.54715574351041e-07,2.54457503860311e-07,2.54199825375389e-07,2.53942538102742e-07,2.53685641250842e-07,2.53429134030164e-07,2.53173015653175e-07,2.52917285334334e-07,2.52661942290079e-07,2.52406985738827e-07,2.52152414900967e-07,2.51898228998849e-07,2.51644427256785e-07,2.51391008901038e-07,2.51137973159819e-07,2.50885319263279e-07,2.50633046443508e-07,2.50381153934519e-07,2.50129640972254e-07,2.49878506794571e-07,2.4962775064124e-07,2.49377371753937e-07,2.49127369376239e-07,2.48877742753617e-07,2.48628491133433e-07,2.48379613764931e-07,2.48131109899234e-07,2.47882978789334e-07,2.47635219690095e-07,2.47387831858237e-07,2.47140814552337e-07,2.46894167032824e-07,2.46647888561968e-07,2.4640197840388e-07,2.46156435824504e-07,2.45911260091611e-07,2.45666450474795e-07,2.45422006245467e-07,2.45177926676849e-07,2.4493421104397e-07,2.44690858623658e-07,2.44447868694538e-07,2.44205240537025e-07,2.43962973433317e-07,2.43721066667394e-07,2.43479519525008e-07,2.43238331293681e-07,2.42997501262697e-07,2.427570287231e-07,2.42516912967687e-07,2.42277153291001e-07,2.4203774898933e-07,2.41798699360699e-07,2.41560003704864e-07,2.4132166132331e-07,2.41083671519243e-07,2.40846033597588e-07,2.4060874686498e-07,2.40371810629761e-07,2.40135224201976e-07,2.39898986893366e-07,2.39663098017364e-07,2.39427556889092e-07,2.3919236282535e-07,2.38957515144618e-07,2.38723013167047e-07,2.38488856214456e-07,2.38255043610324e-07,2.3802157467979e-07,2.37788448749643e-07,2.37555665148322e-07,2.37323223205906e-07,2.37091122254116e-07,2.36859361626301e-07,2.36627940657443e-07,2.36396858684145e-07,2.36166115044629e-07,2.35935709078732e-07,2.35705640127899e-07,2.35475907535182e-07,2.35246510645231e-07,2.35017448804291e-07,2.34788721360199e-07,2.34560327662378e-07,2.34332267061831e-07,2.34104538911139e-07,2.33877142564455e-07,2.33650077377499e-07,2.33423342707555e-07,2.33196937913465e-07,2.32970862355623e-07,2.32745115395976e-07,2.32519696398014e-07,2.32294604726767e-07,2.32069839748802e-07,2.31845400832217e-07,2.31621287346638e-07,2.31397498663211e-07,2.31174034154603e-07,2.30950893194994e-07,2.30728075160074e-07,2.30505579427036e-07,2.30283405374577e-07,2.30061552382886e-07,2.29840019833649e-07,2.29618807110036e-07,2.29397913596703e-07,2.29177338679783e-07,2.28957081746886e-07,2.2873714218709e-07,2.28517519390943e-07,2.28298212750453e-07,2.28079221659086e-07,2.27860545511761e-07,2.2764218370485e-07,2.27424135636167e-07,2.2720640070497e-07,2.26988978311951e-07,2.26771867859237e-07,2.26555068750385e-07,2.26338580390375e-07,2.26122402185609e-07,2.25906533543904e-07,2.25690973874492e-07,2.25475722588012e-07,2.25260779096507e-07,2.25046142813424e-07,2.24831813153601e-07,2.24617789533274e-07,2.24404071370065e-07,2.24190658082979e-07,2.23977549092406e-07,2.23764743820109e-07,2.23552241689226e-07,2.23340042124262e-07,2.2312814455109e-07,2.2291654839694e-07,2.22705253090402e-07,2.2249425806142e-07,2.22283562741286e-07,2.22073166562639e-07,2.21863068959457e-07};
+
+//
+// Encrypt the context again
+//
+static void
+_aes_randomize_context(char *context)
+{
+ AES_KEY key;
+
+ AES_set_encrypt_key((unsigned char *) "0123456789012345", 128, &key);
+
+ AES_encrypt((unsigned char *) context + 4,
+ (unsigned char *) context + 4,
+ &key);
+ ( (int *) context )[0] = 1;
+}
+
+//
+// Encrypt the buffer of ctx and set its counter to 0
+//
+static void
+aes_randomize_context(VALUE self)
+{
+ rb_str_modify(self);
+ _aes_randomize_context(RSTRING(self)->ptr);
+}
+
+//
+// Fill the buffer of context with seed and randomize it.
+//
+static VALUE
+aes_seed_context(VALUE self, VALUE seed)
+{
+ int tmp;
+
+ if (RSTRING(self)->len < 17)
+ rb_str_concat(self, rb_str_new(0, 17));
+
+ rb_str_modify(self);
+ for (tmp = 1; tmp < 5; tmp++)
+ ( (int *) RSTRING(self)->ptr )[tmp] = NUM2INT(seed);
+
+ aes_randomize_context(self);
+
+ return seed;
+}
+
+//
+// Return an int that is the next random in the context
+//
+static int
+_aes_random(char *context)
+{
+ int *int_context = (int *) context;
+ if (int_context[0] < 5) {
+ int return_value = int_context[int_context[0]];
+ int_context[0]++;
+ return return_value;
+ } else {
+ _aes_randomize_context(context);
+ return _aes_random(context);
+ }
+}
+
+//
+// Get the int at the counter of context in the buffer of context.
+//
+static VALUE
+aes_random(int argc, VALUE *argv, VALUE self)
+{
+ int return_value;
+
+ rb_str_modify(self);
+ return_value = _aes_random(RSTRING(self)->ptr);
+
+ if (argc == 0) {
+ return rb_float_new( ( (double) ( (unsigned int) return_value ) ) / ( (double) UINT_MAX ));
+ } else {
+ VALUE max = argv[0];
+ return INT2NUM(( (unsigned int) return_value ) % NUM2INT(max));
+ }
+}
+
+//
+// Initialize a context.
+//
+static VALUE
+context_initialize(int argc, VALUE *argv, VALUE self)
+{
+ rb_str_concat(self, rb_str_new(0, 17));
+ return self;
+}
+
+//
+// Return self ^ subject
+//
+static VALUE
+string_xor(VALUE self, VALUE subject)
+{
+ int tmp;
+ int len = RSTRING(self)->len;
+ VALUE return_value = rb_str_new(0, len);
+
+ Check_Type(subject, T_STRING);
+
+ if (len != RSTRING(subject)->len)
+ rb_raise(rb_eTypeError, "argument to xor must be of same length as receiver");
+
+ rb_str_modify(return_value);
+ for (tmp = 0; tmp < len / 4; tmp++) {
+ ( (int *) RSTRING(return_value)->ptr )[tmp] = ( (int *) RSTRING(self)->ptr )[tmp] ^ ( (int *) RSTRING(subject)->ptr )[tmp];
+ }
+ for (tmp = 0; tmp < (len % 4); tmp++) {
+ RSTRING(return_value)->ptr[len - tmp - 1] = RSTRING(self)->ptr[len - tmp - 1] ^ RSTRING(subject)->ptr[len - tmp - 1];
+ }
+
+ return return_value;
+}
+
+//
+// Make str into str ^ subject
+//
+static void
+_string_xor2(char *str, char *subject, int len)
+{
+ int tmp;
+
+ for (tmp = 0; tmp < len / 4; tmp++) {
+ ( (int *) str )[tmp] = ( (int *) str )[tmp] ^ ( (int *) subject )[tmp];
+ }
+ for (tmp = 0; tmp < (len % 4); tmp++) {
+ str[len - tmp - 1] = str[len - tmp - 1] ^ subject[len - tmp - 1];
+ }
+}
+
+//
+// Return an Array with bite size chunks of us
+//
+static VALUE
+string_hack(VALUE self, VALUE size_value)
+{
+ int size = NUM2INT(size_value);
+ VALUE rval = rb_ary_new();
+ int tmp;
+
+ for (tmp = 0; tmp < RSTRING(self)->len; tmp += size) {
+ rb_ary_push(rval, rb_str_new(RSTRING(self)->ptr + tmp, tmp + size < RSTRING(self)->len ? size : RSTRING(self)->len - tmp));
+ }
+
+ return rval;
+}
+
+//
+// Make self into self ^ subject
+//
+static VALUE
+string_xor2(VALUE self, VALUE subject)
+{
+ int tmp;
+ int len = RSTRING(self)->len;
+
+ Check_Type(subject, T_STRING);
+
+ if (len != RSTRING(subject)->len)
+ rb_raise(rb_eTypeError, "argument to xor must be of same length as receiver");
+
+ rb_str_modify(self);
+ _string_xor2(RSTRING(self)->ptr, RSTRING(subject)->ptr, len);
+
+ return self;
+}
+
+//
+// Get a random degree
+//
+static VALUE
+superstring_get_degree(VALUE self, VALUE context)
+{
+ double f;
+ int tmp = 0;
+
+ rb_str_modify(context);
+ f = ( (double) ( (unsigned int) _aes_random(RSTRING(context)->ptr) ) ) / ( (double) UINT_MAX );
+ while (f > 0) {
+ tmp++;
+ f = f - P[tmp];
+ }
+ return INT2NUM(tmp);
+}
+
+//
+// Use context to check if the degree blocks adjacent to block
+// can lead to anything good.
+//
+static VALUE
+superstring_decode_multiple(VALUE self, VALUE block, VALUE degree, VALUE context, VALUE blocks, VALUE nr_of_blocks)
+{
+ int tmp;
+ int got_new_block = 0;
+ int missing_blocks = NUM2INT(degree);
+ int _nr_of_blocks = NUM2INT(nr_of_blocks);
+ int missing_block_nr;
+ char xor_sum[RSTRING(block)->len];
+ VALUE get_func = rb_intern("[]");
+ VALUE this_block;
+
+ memcpy(xor_sum, RSTRING(block)->ptr, RSTRING(block)->len);
+
+ for (tmp = 0; tmp < NUM2INT(degree); tmp++) {
+ rb_str_modify(context);
+ int block_nr = ( (unsigned int) _aes_random(RSTRING(context)->ptr) ) % _nr_of_blocks;
+
+ if ((this_block = rb_funcall(blocks, get_func, 1, INT2NUM(block_nr))) != Qnil) {
+ _string_xor2(xor_sum, RSTRING(this_block)->ptr, RSTRING(this_block)->len);
+ missing_blocks--;
+ } else {
+ missing_block_nr = block_nr;
+ }
+ }
+
+ if (missing_blocks == 1) {
+ rb_funcall(blocks, rb_intern("[]="), 2, INT2NUM(missing_block_nr), rb_str_new(xor_sum, RSTRING(block)->len));
+ got_new_block = 1;
+ }
+
+ return got_new_block ? Qtrue : Qfalse;
+}
+
+
+VALUE rb_superstring;
+VALUE rb_context;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void Init_oneliner_ext() {
+ VALUE string = rb_const_get(rb_cObject, rb_intern("String"));
+ rb_superstring = rb_define_class_under(rb_define_module("Oneliner"),
+ "SuperString",
+ string);
+ rb_context = rb_define_class_under(rb_superstring,
+ "Context",
+ string);
+
+ rb_define_const(rb_context, "INT_MAX", INT2NUM(INT_MAX));
+ rb_define_method(rb_context, "initialize", context_initialize, -1);
+ rb_define_method(rb_context, "random", aes_random, -1);
+ rb_define_method(rb_context, "seed", aes_seed_context, 1);
+ rb_define_method(string, "^", string_xor, 1);
+ rb_define_method(string, "^=", string_xor2, 1);
+ rb_define_method(string, "hack", string_hack, 1);
+ rb_define_method(rb_superstring, "get_degree", superstring_get_degree, 1);
+ rb_define_method(rb_superstring, "decode_multiple", superstring_decode_multiple, 5);
+ }
+#ifdef __cplusplus
+}
+#endif
+
Deleted: trunk/oneliner/ext/superstring_ext.c
===================================================================
--- trunk/oneliner/ext/superstring_ext.c 2007-03-13 13:48:20 UTC (rev 249)
+++ trunk/oneliner/ext/superstring_ext.c 2007-03-15 14:49:36 UTC (rev 250)
@@ -1,265 +0,0 @@
-// Archipelago - a distributed computing toolkit for ruby
-// Copyright (C) 2006 Martin Kihlgren
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-#include "ruby.h"
-#include
-#include
-
-//
-// The distribution of degrees.
-//
-double P[] = {0.0,0.00943264209782069,0.49551807459988,0.165172691533293,0.0825863457666466,0.049551807459988,0.0330345383066586,0.0235960987904705,0.0176970740928528,0.0137643909611078,0.0110115127688862,0.00900941953817963,0.00750784961514969,0.00635279582820358,0.00544525356703164,0.00471921975809409,0.00412931728833233,0.00364351525441088,0.003238680226143,0.00289776651812795,0.00260798986631516,0.00235960987904705,0.00214509989004277,0.00195856946482166,0.00179535534275319,0.00165172691533293,0.00152467099876886,0.00141173240626746,0.00131089437724836,0.00122048786847261,0.00113912201057444,0.00106563026795673,0.000999028376209435,0.000938481201893711,0.000883276425311728,0.000832803486722487,0.000786536626349015,0.000744021133032852,0.000704862126031123,0.000668715350337219,0.000635279582820358,0.000604290334877902,0.000575514604645621,0.000548746483499313,0.000523803461522071,0.000500523307676646,0.000478761424734183,0.00045838859814975,0.000439289073226844,0.000421358906972687,0.000404504550693779,0.00038864162713716,0.00037369387224727,0.000359592216690769,0.000346273986442963,0.000333682205117764,0.000321764983506415,0.000310474984085138,0.000299768950151167,0.000289607290824009,0.000279953714463209,0.00027077490415294,0.000262040229825425,0.00025372149237065,0.000245792695734067,0.000238229843557634,0.000231010757389221,0.000224114913885065,0.000217523298770799,0.000211218275618022,0.000205183467743221,0.000199403651750455,0.000193864661424053,0.000188553300837093,0.000183457265679333,0.000178565071927885,0.000173865991087677,0.000169349991319166,0.000165007683849444,0.000160830274131736,0.000156809517278443,0.000152937677345642,0.000149207490093309,0.000145612128886241,0.000142145173436569,0.000138800581120414,0.000135572660629242,0.000132456047741213,0.000129445683019822,0.000126536791266568,0.000123724862571755,0.000121005634822925,0.000118375077544166,0.000115829376951819,0.000113364922123056,0.000110978292183624,0.000108666244429798,0.000106425703307534,0.000104253750178809,0.00010214761381156,0.000100104661535329,9.81223910098771e-05,9.61984225587031e-05,9.43304920235826e-05,9.25164441000522e-05,9.07542261171941e-05,8.90418822281904e-05,8.73775479809345e-05,8.57594452405468e-05,8.4185877437968e-05,8.2655225120914e-05,8.11659417854021e-05,7.97165499678056e-05,7.83056375789949e-05,7.69318544635739e-05,7.55939091685552e-05,7.42905659070284e-05,7.30206417034895e-05,7.17830037085151e-05,7.05765666713972e-05,6.94002905602072e-05,6.82531783195426e-05,6.71342737569272e-05,6.60426595494975e-05,6.49774553632153e-05,6.39378160774038e-05,6.29229301079212e-05,6.19320178227571e-05,6.09643300442765e-05,6.00191466327373e-05,5.90957751460799e-05,5.81935495713305e-05,5.73118291232801e-05,5.64499971063886e-05,5.5607459836144e-05,5.47836456163493e-05,5.39780037690501e-05,5.31900037140274e-05,5.24191340949836e-05,5.1664901949732e-05,5.09268319218787e-05,5.02044655116393e-05,4.9497360363588e-05,4.88050895892721e-05,4.81272411227544e-05,4.74634171072682e-05,4.68132333112782e-05,4.61763185723492e-05,4.55523142673175e-05,4.49408738073535e-05,4.43416621565888e-05,4.37543553730578e-05,4.31786401707807e-05,4.26142135018816e-05,4.20607821577013e-05,4.15180623879246e-05,4.09857795367973e-05,4.04636676955642e-05,3.99514693703039e-05,3.94489351643882e-05,3.89558234748333e-05,3.8471900201854e-05,3.79969384709669e-05,3.75307183670287e-05,3.70730266796259e-05,3.66236566592668e-05,3.61824077838539e-05,3.57490855349455e-05,3.5323501183339e-05,3.49054715835362e-05,3.4494818976671e-05,3.40913708015053e-05,3.36949595131157e-05,3.33054224089178e-05,3.29226014616889e-05,3.25463431592696e-05,3.21764983506415e-05,3.18129220980919e-05,3.1455473535192e-05,3.11040157303295e-05,3.07584155555481e-05,3.04185435604592e-05,3.00842738510036e-05,2.97554839728505e-05,2.94320547992326e-05,2.91138704230246e-05,2.88008180528846e-05,2.84927879132816e-05,2.81896731482467e-05,2.78913697286885e-05,2.75977763631233e-05,2.7308794411677e-05,2.70243278032221e-05,2.67442829555203e-05,2.64685686982469e-05,2.61970961987777e-05,2.59297788906269e-05,2.56665324044276e-05,2.54072745013526e-05,2.51519250088767e-05,2.49004057587879e-05,2.46526405273572e-05,2.44085549775814e-05,2.4168076603418e-05,2.39311346759335e-05,2.36976601912903e-05,2.3467585820501e-05,2.32408458608827e-05,2.30173761891434e-05,2.27971142160416e-05,2.25799988425555e-05,2.23659704175075e-05,2.21549706965877e-05,2.1946942802723e-05,2.17418311877443e-05,2.15395815953001e-05,2.13401410249733e-05,2.11434576975542e-05,2.09494810214298e-05,2.07581615600469e-05,2.05694510004101e-05,2.03833021225783e-05,2.01996687701227e-05,2.00185058215117e-05,1.98397691623911e-05,1.96634156587254e-05,1.94894031307721e-05,1.93176903278578e-05,1.91482369039292e-05,1.89810033938512e-05,1.88159511904264e-05,1.8653042522111e-05,1.84922404314032e-05,1.83335087538804e-05,1.81768120978643e-05,1.8022115824691e-05,1.78693860295665e-05,1.77185895229879e-05,1.75696938127107e-05,1.74226670862445e-05,1.72774781938591e-05,1.71340966320844e-05,1.6992492527687e-05,1.68526366221093e-05,1.67145002563543e-05,1.65780553563024e-05,1.64432744184463e-05,1.63101304960297e-05,1.61785971855779e-05,1.60486486138062e-05,1.59202594248957e-05,1.57934047681237e-05,1.5668060285837e-05,1.55442021017592e-05,1.54218068096194e-05,1.53008514620929e-05,1.51813135600453e-05,1.50631710420683e-05,1.49464022743003e-05,1.4830986040522e-05,1.4716901532518e-05,1.46041283406979e-05,1.44926464449674e-05,1.43824362058421e-05,1.42734783557979e-05,1.41657539908485e-05,1.40592445623459e-05,1.39539318689949e-05,1.38497980490771e-05,1.37468255728758e-05,1.36449972352989e-05,1.35442961486915e-05,1.34447057358335e-05,1.33462097231168e-05,1.32487921338969e-05,1.31524372820141e-05,1.30571297654777e-05,1.29628544603118e-05,1.28695965145542e-05,1.27773413424068e-05,1.26860746185325e-05,1.25957822724931e-05,1.25064504833265e-05,1.24180656742571e-05,1.23306145075369e-05,1.22440838794139e-05,1.21584609152222e-05,1.20737329645934e-05,1.19898875967838e-05,1.1906912596114e-05,1.18247959575201e-05,1.17435258822107e-05,1.16630907734284e-05,1.15834792323129e-05,1.15046800538618e-05,1.14266822229881e-05,1.13494749106706e-05,1.12730474701947e-05,1.1197389433482e-05,1.11224905075055e-05,1.10483405707888e-05,1.09749296699863e-05,1.09022480165426e-05,1.08302859834301e-05,1.07590341019602e-05,1.06884830586687e-05,1.06186236922721e-05,1.05494469906938e-05,1.04809440881568e-05,1.04131062623435e-05,1.03459249316187e-05,1.02793916523157e-05,1.02134981160829e-05,1.01482361472901e-05,1.0083597700492e-05,1.00195748579492e-05,9.95615982720272e-06,9.89334493870302e-06,9.83112264349106e-06,9.76948551092998e-06,9.70842622648667e-06,9.64793758956152e-06,9.58801251136548e-06,9.5286440128431e-06,9.46982522264036e-06,9.41154937511642e-06,9.35380980839792e-06,9.29659996247499e-06,9.23991337733795e-06,9.18374369115352e-06,9.12808463847987e-06,9.07293004851926e-06,9.0182738434077e-06,8.96411003654039e-06,8.91043273093236e-06,8.85723611761336e-06,8.80451447405614e-06,8.75226216263741e-06,8.70047362913068e-06,8.6491434012302e-06,8.59826608710532e-06,8.54783637398447e-06,8.49784902676818e-06,8.44829888667041e-06,8.39918086988744e-06,8.35048996629389e-06,8.30222123816503e-06,8.25436981892488e-06,8.20693091191957e-06,8.15989978921516e-06,8.11327179041964e-06,8.06704232152836e-06,8.02120685379241e-06,7.97576092260944e-06,7.93070012643651e-06,7.88602012572419e-06,7.84171664187181e-06,7.79778545620306e-06,7.7542224089617e-06,7.71102339832682e-06,7.66818437944722e-06,7.62570136349461e-06,7.58357041673497e-06,7.54178765961797e-06,7.50034926588381e-06,7.45925146168718e-06,7.41849052473807e-06,7.37806278345885e-06,7.33796461615744e-06,7.29819245021621e-06,7.25874276129612e-06,7.21961207255598e-06,7.18079695388632e-06,7.14229402115771e-06,7.10409993548307e-06,7.06621140249383e-06,7.0286251716295e-06,6.99133803544048e-06,6.95434682890376e-06,6.91764842875123e-06,6.88123975281044e-06,6.84511775935736e-06,6.80927944648115e-06,6.77372185146036e-06,6.73844205015067e-06,6.70343715638365e-06,6.66870432137648e-06,6.63424073315232e-06,6.60004361597112e-06,6.56611022977075e-06,6.53243786961808e-06,6.49902386516991e-06,6.46586558014353e-06,6.43296041179674e-06,6.40030579041707e-06,6.36789917882002e-06,6.33573807185628e-06,6.30381999592753e-06,6.27214250851081e-06,6.24070319769121e-06,6.20949968170275e-06,6.1785296084773e-06,6.1477906552013e-06,6.1172805278802e-06,6.08699696091049e-06,6.05693771665908e-06,6.02710058504993e-06,5.99748338315779e-06,5.96808395480898e-06,5.93890017018888e-06,5.90992992545625e-06,5.88117114236401e-06,5.85262176788651e-06,5.82427977385317e-06,5.79614315658817e-06,5.76820993655642e-06,5.74047815801529e-06,5.71294588867229e-06,5.6856112193485e-06,5.65847226364755e-06,5.63152715763018e-06,5.60477405949417e-06,5.5782111492596e-06,5.55183662845932e-06,5.52564871983451e-06,5.49964566703529e-06,5.4738257343262e-06,5.44818720629657e-06,5.42272838757556e-06,5.3974476025519e-06,5.37234319509817e-06,5.34741352829957e-06,5.32265698418707e-06,5.29807196347489e-06,5.2736568853022e-06,5.24941018697897e-06,5.22533032373594e-06,5.20141576847857e-06,5.17766501154488e-06,5.15407656046723e-06,5.13064893973783e-06,5.10738069057802e-06,5.08427037071115e-06,5.06131655413909e-06,5.03851783092225e-06,5.01587280696305e-06,4.99338010379281e-06,4.97103835836197e-06,4.94884622283357e-06,4.92680236437997e-06,4.90490546498272e-06,4.88315422123557e-06,4.86154734415046e-06,4.84008355896657e-06,4.81876160496231e-06,4.79758023527017e-06,4.77653821669442e-06,4.75563432953165e-06,4.73486736739396e-06,4.71423613703494e-06,4.69373945817827e-06,4.67337616334886e-06,4.65314509770666e-06,4.63304511888287e-06,4.61307509681872e-06,4.5932339136066e-06,4.57352046333361e-06,4.55393365192747e-06,4.5344723970047e-06,4.5151356277211e-06,4.49592228462441e-06,4.47683131950923e-06,4.45786169527403e-06,4.43901238578027e-06,4.42028237571368e-06,4.40167066044752e-06,4.38317624590782e-06,4.36479814844071e-06,4.34653539468154e-06,4.32838702142608e-06,4.31035207550348e-06,4.29242961365107e-06,4.27461870239111e-06,4.25691841790916e-06,4.23932784593433e-06,4.22184608162119e-06,4.20447222943345e-06,4.18720540302921e-06,4.17004472514794e-06,4.15298932749907e-06,4.13603835065214e-06,4.11919094392851e-06,4.10244626529465e-06,4.08580348125694e-06,4.06926176675793e-06,4.05282030507406e-06,4.03647828771489e-06,4.02023491432368e-06,4.00408939257941e-06,3.98804093810013e-06,3.97208877434773e-06,3.95623213253397e-06,3.94047025152786e-06,3.92480237776433e-06,3.90922776515415e-06,3.89374567499512e-06,3.87835537588447e-06,3.86305614363246e-06,3.84784726117722e-06,3.83272801850069e-06,3.81769771254578e-06,3.80275564713464e-06,3.78790113288802e-06,3.77313348714577e-06,3.75845203388839e-06,3.7438561036597e-06,3.72934503349048e-06,3.7149181668232e-06,3.70057485343778e-06,3.68631444937829e-06,3.67213631688068e-06,3.65803982430149e-06,3.64402434604746e-06,3.63008926250617e-06,3.61623395997752e-06,3.60245783060618e-06,3.5887602723149e-06,3.57514068873875e-06,3.56159848916019e-06,3.54813308844503e-06,3.5347439069792e-06,3.5214303706064e-06,3.50819191056653e-06,3.49502796343494e-06,3.48193797106253e-06,3.4689213805165e-06,3.45597764402204e-06,3.44310621890464e-06,3.43030656753324e-06,3.4175781572641e-06,3.40492046038535e-06,3.3923329540623e-06,3.37981512028347e-06,3.36736644580729e-06,3.35498642210947e-06,3.34267454533108e-06,3.33043031622731e-06,3.31825324011678e-06,3.30614282683169e-06,3.29409859066837e-06,3.28212005033866e-06,3.27020672892183e-06,3.25835815381704e-06,3.24657385669654e-06,3.23485337345937e-06,3.22319624418564e-06,3.21160201309145e-06,3.2000702284843e-06,3.18860044271912e-06,3.17719221215483e-06,3.16584509711142e-06,3.1545586618276e-06,3.14333247441896e-06,3.13216610683666e-06,3.1210591348266e-06,3.11001113788916e-06,3.09902169923937e-06,3.08809040576763e-06,3.07721684800084e-06,3.06640062006411e-06,3.05564131964283e-06,3.04493854794531e-06,3.03429190966578e-06,3.02370101294792e-06,3.0131654693488e-06,3.00268489380324e-06,2.99225890458865e-06,2.98188712329024e-06,2.97156917476675e-06,2.96130468711643e-06,2.95109329164362e-06,2.94093462282557e-06,2.93082831827977e-06,2.92077401873164e-06,2.91077136798256e-06,2.90082001287835e-06,2.89091960327808e-06,2.8810697920233e-06,2.87127023490758e-06,2.86152059064643e-06,2.85182052084763e-06,2.84216968998182e-06,2.8325677653535e-06,2.82301441707237e-06,2.81350931802499e-06,2.80405214384676e-06,2.79464257289425e-06,2.78528028621789e-06,2.77596496753489e-06,2.76669630320255e-06,2.75747398219187e-06,2.74829769606145e-06,2.73916713893168e-06,2.73008200745927e-06,2.72104200081205e-06,2.71204682064408e-06,2.703096171071e-06,2.69418975864572e-06,2.68532729233439e-06,2.67650848349257e-06,2.66773304584177e-06,2.65900069544622e-06,2.65031115068986e-06,2.64166413225367e-06,2.63305936309324e-06,2.62449656841651e-06,2.61597547566191e-06,2.60749581447662e-06,2.59905731669515e-06,2.5906597163181e-06,2.58230274949127e-06,2.57398615448486e-06,2.56570967167301e-06,2.55747304351354e-06,2.54927601452792e-06,2.54111833128143e-06,2.5329997423636e-06,2.52491999836882e-06,2.5168788518772e-06,2.50887605743562e-06,2.500911371539e-06,2.49298455261178e-06,2.48509536098959e-06,2.47724355890115e-06,2.46942891045036e-06,2.46165118159855e-06,2.45391014014698e-06,2.44620555571952e-06,2.43853719974547e-06,2.43090484544267e-06,2.42330826780066e-06,2.41574724356416e-06,2.4082215512166e-06,2.40073097096399e-06,2.39327528471876e-06,2.38585427608397e-06,2.37846773033758e-06,2.37111543441691e-06,2.36379717690327e-06,2.35651274800681e-06,2.3492619395514e-06,2.34204454495985e-06,2.33486035923911e-06,2.32770917896579e-06,2.32059080227171e-06,2.31350502882965e-06,2.30645165983932e-06,2.29943049801333e-06,2.29244134756344e-06,2.28548401418692e-06,2.27855830505302e-06,2.27166402878962e-06,2.26480099547001e-06,2.25796901659982e-06,2.25116790510403e-06,2.24439747531425e-06,2.23765754295595e-06,2.23094792513599e-06,2.22426844033019e-06,2.21761890837106e-06,2.21099915043562e-06,2.20440898903343e-06,2.19784824799464e-06,2.19131675245825e-06,2.18481432886045e-06,2.17834080492309e-06,2.17189600964225e-06,2.16547977327699e-06,2.15909192733812e-06,2.15273230457718e-06,2.14640073897548e-06,2.14009706573326e-06,2.13382112125897e-06,2.12757274315865e-06,2.12135177022544e-06,2.11515804242916e-06,2.10899140090604e-06,2.10285168794853e-06,2.09673874699519e-06,2.09065242262075e-06,2.0845925605262e-06,2.07855900752902e-06,2.0725516115535e-06,2.06657022162116e-06,2.06061468784128e-06,2.05468486140145e-06,2.04878059455834e-06,2.04290174062847e-06,2.03704815397911e-06,2.03121969001922e-06,2.0254162051906e-06,2.01963755695895e-06,2.01388360380523e-06,2.00815420521688e-06,2.00244922167933e-06,1.99676851466747e-06,1.99111194663725e-06,1.98547938101734e-06,1.97987068220091e-06,1.97428571553744e-06,1.96872434732466e-06,1.96318644480054e-06,1.95767187613537e-06,1.95218051042391e-06,1.94671221767762e-06,1.94126686881699e-06,1.93584433566387e-06,1.93044449093398e-06,1.92506720822943e-06,1.91971236203129e-06,1.91437982769232e-06,1.90906948142965e-06,1.90378120031766e-06,1.89851486228082e-06,1.89327034608667e-06,1.88804753133884e-06,1.88284629847014e-06,1.8776665287357e-06,1.8725081042062e-06,1.86737090776119e-06,1.8622548230824e-06,1.85715973464715e-06,1.85208552772189e-06,1.84703208835566e-06,1.84199930337376e-06,1.83698706037139e-06,1.83199524770733e-06,1.82702375449782e-06,1.82207247061029e-06,1.81714128665735e-06,1.81223009399071e-06,1.80733878469519e-06,1.8024672515828e-06,1.79761538818689e-06,1.79278308875628e-06,1.78797024824955e-06,1.78317676232931e-06,1.77840252735654e-06,1.773647440385e-06,1.76891139915566e-06,1.76419430209125e-06,1.75949604829074e-06,1.75481653752401e-06,1.75015567022647e-06,1.74551334749377e-06,1.74088947107657e-06,1.73628394337531e-06,1.73169666743508e-06,1.72712754694053e-06,1.72257648621078e-06,1.71804339019444e-06,1.71352816446462e-06,1.70903071521406e-06,1.7045509492502e-06,1.70008877399038e-06,1.69564409745707e-06,1.69121682827311e-06,1.68680687565701e-06,1.68241414941832e-06,1.67803855995299e-06,1.67368001823883e-06,1.66933843583095e-06,1.66501372485729e-06,1.6607057980142e-06,1.65641456856196e-06,1.65213995032051e-06,1.64788185766505e-06,1.64364020552176e-06,1.63941490936361e-06,1.63520588520607e-06,1.63101304960297e-06,1.6268363196424e-06,1.62267561294255e-06,1.61853084764768e-06,1.61440194242409e-06,1.61028881645613e-06,1.60619138944224e-06,1.60210958159106e-06,1.59804331361748e-06,1.59399250673885e-06,1.58995708267116e-06,1.58593696362521e-06,1.58193207230293e-06,1.57794233189359e-06,1.57396766607018e-06,1.57000799898572e-06,1.56606325526968e-06,1.56213336002434e-06,1.55821823882127e-06,1.55431781769781e-06,1.55043202315357e-06,1.54656078214694e-06,1.54270402209171e-06,1.53886167085362e-06,1.53503365674702e-06,1.5312199085315e-06,1.5274203554086e-06,1.52363492701849e-06,1.51986355343676e-06,1.51610616517115e-06,1.51236269315839e-06,1.50863306876096e-06,1.50491722376401e-06,1.50121509037221e-06,1.49752660120668e-06,1.49385168930188e-06,1.49019028810261e-06,1.48654233146099e-06,1.48290775363345e-06,1.47928648927782e-06,1.47567847345031e-06,1.47208364160268e-06,1.46850192957932e-06,1.46493327361436e-06,1.46137761032889e-06,1.4578348767281e-06,1.45430501019849e-06,1.45078794850514e-06,1.44728362978895e-06,1.44379199256388e-06,1.44031297571433e-06,1.43684651849239e-06,1.43339256051525e-06,1.42995104176251e-06,1.42652190257363e-06,1.42310508364531e-06,1.41970052602894e-06,1.41630817112803e-06,1.41292796069574e-06,1.40955983683235e-06,1.40620374198274e-06,1.40285961893403e-06,1.39952741081305e-06,1.39620706108396e-06,1.39289851354584e-06,1.38960171233035e-06,1.38631660189931e-06,1.3830431270424e-06,1.37978123287485e-06,1.3765308648351e-06,1.37329196868255e-06,1.37006449049528e-06,1.36684837666782e-06,1.36364357390893e-06,1.36045002923936e-06,1.35726768998967e-06,1.35409650379811e-06,1.35093641860838e-06,1.34778738266757e-06,1.34464934452399e-06,1.3415222530251e-06,1.3384060573154e-06,1.33530070683439e-06,1.33220615131449e-06,1.32912234077904e-06,1.32604922554025e-06,1.3229867561972e-06,1.31993488363388e-06,1.31689355901721e-06,1.31386273379508e-06,1.3108423596944e-06,1.30783238871921e-06,1.30483277314876e-06,1.30184346553559e-06,1.2988644187037e-06,1.29589558574666e-06,1.29293692002578e-06,1.28998837516825e-06,1.28704990506536e-06,1.28412146387067e-06,1.28120300599824e-06,1.27829448612083e-06,1.27539585916818e-06,1.27250708032521e-06,1.26962810503036e-06,1.2667588889738e-06,1.26389938809575e-06,1.26104955858482e-06,1.2582093568763e-06,1.25537873965048e-06,1.25255766383104e-06,1.24974608658339e-06,1.24694396531302e-06,1.24415125766394e-06,1.24136792151704e-06,1.23859391498851e-06,1.23582919642827e-06,1.2330737244184e-06,1.23032745777159e-06,1.2275903555296e-06,1.22486237696176e-06,1.2221434815634e-06,1.21943362905439e-06,1.21673277937763e-06,1.21404089269759e-06,1.21135792939882e-06,1.20868385008447e-06,1.20601861557491e-06,1.20336218690624e-06,1.20071452532889e-06,1.19807559230619e-06,1.19544534951298e-06,1.19282375883423e-06,1.19021078236362e-06,1.18760638240221e-06,1.18501052145707e-06,1.18242316223991e-06,1.17984426766578e-06,1.17727380085169e-06,1.17471172511534e-06,1.17215800397379e-06,1.16961260114214e-06,1.16707548053229e-06,1.16454660625161e-06,1.16202594260172e-06,1.15951345407717e-06,1.15700910536426e-06,1.15451286133974e-06,1.15202468706962e-06,1.14954454780789e-06,1.1470724089954e-06,1.14460823625857e-06,1.14215199540823e-06,1.13970365243844e-06,1.13726317352529e-06,1.13483052502578e-06,1.13240567347657e-06,1.12998858559295e-06,1.1275792282676e-06,1.12517756856947e-06,1.12278357374273e-06,1.12039721120555e-06,1.11801844854906e-06,1.11564725353624e-06,1.11328359410078e-06,1.11092743834607e-06,1.10857875454407e-06,1.10623751113426e-06,1.10390367672258e-06,1.10157722008039e-06,1.09925811014337e-06,1.09694631601058e-06,1.09464180694333e-06,1.09234455236423e-06,1.09005452185613e-06,1.08777168516114e-06,1.08549601217963e-06,1.08322747296922e-06,1.08096603774382e-06,1.07871167687261e-06,1.07646436087913e-06,1.07422406044025e-06,1.07199074638528e-06,1.06976438969497e-06,1.06754496150058e-06,1.06533243308297e-06,1.06312677587161e-06,1.06092796144375e-06,1.05873596152341e-06,1.05655074798054e-06,1.05437229283006e-06,1.05220056823103e-06,1.0500355464857e-06,1.04787720003866e-06,1.04572550147595e-06,1.0435804235242e-06,1.04144193904977e-06,1.03931002105785e-06,1.03718464269168e-06,1.03506577723164e-06,1.03295339809443e-06,1.03084747883226e-06,1.02874799313199e-06,1.02665491481433e-06,1.024568217833e-06,1.02248787627395e-06,1.02041386435453e-06,1.0183461564227e-06,1.01628472695626e-06,1.01422955056201e-06,1.01218060197502e-06,1.01013785605781e-06,1.00810128779963e-06,1.00607087231564e-06,1.0040465848462e-06,1.00202840075605e-06,1.00001629553365e-06,9.98010244790354e-07,9.96010224259712e-07,9.9401620979673e-07,9.92028177377136e-07,9.90046103096662e-07,9.88069963170322e-07,9.86099733931697e-07,9.84135391832232e-07,9.82176913440526e-07,9.80224275441638e-07,9.78277454636391e-07,9.76336427940684e-07,9.74401172384806e-07,9.72471665112757e-07,9.70547883381574e-07,9.68629804560662e-07,9.66717406131125e-07,9.64810665685107e-07,9.62909560925136e-07,9.61014069663472e-07,9.5912416982146e-07,9.57239839428883e-07,9.55361056623331e-07,9.5348779964956e-07,9.51620046858865e-07,9.49757776708456e-07,9.47900967760835e-07,9.46049598683177e-07,9.44203648246722e-07,9.42363095326163e-07,9.40527918899043e-07,9.38698098045154e-07,9.36873611945941e-07,9.35054439883909e-07,9.3324056124204e-07,9.31431955503199e-07,9.29628602249562e-07,9.27830481162039e-07,9.26037572019697e-07,9.24249854699195e-07,9.22467309174221e-07,9.20689915514926e-07,9.18917653887371e-07,9.17150504552972e-07,9.15388447867951e-07,9.13631464282792e-07,9.11879534341694e-07,9.10132638682036e-07,9.0839075803384e-07,9.06653873219244e-07,9.04921965151968e-07,9.03195014836792e-07,9.01473003369039e-07,8.9975591193405e-07,8.98043721806678e-07,8.96336414350771e-07,8.94633971018671e-07,8.92936373350704e-07,8.91243602974684e-07,8.89555641605414e-07,8.87872471044192e-07,8.86194073178324e-07,8.84520429980632e-07,8.8285152350897e-07,8.81187335905748e-07,8.79527849397451e-07,8.77873046294164e-07,8.76222908989099e-07,8.74577419958134e-07,8.72936561759338e-07,8.71300317032517e-07,8.69668668498748e-07,8.68041598959929e-07,8.66419091298322e-07,8.64801128476103e-07,8.63187693534916e-07,8.61578769595428e-07,8.59974339856889e-07,8.58374387596691e-07,8.56778896169931e-07,8.55187849008984e-07,8.53601229623068e-07,8.52019021597816e-07,8.50441208594857e-07,8.48867774351389e-07,8.4729870267976e-07,8.45733977467054e-07,8.4417358267468e-07,8.42617502337952e-07,8.41065720565691e-07,8.39518221539811e-07,8.37974989514922e-07,8.36436008817925e-07,8.34901263847616e-07,8.33370739074294e-07,8.31844419039359e-07,8.30322288354932e-07,8.2880433170346e-07,8.27290533837335e-07,8.25780879578508e-07,8.2427535381811e-07,8.22773941516073e-07,8.21276627700757e-07,8.19783397468574e-07,8.18294235983618e-07,8.16809128477295e-07,8.15328060247962e-07,8.13851016660556e-07,8.12377983146238e-07,8.10908945202032e-07,8.09443888390465e-07,8.07982798339219e-07,8.06525660740771e-07,8.05072461352049e-07,8.0362318599408e-07,8.02177820551645e-07,8.00736350972935e-07,7.99298763269214e-07,7.97865043514471e-07,7.9643517784509e-07,7.95009152459512e-07,7.93586953617903e-07,7.92168567641821e-07,7.90753980913889e-07,7.89343179877468e-07,7.87936151036332e-07,7.86532880954344e-07,7.85133356255137e-07,7.83737563621794e-07,7.82345489796534e-07,7.80957121580391e-07,7.79572445832908e-07,7.78191449471822e-07,7.76814119472757e-07,7.75440442868915e-07,7.74070406750772e-07,7.72703998265775e-07,7.7134120461804e-07,7.69982013068052e-07,7.68626410932369e-07,7.67274385583323e-07,7.6592592444873e-07,7.64581015011595e-07,7.6323964480982e-07,7.6190180143592e-07,7.60567472536733e-07,7.59236645813134e-07,7.57909309019755e-07,7.56585449964698e-07,7.55265056509263e-07,7.5394811656766e-07,7.5263461810674e-07,7.51324549145719e-07,7.500178977559e-07,7.48714652060408e-07,7.47414800233914e-07,7.46118330502372e-07,7.4482523114275e-07,7.43535490482762e-07,7.42249096900612e-07,7.40966038824725e-07,7.39686304733491e-07,7.38409883155003e-07,7.37136762666805e-07,7.3586693189563e-07,7.34600379517153e-07,7.3333709425573e-07,7.32077064884157e-07,7.30820280223411e-07,7.29566729142411e-07,7.28316400557762e-07,7.27069283433519e-07,7.25825366780938e-07,7.24584639658236e-07,7.23347091170348e-07,7.22112710468693e-07,7.20881486750928e-07,7.19653409260722e-07,7.18428467287513e-07,7.17206650166275e-07,7.15987947277293e-07,7.14772348045922e-07,7.13559841942367e-07,7.12350418481447e-07,7.11144067222376e-07,7.09940777768531e-07,7.08740539767232e-07,7.07543342909517e-07,7.06349176929923e-07,7.05158031606264e-07,7.03969896759412e-07,7.02784762253083e-07,7.01602617993616e-07,7.00423453929762e-07,6.99247260052466e-07,6.98074026394659e-07,6.96903743031047e-07,6.95736400077897e-07,6.94571987692829e-07,6.93410496074614e-07,6.9225191546296e-07,6.91096236138314e-07,6.89943448421653e-07,6.88793542674283e-07,6.8764650929764e-07,6.86502338733085e-07,6.85361021461708e-07,6.84222548004131e-07,6.83086908920307e-07,6.81954094809328e-07,6.80824096309229e-07,6.79696904096797e-07,6.78572508887373e-07,6.77450901434666e-07,6.76332072530563e-07,6.75216013004935e-07,6.74102713725454e-07,6.72992165597406e-07,6.71884359563501e-07,6.70779286603692e-07,6.69676937734993e-07,6.6857730401129e-07,6.67480376523166e-07,6.66386146397719e-07,6.65294604798377e-07,6.6420574292473e-07,6.63119552012343e-07,6.62036023332585e-07,6.6095514819245e-07,6.59876917934387e-07,6.58801323936124e-07,6.57728357610495e-07,6.56658010405271e-07,6.55590273802985e-07,6.54525139320771e-07,6.53462598510185e-07,6.52402642957046e-07,6.51345264281265e-07,6.5029045413668e-07,6.49238204210893e-07,6.48188506225103e-07,6.47141351933948e-07,6.46096733125338e-07,6.45054641620297e-07,6.44015069272802e-07,6.42978007969625e-07,6.41943449630173e-07,6.4091138620633e-07,6.39881809682304e-07,6.38854712074468e-07,6.37830085431205e-07,6.36807921832757e-07,6.35788213391071e-07,6.34770952249646e-07,6.33756130583379e-07,6.32743740598422e-07,6.31733774532024e-07,6.30726224652387e-07,6.29721083258519e-07,6.28718342680082e-07,6.27717995277249e-07,6.26720033440561e-07,6.25724449590774e-07,6.24731236178725e-07,6.23740385685183e-07,6.22751890620705e-07,6.21765743525502e-07,6.20781936969291e-07,6.19800463551158e-07,6.18821315899418e-07,6.17844486671479e-07,6.16869968553701e-07,6.1589775426126e-07,6.14927836538014e-07,6.13960208156365e-07,6.12994861917125e-07,6.12031790649384e-07,6.11070987210374e-07,6.10112444485338e-07,6.09156155387399e-07,6.08202112857426e-07,6.07250309863909e-07,6.06300739402824e-07,6.05353394497507e-07,6.04408268198526e-07,6.03465353583552e-07,6.02524643757233e-07,6.01586131851069e-07,6.00649811023285e-07,5.99715674458708e-07,5.9878371536864e-07,5.97853926990738e-07,5.96926302588891e-07,5.96000835453094e-07,5.95077518899332e-07,5.94156346269457e-07,5.93237310931067e-07,5.92320406277387e-07,5.91405625727151e-07,5.90492962724486e-07,5.89582410738789e-07,5.88673963264615e-07,5.8776761382156e-07,5.86863355954142e-07,5.85961183231691e-07,5.85061089248232e-07,5.84163067622372e-07,5.83267111997184e-07,5.823732160401e-07,5.81481373442795e-07,5.80591577921077e-07,5.79703823214776e-07,5.78818103087633e-07,5.77934411327194e-07,5.77052741744696e-07,5.76173088174964e-07,5.75295444476297e-07,5.74419804530366e-07,5.73546162242107e-07,5.72674511539611e-07,5.71804846374023e-07,5.70937160719435e-07,5.70071448572779e-07,5.69207703953729e-07,5.68345920904594e-07,5.67486093490215e-07,5.66628215797864e-07,5.65772281937142e-07,5.64918286039879e-07,5.6406622226003e-07,5.63216084773579e-07,5.62367867778438e-07,5.61521565494347e-07,5.60677172162777e-07,5.5983468204683e-07,5.58994089431144e-07,5.58155388621795e-07,5.573185739462e-07,5.56483639753022e-07,5.55650580412074e-07,5.54819390314225e-07,5.53990063871304e-07,5.53162595516007e-07,5.52336979701804e-07,5.51513210902845e-07,5.5069128361387e-07,5.49871192350111e-07,5.49052931647209e-07,5.48236496061117e-07,5.47421880168009e-07,5.46609078564196e-07,5.4579808586603e-07,5.44988896709816e-07,5.44181505751728e-07,5.43375907667713e-07,5.42572097153412e-07,5.41770068924065e-07,5.40969817714428e-07,5.40171338278687e-07,5.39374625390371e-07,5.38579673842264e-07,5.37786478446325e-07,5.36995034033601e-07,5.3620533545414e-07,5.35417377576911e-07,5.3463115528972e-07,5.33846663499126e-07,5.33063897130359e-07,5.32282851127238e-07,5.31503520452088e-07,5.30725900085662e-07,5.29949985027058e-07,5.29175770293636e-07,5.28403250920945e-07,5.27632421962635e-07,5.26863278490387e-07,5.26095815593824e-07,5.25330028380441e-07,5.24565911975524e-07,5.23803461522071e-07,5.23042672180718e-07,5.22283539129657e-07,5.21526057564567e-07,5.20770222698531e-07,5.20016029761966e-07,5.19263474002542e-07,5.18512550685112e-07,5.17763255091637e-07,5.17015582521107e-07,5.16269528289475e-07,5.15525087729577e-07,5.14782256191062e-07,5.14041029040318e-07,5.13301401660404e-07,5.12563369450972e-07,5.11826927828197e-07,5.11092072224711e-07,5.10358798089525e-07,5.09627100887963e-07,5.0889697610159e-07,5.08168419228145e-07,5.07441425781467e-07,5.06715991291429e-07,5.0599211130387e-07,5.05269781380524e-07,5.04548997098954e-07,5.03829754052483e-07,5.03112047850129e-07,5.02395874116535e-07,5.01681228491903e-07,5.00968106631928e-07,5.00256504207735e-07,4.99546416905808e-07,4.98837840427927e-07,4.98130770491105e-07,4.9742520282752e-07,4.96721133184452e-07,4.9601855732422e-07,4.95317471024115e-07,4.94617870076341e-07,4.93919750287948e-07,4.93223107480772e-07,4.92527937491369e-07,4.91834236170959e-07,4.91141999385356e-07,4.90451223014913e-07,4.89761902954456e-07,4.89074035113228e-07,4.88387615414823e-07,4.87702639797131e-07,4.87019104212271e-07,4.8633700462654e-07,4.85656337020345e-07,4.84977097388148e-07,4.84299281738409e-07,4.83622886093523e-07,4.82947906489764e-07,4.82274338977226e-07,4.81602179619766e-07,4.80931424494948e-07,4.80262069693981e-07,4.79594111321666e-07,4.78927545496341e-07,4.78262368349818e-07,4.77598576027334e-07,4.7693616468749e-07,4.76275130502199e-07,4.75615469656628e-07,4.74957178349145e-07,4.74300252791263e-07,4.73644689207584e-07,4.72990483835751e-07,4.72337632926384e-07,4.71686132743038e-07,4.71035979562137e-07,4.70387169672933e-07,4.69739699377444e-07,4.69093564990405e-07,4.68448762839215e-07,4.67805289263887e-07,4.67163140616991e-07,4.66522313263607e-07,4.65882803581271e-07,4.65244607959927e-07,4.64607722801871e-07,4.63972144521704e-07,4.63337869546282e-07,4.62704894314661e-07,4.62073215278054e-07,4.61442828899776e-07,4.60813731655195e-07,4.60185920031687e-07,4.5955939052858e-07,4.58934139657113e-07,4.5831016394038e-07,4.57687459913287e-07,4.57066024122502e-07,4.56445853126406e-07,4.55826943495048e-07,4.55209291810096e-07,4.54592894664787e-07,4.53977748663888e-07,4.53363850423639e-07,4.52751196571715e-07,4.52139783747176e-07,4.51529608600418e-07,4.50920667793135e-07,4.50312957998266e-07,4.49706475899951e-07,4.49101218193491e-07,4.48497181585296e-07,4.47894362792842e-07,4.47292758544632e-07,4.46692365580142e-07,4.46093180649786e-07,4.45495200514867e-07,4.44898421947533e-07,4.44302841730736e-07,4.43708456658186e-07,4.43115263534312e-07,4.42523259174213e-07,4.41932440403619e-07,4.41342804058852e-07,4.40754346986773e-07,4.40167066044752e-07,4.39580958100618e-07,4.3899602003262e-07,4.38412248729385e-07,4.37829641089877e-07,4.37248194023357e-07,4.36667904449338e-07,4.36088769297548e-07,4.35510785507889e-07,4.34933950030396e-07,4.34358259825193e-07,4.33783711862462e-07,4.33210303122392e-07,4.3263803059515e-07,4.32066891280833e-07,4.31496882189433e-07,4.30928000340799e-07,4.30360242764592e-07,4.29793606500254e-07,4.29228088596964e-07,4.28663686113602e-07,4.28100396118709e-07,4.27538215690451e-07,4.26977141916579e-07,4.26417171894393e-07,4.25858302730705e-07,4.25300531541797e-07,4.24743855453392e-07,4.24188271600608e-07,4.23633777127927e-07,4.23080369189158e-07,4.22528044947397e-07,4.21976801574993e-07,4.21426636253513e-07,4.20877546173704e-07,4.20329528535457e-07,4.19782580547773e-07,4.19236699428725e-07,4.18691882405426e-07,4.1814812671399e-07,4.17605429599501e-07,4.17063788315973e-07,4.16523200126322e-07,4.15983662302324e-07,4.15445172124587e-07,4.14907726882511e-07,4.1437132387426e-07,4.13835960406722e-07,4.1330163379548e-07,4.12768341364776e-07,4.12236080447478e-07,4.11704848385046e-07,4.11174642527499e-07,4.10645460233384e-07,4.10117298869739e-07,4.09590155812066e-07,4.09064028444292e-07,4.08538914158741e-07,4.080148103561e-07,4.07491714445387e-07,4.0696962384392e-07,4.06448535977282e-07,4.05928448279294e-07,4.05409358191981e-07,4.04891263165537e-07,4.04374160658302e-07,4.03858048136721e-07,4.03342923075322e-07,4.02828782956679e-07,4.02315625271384e-07,4.01803447518015e-07,4.01292247203107e-07,4.00782021841119e-07,4.00272768954409e-07,3.99764486073197e-07,3.99257170735541e-07,3.98750820487303e-07,3.98245432882123e-07,3.97741005481385e-07,3.97237535854194e-07,3.96735021577338e-07,3.96233460235268e-07,3.95732849420063e-07,3.95233186731401e-07,3.94734469776535e-07,3.94236696170259e-07,3.93739863534884e-07,3.93243969500205e-07,3.92749011703477e-07,3.92254987789385e-07,3.91761895410014e-07,3.91269732224826e-07,3.90778495900626e-07,3.90288184111542e-07,3.89798794538988e-07,3.89310324871646e-07,3.88822772805432e-07,3.88336136043473e-07,3.87850412296076e-07,3.87365599280706e-07,3.86881694721955e-07,3.86398696351515e-07,3.85916601908157e-07,3.85435409137698e-07,3.84955115792978e-07,3.84475719633834e-07,3.83997218427071e-07,3.8351960994644e-07,3.8304289197261e-07,3.82567062293141e-07,3.82092118702461e-07,3.81618059001837e-07,3.81144880999355e-07,3.80672582509889e-07,3.80201161355078e-07,3.79730615363302e-07,3.79260942369655e-07,3.78792140215922e-07,3.78324206750554e-07,3.7785713982864e-07,3.77390937311886e-07,3.76925597068592e-07,3.76461116973621e-07,3.75997494908383e-07,3.75534728760803e-07,3.75072816425304e-07,3.74611755802777e-07,3.74151544800563e-07,3.73692181332422e-07,3.73233663318517e-07,3.72775988685386e-07,3.72319155365919e-07,3.71863161299334e-07,3.71408004431159e-07,3.709536827132e-07,3.70500194103527e-07,3.70047536566442e-07,3.69595708072466e-07,3.69144706598308e-07,3.68694530126847e-07,3.68245176647107e-07,3.67796644154236e-07,3.67348930649483e-07,3.66902034140177e-07,3.66455952639703e-07,3.66010684167479e-07,3.6556622674894e-07,3.65122578415507e-07,3.64679737204572e-07,3.64237701159476e-07,3.63796468329482e-07,3.63356036769761e-07,3.62916404541365e-07,3.62477569711206e-07,3.62039530352038e-07,3.61602284542434e-07,3.61165830366765e-07,3.60730165915176e-07,3.60295289283573e-07,3.59861198573592e-07,3.59427891892589e-07,3.58995367353608e-07,3.58563623075372e-07,3.58132657182253e-07,3.57702467804256e-07,3.57273053077e-07,3.56844411141695e-07,3.56416540145122e-07,3.55989438239615e-07,3.55563103583041e-07,3.55137534338776e-07,3.54712728675692e-07,3.5428868476813e-07,3.53865400795886e-07,3.53442874944189e-07,3.53021105403683e-07,3.52600090370405e-07,3.52179828045768e-07,3.51760316636541e-07,3.51341554354831e-07,3.50923539418062e-07,3.50506270048956e-07,3.50089744475517e-07,3.49673960931009e-07,3.49258917653939e-07,3.48844612888039e-07,3.48431044882244e-07,3.48018211890678e-07,3.47606112172631e-07,3.47194743992545e-07,3.46784105619993e-07,3.46374195329662e-07,3.45965011401334e-07,3.45556552119868e-07,3.45148815775184e-07,3.44741800662242e-07,3.44335505081025e-07,3.43929927336524e-07,3.43525065738718e-07,3.43120918602555e-07,3.42717484247937e-07,3.42314760999702e-07,3.41912747187606e-07,3.41511441146306e-07,3.41110841215343e-07,3.40710945739123e-07,3.40311753066904e-07,3.39913261552774e-07,3.39515469555638e-07,3.39118375439198e-07,3.3872197757194e-07,3.38326274327113e-07,3.37931264082715e-07,3.37536945221475e-07,3.37143316130838e-07,3.36750375202946e-07,3.36358120834626e-07,3.35966551427368e-07,3.35575665387313e-07,3.35185461125235e-07,3.34795937056524e-07,3.34407091601174e-07,3.34018923183761e-07,3.33631430233432e-07,3.33244611183886e-07,3.3285846447336e-07,3.32472988544613e-07,3.32088181844908e-07,3.31704042826002e-07,3.31320569944122e-07,3.30937761659958e-07,3.30555616438641e-07,3.30174132749734e-07,3.29793309067208e-07,3.29413143869436e-07,3.29033635639172e-07,3.28654782863536e-07,3.28276584034004e-07,3.27899037646386e-07,3.27522142200815e-07,3.27145896201733e-07,3.26770298157874e-07,3.26395346582248e-07,3.26021039992131e-07,3.25647376909045e-07,3.25274355858748e-07,3.24901975371217e-07,3.24530233980632e-07,3.24159130225366e-07,3.23788662647965e-07,3.2341882979514e-07,3.23049630217749e-07,3.2268106247078e-07,3.22313125113345e-07,3.21945816708658e-07,3.21579135824024e-07,3.21213081030826e-07,3.20847650904511e-07,3.20482844024574e-07,3.20118658974546e-07,3.1975509434198e-07,3.19392148718436e-07,3.1902982069947e-07,3.18668108884618e-07,3.18307011877384e-07,3.17946528285224e-07,3.17586656719536e-07,3.17227395795645e-07,3.16868744132789e-07,3.16510700354108e-07,3.16153263086628e-07,3.15796430961248e-07,3.1544020261273e-07,3.15084576679683e-07,3.14729551804551e-07,3.143751266336e-07,3.14021299816904e-07,3.13668070008337e-07,3.1331543586555e-07,3.12963396049971e-07,3.12611949226782e-07,3.12261094064911e-07,3.1191082923702e-07,3.1156115341949e-07,3.11212065292409e-07,3.10863563539562e-07,3.10515646848415e-07,3.10168313910106e-07,3.0982156341943e-07,3.09475394074827e-07,3.09129804578373e-07,3.08784793635763e-07,3.08440359956303e-07,3.08096502252896e-07,3.07753219242029e-07,3.07410509643764e-07,3.07068372181723e-07,3.06726805583078e-07,3.06385808578539e-07,3.06045379902341e-07,3.05705518292232e-07,3.05366222489466e-07,3.05027491238785e-07,3.04689323288409e-07,3.04351717390029e-07,3.04014672298788e-07,3.03678186773278e-07,3.0334225957552e-07,3.03006889470959e-07,3.02672075228449e-07,3.02337815620246e-07,3.02004109421989e-07,3.01670955412699e-07,3.01338352374757e-07,3.01006299093904e-07,3.00674794359219e-07,3.00343836963116e-07,3.00013425701331e-07,2.99683559372907e-07,2.99354236780189e-07,2.99025456728811e-07,2.98697218027681e-07,2.98369519488979e-07,2.98042359928135e-07,2.97715738163831e-07,2.97389653017978e-07,2.97064103315714e-07,2.9673908788539e-07,2.96414605558561e-07,2.96090655169972e-07,2.95767235557553e-07,2.95444345562403e-07,2.95121984028783e-07,2.94800149804106e-07,2.94478841738924e-07,2.94158058686921e-07,2.938377995049e-07,2.93518063052772e-07,2.93198848193552e-07,2.92880153793342e-07,2.92561978721323e-07,2.92244321849747e-07,2.91927182053926e-07,2.91610558212219e-07,2.91294449206026e-07,2.90978853919779e-07,2.90663771240928e-07,2.90349200059931e-07,2.9003513927025e-07,2.89721587768336e-07,2.89408544453622e-07,2.8909600822851e-07,2.88783977998366e-07,2.88472452671506e-07,2.88161431159192e-07,2.87850912375615e-07,2.87540895237893e-07,2.87231378666054e-07,2.86922361583035e-07,2.86613842914667e-07,2.86305821589664e-07,2.85998296539622e-07,2.85691266698999e-07,2.85384731005116e-07,2.8507868839814e-07,2.84773137821079e-07,2.84468078219771e-07,2.84163508542876e-07,2.83859427741867e-07,2.8355583477102e-07,2.83252728587406e-07,2.82950108150881e-07,2.82647972424078e-07,2.82346320372398e-07,2.82045150964001e-07,2.81744463169796e-07,2.81444255963435e-07,2.81144528321302e-07,2.80845279222503e-07,2.80546507648862e-07,2.80248212584908e-07,2.79950393017868e-07,2.79653047937658e-07,2.79356176336875e-07,2.79059777210788e-07,2.7876384955733e-07,2.78468392377089e-07,2.781734046733e-07,2.77878885451835e-07,2.77584833721198e-07,2.77291248492514e-07,2.7699812877952e-07,2.76705473598559e-07,2.76413281968571e-07,2.76121552911084e-07,2.75830285450207e-07,2.75539478612622e-07,2.75249131427571e-07,2.74959242926858e-07,2.74669812144829e-07,2.74380838118375e-07,2.74092319886915e-07,2.73804256492394e-07,2.73516646979272e-07,2.73229490394517e-07,2.72942785787597e-07,2.72656532210473e-07,2.7237072871759e-07,2.72085374365869e-07,2.71800468214701e-07,2.71516009325936e-07,2.71231996763879e-07,2.70948429595282e-07,2.7066530688933e-07,2.70382627717644e-07,2.70100391154265e-07,2.69818596275648e-07,2.69537242160658e-07,2.69256327890558e-07,2.68975852549005e-07,2.68695815222041e-07,2.68416214998085e-07,2.68137050967926e-07,2.67858322224716e-07,2.67580027863963e-07,2.67302166983523e-07,2.67024738683592e-07,2.667477420667e-07,2.66471176237704e-07,2.66195040303779e-07,2.65919333374412e-07,2.65644054561395e-07,2.65369202978817e-07,2.65094777743058e-07,2.64820777972781e-07,2.64547202788925e-07,2.64274051314698e-07,2.6400132267557e-07,2.63729015999267e-07,2.63457130415763e-07,2.63185665057272e-07,2.62914619058243e-07,2.62643991555352e-07,2.62373781687497e-07,2.62103988595787e-07,2.61834611423541e-07,2.61565649316275e-07,2.61297101421699e-07,2.61028966889712e-07,2.60761244872389e-07,2.60493934523981e-07,2.60227035000903e-07,2.59960545461732e-07,2.59694465067196e-07,2.5942879298017e-07,2.59163528365671e-07,2.58898670390847e-07,2.58634218224973e-07,2.58370171039444e-07,2.58106528007771e-07,2.57843288305571e-07,2.5758045111056e-07,2.57318015602551e-07,2.57055980963445e-07,2.56794346377222e-07,2.56533111029941e-07,2.56272274109728e-07,2.56011834806771e-07,2.55751792313316e-07,2.55492145823657e-07,2.55232894534136e-07,2.54974037643127e-07,2.54715574351041e-07,2.54457503860311e-07,2.54199825375389e-07,2.53942538102742e-07,2.53685641250842e-07,2.53429134030164e-07,2.53173015653175e-07,2.52917285334334e-07,2.52661942290079e-07,2.52406985738827e-07,2.52152414900967e-07,2.51898228998849e-07,2.51644427256785e-07,2.51391008901038e-07,2.51137973159819e-07,2.50885319263279e-07,2.50633046443508e-07,2.50381153934519e-07,2.50129640972254e-07,2.49878506794571e-07,2.4962775064124e-07,2.49377371753937e-07,2.49127369376239e-07,2.48877742753617e-07,2.48628491133433e-07,2.48379613764931e-07,2.48131109899234e-07,2.47882978789334e-07,2.47635219690095e-07,2.47387831858237e-07,2.47140814552337e-07,2.46894167032824e-07,2.46647888561968e-07,2.4640197840388e-07,2.46156435824504e-07,2.45911260091611e-07,2.45666450474795e-07,2.45422006245467e-07,2.45177926676849e-07,2.4493421104397e-07,2.44690858623658e-07,2.44447868694538e-07,2.44205240537025e-07,2.43962973433317e-07,2.43721066667394e-07,2.43479519525008e-07,2.43238331293681e-07,2.42997501262697e-07,2.427570287231e-07,2.42516912967687e-07,2.42277153291001e-07,2.4203774898933e-07,2.41798699360699e-07,2.41560003704864e-07,2.4132166132331e-07,2.41083671519243e-07,2.40846033597588e-07,2.4060874686498e-07,2.40371810629761e-07,2.40135224201976e-07,2.39898986893366e-07,2.39663098017364e-07,2.39427556889092e-07,2.3919236282535e-07,2.38957515144618e-07,2.38723013167047e-07,2.38488856214456e-07,2.38255043610324e-07,2.3802157467979e-07,2.37788448749643e-07,2.37555665148322e-07,2.37323223205906e-07,2.37091122254116e-07,2.36859361626301e-07,2.36627940657443e-07,2.36396858684145e-07,2.36166115044629e-07,2.35935709078732e-07,2.35705640127899e-07,2.35475907535182e-07,2.35246510645231e-07,2.35017448804291e-07,2.34788721360199e-07,2.34560327662378e-07,2.34332267061831e-07,2.34104538911139e-07,2.33877142564455e-07,2.33650077377499e-07,2.33423342707555e-07,2.33196937913465e-07,2.32970862355623e-07,2.32745115395976e-07,2.32519696398014e-07,2.32294604726767e-07,2.32069839748802e-07,2.31845400832217e-07,2.31621287346638e-07,2.31397498663211e-07,2.31174034154603e-07,2.30950893194994e-07,2.30728075160074e-07,2.30505579427036e-07,2.30283405374577e-07,2.30061552382886e-07,2.29840019833649e-07,2.29618807110036e-07,2.29397913596703e-07,2.29177338679783e-07,2.28957081746886e-07,2.2873714218709e-07,2.28517519390943e-07,2.28298212750453e-07,2.28079221659086e-07,2.27860545511761e-07,2.2764218370485e-07,2.27424135636167e-07,2.2720640070497e-07,2.26988978311951e-07,2.26771867859237e-07,2.26555068750385e-07,2.26338580390375e-07,2.26122402185609e-07,2.25906533543904e-07,2.25690973874492e-07,2.25475722588012e-07,2.25260779096507e-07,2.25046142813424e-07,2.24831813153601e-07,2.24617789533274e-07,2.24404071370065e-07,2.24190658082979e-07,2.23977549092406e-07,2.23764743820109e-07,2.23552241689226e-07,2.23340042124262e-07,2.2312814455109e-07,2.2291654839694e-07,2.22705253090402e-07,2.2249425806142e-07,2.22283562741286e-07,2.22073166562639e-07,2.21863068959457e-07};
-
-//
-// Encrypt the context again
-//
-static void
-_aes_randomize_context(char *context)
-{
- AES_KEY key;
-
- AES_set_encrypt_key((unsigned char *) "0123456789012345", 128, &key);
-
- AES_encrypt((unsigned char *) context + 4,
- (unsigned char *) context + 4,
- &key);
- ( (int *) context )[0] = 1;
-}
-
-//
-// Encrypt the buffer of ctx and set its counter to 0
-//
-static void
-aes_randomize_context(VALUE self)
-{
- rb_str_modify(self);
- _aes_randomize_context(RSTRING(self)->ptr);
-}
-
-//
-// Fill the buffer of context with seed and randomize it.
-//
-static VALUE
-aes_seed_context(VALUE self, VALUE seed)
-{
- int tmp;
-
- if (RSTRING(self)->len < 17)
- rb_str_concat(self, rb_str_new(0, 17));
-
- rb_str_modify(self);
- for (tmp = 1; tmp < 5; tmp++)
- ( (int *) RSTRING(self)->ptr )[tmp] = NUM2INT(seed);
-
- aes_randomize_context(self);
-
- return seed;
-}
-
-//
-// Return an int that is the next random in the context
-//
-static int
-_aes_random(char *context)
-{
- int *int_context = (int *) context;
- if (int_context[0] < 5) {
- int return_value = int_context[int_context[0]];
- int_context[0]++;
- return return_value;
- } else {
- _aes_randomize_context(context);
- return _aes_random(context);
- }
-}
-
-//
-// Get the int at the counter of context in the buffer of context.
-//
-static VALUE
-aes_random(int argc, VALUE *argv, VALUE self)
-{
- int return_value;
-
- rb_str_modify(self);
- return_value = _aes_random(RSTRING(self)->ptr);
-
- if (argc == 0) {
- return rb_float_new( ( (double) ( (unsigned int) return_value ) ) / ( (double) UINT_MAX ));
- } else {
- VALUE max = argv[0];
- return INT2NUM(( (unsigned int) return_value ) % NUM2INT(max));
- }
-}
-
-//
-// Initialize a context.
-//
-static VALUE
-context_initialize(int argc, VALUE *argv, VALUE self)
-{
- rb_str_concat(self, rb_str_new(0, 17));
- return self;
-}
-
-//
-// Return self ^ subject
-//
-static VALUE
-string_xor(VALUE self, VALUE subject)
-{
- int tmp;
- int len = RSTRING(self)->len;
- VALUE return_value = rb_str_new(0, len);
-
- Check_Type(subject, T_STRING);
-
- if (len != RSTRING(subject)->len)
- rb_raise(rb_eTypeError, "argument to xor must be of same length as receiver");
-
- rb_str_modify(return_value);
- for (tmp = 0; tmp < len / 4; tmp++) {
- ( (int *) RSTRING(return_value)->ptr )[tmp] = ( (int *) RSTRING(self)->ptr )[tmp] ^ ( (int *) RSTRING(subject)->ptr )[tmp];
- }
- for (tmp = 0; tmp < (len % 4); tmp++) {
- RSTRING(return_value)->ptr[len - tmp - 1] = RSTRING(self)->ptr[len - tmp - 1] ^ RSTRING(subject)->ptr[len - tmp - 1];
- }
-
- return return_value;
-}
-
-//
-// Make str into str ^ subject
-//
-static void
-_string_xor2(char *str, char *subject, int len)
-{
- int tmp;
-
- for (tmp = 0; tmp < len / 4; tmp++) {
- ( (int *) str )[tmp] = ( (int *) str )[tmp] ^ ( (int *) subject )[tmp];
- }
- for (tmp = 0; tmp < (len % 4); tmp++) {
- str[len - tmp - 1] = str[len - tmp - 1] ^ subject[len - tmp - 1];
- }
-}
-
-//
-// Make self into self ^ subject
-//
-static VALUE
-string_xor2(VALUE self, VALUE subject)
-{
- int tmp;
- int len = RSTRING(self)->len;
-
- Check_Type(subject, T_STRING);
-
- if (len != RSTRING(subject)->len)
- rb_raise(rb_eTypeError, "argument to xor must be of same length as receiver");
-
- rb_str_modify(self);
- _string_xor2(RSTRING(self)->ptr, RSTRING(subject)->ptr, len);
-
- return self;
-}
-
-//
-// Get a random degree
-//
-static VALUE
-superstring_get_degree(VALUE self, VALUE context)
-{
- double f;
- int tmp = 0;
-
- rb_str_modify(context);
- f = ( (double) ( (unsigned int) _aes_random(RSTRING(context)->ptr) ) ) / ( (double) UINT_MAX );
- while (f > 0) {
- tmp++;
- f = f - P[tmp];
- }
- return INT2NUM(tmp);
-}
-
-//
-// Use context to check if the degree blocks adjacent to block
-// can lead to anything good.
-//
-static VALUE
-superstring_decode_multiple(VALUE self, VALUE block, VALUE degree, VALUE context, VALUE blocks, VALUE nr_of_blocks)
-{
- int tmp;
- int got_new_block = 0;
- int missing_blocks = NUM2INT(degree);
- int _nr_of_blocks = NUM2INT(nr_of_blocks);
- int missing_block_nr;
- char xor_sum[RSTRING(block)->len];
- VALUE get_func = rb_intern("[]");
- VALUE this_block;
-
- memcpy(xor_sum, RSTRING(block)->ptr, RSTRING(block)->len);
-
- for (tmp = 0; tmp < NUM2INT(degree); tmp++) {
- rb_str_modify(context);
- int block_nr = ( (unsigned int) _aes_random(RSTRING(context)->ptr) ) % _nr_of_blocks;
-
- if ((this_block = rb_funcall(blocks, get_func, 1, INT2NUM(block_nr))) != Qnil) {
- _string_xor2(xor_sum, RSTRING(this_block)->ptr, RSTRING(this_block)->len);
- missing_blocks--;
- } else {
- missing_block_nr = block_nr;
- }
- }
-
- if (missing_blocks == 1) {
- rb_funcall(blocks, rb_intern("[]="), 2, INT2NUM(missing_block_nr), rb_str_new(xor_sum, RSTRING(block)->len));
- got_new_block = 1;
- }
-
- return got_new_block ? Qtrue : Qfalse;
-}
-
-
-VALUE rb_superstring;
-VALUE rb_context;
-
-#ifdef __cplusplus
-extern "C" {
-#endif
- void Init_superstring_ext() {
- VALUE string = rb_const_get(rb_cObject, rb_intern("String"));
- rb_superstring = rb_define_class_under(rb_define_module("Oneliner"),
- "SuperString",
- string);
- rb_context = rb_define_class_under(rb_superstring,
- "Context",
- string);
-
- rb_define_const(rb_context, "INT_MAX", INT2NUM(INT_MAX));
- rb_define_method(rb_context, "initialize", context_initialize, -1);
- rb_define_method(rb_context, "random", aes_random, -1);
- rb_define_method(rb_context, "seed", aes_seed_context, 1);
- rb_define_method(string, "^", string_xor, 1);
- rb_define_method(string, "^=", string_xor2, 1);
- rb_define_method(rb_superstring, "get_degree", superstring_get_degree, 1);
- rb_define_method(rb_superstring, "decode_multiple", superstring_decode_multiple, 5);
- }
-#ifdef __cplusplus
-}
-#endif
-
Modified: trunk/oneliner/lib/oneliner/superstring.rb
===================================================================
--- trunk/oneliner/lib/oneliner/superstring.rb 2007-03-13 13:48:20 UTC (rev 249)
+++ trunk/oneliner/lib/oneliner/superstring.rb 2007-03-15 14:49:36 UTC (rev 250)
@@ -144,7 +144,7 @@
bytes_per_block = @block_size / 8
rval = s
rval += "\000" * (bytes_per_block - (rval.size % bytes_per_block)) if (rval.size % bytes_per_block) > 0
- return rval.gsub(/(.{#{bytes_per_block},#{bytes_per_block}})/, ",\\1").split(/,/)[1..-1]
+ return rval.hack(bytes_per_block)
end
end
@@ -324,6 +324,7 @@
context = Context.new
context.seed(@nr_of_data_blocks)
+ b_size = nil
@blocks.each_with_index do |b, i|
1.upto(Q) do
aux_block_nr = context.random(@nr_of_aux_blocks)
Modified: trunk/oneliner/lib/oneliner.rb
===================================================================
--- trunk/oneliner/lib/oneliner.rb 2007-03-13 13:48:20 UTC (rev 249)
+++ trunk/oneliner/lib/oneliner.rb 2007-03-15 14:49:36 UTC (rev 250)
@@ -17,5 +17,5 @@
$: << File.dirname(File.expand_path(__FILE__))
-require 'superstring_ext'
+require 'oneliner_ext'
require 'oneliner/superstring'
Modified: trunk/oneliner/tests/superstring_test.rb
===================================================================
--- trunk/oneliner/tests/superstring_test.rb 2007-03-13 13:48:20 UTC (rev 249)
+++ trunk/oneliner/tests/superstring_test.rb 2007-03-15 14:49:36 UTC (rev 250)
@@ -11,6 +11,29 @@
end
end
+ def test_hack
+ 20.times do
+ size = rand(1024)
+ s = "x" * size
+ bites = rand(128)
+ l = s.hack(bites)
+ assert_equal((size.to_f / bites).ceil, l.size)
+ assert_equal(s, l.join)
+ l.each_with_index do |e, index|
+ if index == l.size - 1
+ assert(e.size <= bites)
+ else
+ assert_equal(bites, e.size, "element #{index} of #{l.inspect} should be of size #{bites}")
+ end
+ end
+ end
+ end
+
+ def test_weird_string
+ s = Oneliner::SuperString.new(open(File.join(File.dirname(__FILE__), 'weird_string')).read)
+ s.encode(20)
+ end
+
private
def size_test(size)
Added: trunk/oneliner/tests/weird_string
===================================================================
--- trunk/oneliner/tests/weird_string (rev 0)
+++ trunk/oneliner/tests/weird_string 2007-03-15 14:49:36 UTC (rev 250)
@@ -0,0 +1,473 @@
+--- !ruby/object:Archipelago::Pirate::Captain
+chest_eval_files:
+- ./script/overloads.rb
+chests_having_evaluated:
+ ?
+ - 3c9722f35eb844bd09cc8563ce359ae9fa209107
+ - 2007-03-15 14:29:26.778436 +01:00
+ : !ruby/object:Set
+ hash:
+ ./script/overloads.rb: true
+
+initial_lookup_timeout: 5
+initial_service_update_interval: 1
+jockey: &id005 !ruby/object:Archipelago::Disco::Jockey
+ incoming: !ruby/object:Queue
+ que: []
+
+ waiting:
+ - &id001 !ruby/object:Thread {}
+
+ initial_lookup_standoff: 0.1
+ listener: !ruby/object:UDPSocket {}
+
+ listener_thread: !ruby/object:Thread {}
+
+ local_services: !ruby/object:Archipelago::Disco::ServiceLocker
+ hash: {}
+
+ jockey: *id005
+ lookup_timeout: 10
+ multiaddress: 234.2.4.2
+ multiport: 25242
+ new_service_semaphore: !ruby/object:MonitorMixin::ConditionVariable
+ monitor: !ruby/object:Archipelago::Current::Lock
+ archipelago_current_synchronized_lock_by_object: {}
+
+ mon_count: 0
+ mon_entering_queue: []
+
+ mon_owner: !ruby/object:Thread {}
+
+ mon_waiting_queue: []
+
+ waiters: []
+
+ outgoing: !ruby/object:Queue
+ que: []
+
+ waiting:
+ - &id012 !ruby/object:Thread {}
+
+ picker_thread: *id001
+ remote_services: !ruby/object:Archipelago::Disco::ServiceLocker
+ hash:
+ 41559f282ebdfc7348a43aba911d05252b74b742: !ruby/object:Archipelago::Disco::Record
+ attributes:
+ :service: &id002 !ruby/object:DRb::DRbObject
+ ref: 9827790
+ uri: druby://dhcp1-150.local.adocca.net:53830
+ :class: Archipelago::Tranny::Manager
+ :validator: *id002
+ :published_at: 2007-03-15 14:29:26.584411 +01:00
+ :service_id: 41559f282ebdfc7348a43aba911d05252b74b742
+ 49d4d1383d4e9ec41c38bdd2659d7041fc9ffbec: !ruby/object:Archipelago::Disco::Record
+ attributes:
+ :service: &id003 !ruby/object:DRb::DRbObject
+ ref: 9527210
+ uri: druby://dhcp1-150.local.adocca.net:53830
+ :class: Archipelago::Dump::Site
+ :validator: *id003
+ :published_at: 2007-03-15 14:29:26.737298 +01:00
+ :service_id: 49d4d1383d4e9ec41c38bdd2659d7041fc9ffbec
+ 3c9722f35eb844bd09cc8563ce359ae9fa209107: !ruby/object:Archipelago::Disco::Record
+ attributes:
+ :service: &id004 !ruby/object:DRb::DRbObject
+ ref: 9521410
+ uri: druby://dhcp1-150.local.adocca.net:53830
+ :class: Archipelago::Treasure::Chest
+ :validator: *id004
+ :published_at: 2007-03-15 14:29:26.778436 +01:00
+ :service_id: 3c9722f35eb844bd09cc8563ce359ae9fa209107
+ jockey: *id005
+ sender: !ruby/object:UDPSocket {}
+
+ service_change_subscribers_by_event_type:
+ :found:
+ ?
+ - !ruby/object:Archipelago::Disco::Query
+ attributes: &id007 !ruby/object:Archipelago::Disco::Query
+ attributes:
+ :class: Archipelago::Dump::Site
+ :unicast_reply: dhcp1-150.local.adocca.net:25244
+ - 11204190
+ : !ruby/object:Proc {}
+
+ ?
+ - !ruby/object:Archipelago::Disco::Query
+ attributes: &id010
+ :class: Archipelago::Treasure::Chest
+ :unicast_reply: dhcp1-150.local.adocca.net:25244
+ - 11399580
+ : !ruby/object:Proc {}
+
+ ?
+ - !ruby/object:Archipelago::Disco::Query
+ attributes: &id011
+ :class: Archipelago::Tranny::Manager
+ :unicast_reply: dhcp1-150.local.adocca.net:25244
+ - 11399580
+ : !ruby/object:Proc {}
+
+ ?
+ - !ruby/object:Archipelago::Disco::Query
+ attributes: &id009
+ :class: Archipelago::Tranny::Manager
+ :unicast_reply: dhcp1-150.local.adocca.net:25244
+ - 11380640
+ : !ruby/object:Proc {}
+
+ ?
+ - !ruby/object:Archipelago::Disco::Query
+ attributes: &id006 !ruby/object:Archipelago::Disco::Query
+ attributes:
+ :class: Archipelago::Dump::Site
+ :unicast_reply: dhcp1-150.local.adocca.net:25244
+ - 11213820
+ : !ruby/object:Proc {}
+
+ ?
+ - !ruby/object:Archipelago::Disco::Query
+ attributes: &id008
+ :class: Archipelago::Treasure::Chest
+ :unicast_reply: dhcp1-150.local.adocca.net:25244
+ - 11380640
+ : !ruby/object:Proc {}
+
+ :lost:
+ ?
+ - !ruby/object:Archipelago::Disco::Query
+ attributes: *id006
+ - 11213820
+ : !ruby/object:Proc {}
+
+ ?
+ - !ruby/object:Archipelago::Disco::Query
+ attributes: *id007
+ - 11204190
+ : !ruby/object:Proc {}
+
+ ?
+ - !ruby/object:Archipelago::Disco::Query
+ attributes: *id008
+ - 11380640
+ : !ruby/object:Proc {}
+
+ ?
+ - !ruby/object:Archipelago::Disco::Query
+ attributes: *id009
+ - 11380640
+ : !ruby/object:Proc {}
+
+ ?
+ - !ruby/object:Archipelago::Disco::Query
+ attributes: *id010
+ - 11399580
+ : !ruby/object:Proc {}
+
+ ?
+ - !ruby/object:Archipelago::Disco::Query
+ attributes: *id011
+ - 11399580
+ : !ruby/object:Proc {}
+
+ shouter_thread: *id012
+ subscribed_services: !ruby/object:Set
+ hash:
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id011
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id011
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id007
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id010
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id011
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id007
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id009
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id009
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id008
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id011
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id010
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id006
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id008
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id008
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id007
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id008
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id007
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id006
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id009
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id009
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id011
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id010
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id008
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id010
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id009
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id011
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id009
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id006
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id009
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id006
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id007
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id009
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id009
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id008
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id011
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id011
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id009
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id009
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id010
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id006
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id010
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id008
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id007
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id006
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id008
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id011
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id007
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id011
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id010
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id007
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id007
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id006
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id006
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id006
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id008
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id007
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id007
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id006
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id010
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id010
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id008
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id011
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id010
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id006
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id008
+ : true
+
+ !ruby/object:Archipelago::Disco::Query ?
+ attributes: *id010
+ : true
+
+ thrifty_caching: true
+ thrifty_publishing: false
+ thrifty_replying: true
+ unicast_address: dhcp1-150.local.adocca.net:25244
+ unilistener: !ruby/object:UDPSocket {}
+
+ unilistener_thread: !ruby/object:Thread {}
+
+ unisender: !ruby/object:UDPSocket {}
+
+ valid: true
+ validation_interval: 30
+ validator_thread: !ruby/object:Thread {}
+
+jockey_options: {}
+
+maximum_service_update_interval: 60
+service_descriptions:
+ :chests: *id008
+ :trannies: *id009
+service_update_thread: !ruby/object:Thread {}
+
+services:
+ :chests: !ruby/object:Archipelago::Disco::ServiceLocker
+ hash: !ruby/object:RBTree {}
+
+ jockey:
+ :trannies: !ruby/object:Archipelago::Disco::ServiceLocker
+ hash: !ruby/object:RBTree {}
+
+ jockey:
+transaction:
+yar_counter: 0
From nobody at rubyforge.org Thu Mar 15 12:05:47 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Thu, 15 Mar 2007 12:05:47 -0400 (EDT)
Subject: [Archipelago-submits] [251]
trunk/oneliner/tests/superstring_benchmark.rb: made the
benchmark truthful :.(
Message-ID: <20070315160547.17C4D5240CA9@rubyforge.org>
Revision: 251
Author: zond
Date: 2007-03-15 12:05:46 -0400 (Thu, 15 Mar 2007)
Log Message:
-----------
made the benchmark truthful :.(
Modified Paths:
--------------
trunk/oneliner/tests/superstring_benchmark.rb
Modified: trunk/oneliner/tests/superstring_benchmark.rb
===================================================================
--- trunk/oneliner/tests/superstring_benchmark.rb 2007-03-15 14:49:36 UTC (rev 250)
+++ trunk/oneliner/tests/superstring_benchmark.rb 2007-03-15 16:05:46 UTC (rev 251)
@@ -18,13 +18,23 @@
private
+ def randstr(size)
+ rval = ""
+ size.times do
+ rval += rand(128).chr
+ end
+ rval
+ end
+
def decode(n)
- s2 = Oneliner::SuperString.new(" " * n)
- bm("#{n} bytes mixed twice", :n => 10) do
+ s2 = Oneliner::SuperString.new(randstr(n))
+ bm("#{n} bytes in 40% chunks", :n => 10) do
s = Oneliner::SuperString.new
- nil while s.decode!(s2.encode((n * 0.4).to_i + 8))
+ nil while !s.decode!(s2.encode((n * 0.4).to_i + 8))
+ end
+ bm("#{n} bytes in 5% chunks", :n => 10) do
s = Oneliner::SuperString.new
- nil while s.decode!(s2.encode((n * 0.05).to_i + 8))
+ nil while !s.decode!(s2.encode((n * 0.05).to_i + 8))
end
end
From nobody at rubyforge.org Thu Mar 15 12:06:33 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Thu, 15 Mar 2007 12:06:33 -0400 (EDT)
Subject: [Archipelago-submits] [252]
trunk/archipelago/tests/sanitation_benchmark.rb: more benchmarks :.(
Message-ID: <20070315160633.54FD85240CAC@rubyforge.org>
Revision: 252
Author: zond
Date: 2007-03-15 12:06:32 -0400 (Thu, 15 Mar 2007)
Log Message:
-----------
more benchmarks :.(
Modified Paths:
--------------
trunk/archipelago/tests/sanitation_benchmark.rb
Modified: trunk/archipelago/tests/sanitation_benchmark.rb
===================================================================
--- trunk/archipelago/tests/sanitation_benchmark.rb 2007-03-15 16:05:46 UTC (rev 251)
+++ trunk/archipelago/tests/sanitation_benchmark.rb 2007-03-15 16:06:32 UTC (rev 252)
@@ -20,14 +20,23 @@
end
def test_set_get
+ do_set_get(8)
+ do_set_get(128)
+ do_set_get(512)
+ do_set_get(1024)
+ end
+
+ private
+
+ def do_set_get(size)
k = "apa"
- v = "gnu"
- bm("Sanitation#[]=", :n => 100) do
+ v = " " * size
+ bm("Sanitation#[]= (#{size})", :n => 100) do
@c[k] = v
k = k.next
end
k = "apa"
- bm("Sanitation#[]", :n => 100) do
+ bm("Sanitation#[] (#{size})", :n => 100) do
t = @c[k]
k = k.next
end
From nobody at rubyforge.org Wed Mar 28 12:00:00 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Wed, 28 Mar 2007 12:00:00 -0400 (EDT)
Subject: [Archipelago-submits] [253] trunk/oneliner: trying to optimize a
bit, dont know if it worked
Message-ID: <20070328160000.7DA505240AF3@rubyforge.org>
Revision: 253
Author: zond
Date: 2007-03-28 12:00:00 -0400 (Wed, 28 Mar 2007)
Log Message:
-----------
trying to optimize a bit, dont know if it worked
Modified Paths:
--------------
trunk/oneliner/ext/oneliner_ext.c
trunk/oneliner/lib/oneliner/superstring.rb
Modified: trunk/oneliner/ext/oneliner_ext.c
===================================================================
--- trunk/oneliner/ext/oneliner_ext.c 2007-03-15 16:06:32 UTC (rev 252)
+++ trunk/oneliner/ext/oneliner_ext.c 2007-03-28 16:00:00 UTC (rev 253)
@@ -218,7 +218,7 @@
// can lead to anything good.
//
static VALUE
-superstring_decode_multiple(VALUE self, VALUE block, VALUE degree, VALUE context, VALUE blocks, VALUE nr_of_blocks)
+superstring_decode_multiple(VALUE self, VALUE block, VALUE degree, VALUE block_numbers, VALUE blocks, VALUE nr_of_blocks)
{
int tmp;
int got_new_block = 0;
@@ -232,8 +232,7 @@
memcpy(xor_sum, RSTRING(block)->ptr, RSTRING(block)->len);
for (tmp = 0; tmp < NUM2INT(degree); tmp++) {
- rb_str_modify(context);
- int block_nr = ( (unsigned int) _aes_random(RSTRING(context)->ptr) ) % _nr_of_blocks;
+ int block_nr = NUM2INT(rb_funcall(block_numbers, get_func, 1, INT2NUM(tmp)));
if ((this_block = rb_funcall(blocks, get_func, 1, INT2NUM(block_nr))) != Qnil) {
_string_xor2(xor_sum, RSTRING(this_block)->ptr, RSTRING(this_block)->len);
Modified: trunk/oneliner/lib/oneliner/superstring.rb
===================================================================
--- trunk/oneliner/lib/oneliner/superstring.rb 2007-03-15 16:06:32 UTC (rev 252)
+++ trunk/oneliner/lib/oneliner/superstring.rb 2007-03-28 16:00:00 UTC (rev 253)
@@ -77,23 +77,18 @@
the_seed = chunk[4..7].unpack("i").first
- chunk_data = {
- :blocks => expand(chunk[8..-1]),
- :context => context,
- :seed => the_seed
- }
-
+ chunk_data = build_chunk_data(chunk, context, the_seed)
+
first_decode = @known_nr_of_blocks < @nr_of_blocks
@known_nr_of_blocks += chunk_data[:blocks].size
@known_chunks.unshift(chunk_data)
if @known_nr_of_blocks > @nr_of_blocks
-
interesting = true
interesting = decode_chunk(chunk_data) unless first_decode
- if interesting
- nil while do_decode(context)
+ while interesting
+ interesting = do_decode(context)
end
if decode_done?
@@ -127,6 +122,23 @@
# Misc stuff
#
+ def build_chunk_data(chunk, context, the_seed)
+ chunk_data = {
+ :blocks => expand(chunk[8..-1]),
+ :block_metadata => []
+ }
+ context.seed(the_seed)
+ chunk_data[:blocks].size.times do |n|
+ chunk_data[:block_metadata][n] = {}
+ chunk_data[:block_metadata][n][:degree] = get_degree(context)
+ chunk_data[:block_metadata][n][:blocks] = []
+ chunk_data[:block_metadata][n][:degree].times do |m|
+ chunk_data[:block_metadata][n][:blocks] << context.random(@nr_of_blocks)
+ end
+ end
+ return chunk_data
+ end
+
def used_blocks
@known_chunks.inject(0) do |sum, chunk_data|
sum + chunk_data[:blocks].size
@@ -189,11 +201,11 @@
@known_chunks.each do |chunk_data|
found_new_block = decode_chunk(chunk_data) || found_new_block
end
- found_new_block = aux_decode(context) || found_new_block
+ found_new_block = aux_decode || found_new_block
return found_new_block
end
- def aux_decode(context)
+ def aux_decode
got_new_block = false
@nr_of_data_blocks.upto(@nr_of_blocks - 1) do |i|
@@ -208,6 +220,7 @@
if @blocks[block_nr].nil?
@blocks[block_nr] = aux_block
got_new_block = true
+ @blocks[i] = nil
end
else
missing_blocks = source_blocks.size
@@ -226,6 +239,7 @@
if missing_blocks == 1
@blocks[missing_block] = xor_sum
got_new_block = true
+ @blocks[i] = nil
end
end
end
@@ -237,29 +251,36 @@
def decode_chunk(chunk_data)
got_new_block = false
- context = chunk_data[:context]
+ blocks = chunk_data[:blocks]
+ blocks.each_with_index do |block, i|
- context.seed(chunk_data[:seed])
+ if block
- chunk_data[:blocks].each_with_index do |block, i|
-
- degree = get_degree(context)
-
- if degree == 1
- block_nr = context.random(@nr_of_blocks)
+ block_metadata = chunk_data[:block_metadata][i]
+ degree = block_metadata[:degree]
- if @blocks[block_nr].nil?
- @blocks[block_nr] = block
- got_new_block = true
+ if degree == 1
+ block_nr = block_metadata[:blocks].first
+
+ if @blocks[block_nr].nil?
+ @blocks[block_nr] = block
+ got_new_block = true
+ blocks[i] = nil
+ end
+ else
+ if decode_multiple(block, degree, block_metadata[:blocks], @blocks, @nr_of_blocks)
+ got_new_block = true
+ blocks[i] = nil
+ end
end
- else
- got_new_block = decode_multiple(block, degree, context, @blocks, @nr_of_blocks) || got_new_block
+
end
+
end
return got_new_block
end
-
+
def generate_aux_hash
rval = Array.new(@nr_of_blocks)
From nobody at rubyforge.org Wed Mar 28 12:04:57 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Wed, 28 Mar 2007 12:04:57 -0400 (EDT)
Subject: [Archipelago-submits] [254]
trunk/oneliner/lib/oneliner/superstring.rb: even more tries
at optimizing
Message-ID: <20070328160457.6D20F5240AF3@rubyforge.org>
Revision: 254
Author: zond
Date: 2007-03-28 12:04:57 -0400 (Wed, 28 Mar 2007)
Log Message:
-----------
even more tries at optimizing
Modified Paths:
--------------
trunk/oneliner/lib/oneliner/superstring.rb
Modified: trunk/oneliner/lib/oneliner/superstring.rb
===================================================================
--- trunk/oneliner/lib/oneliner/superstring.rb 2007-03-28 16:00:00 UTC (rev 253)
+++ trunk/oneliner/lib/oneliner/superstring.rb 2007-03-28 16:04:57 UTC (rev 254)
@@ -85,7 +85,7 @@
if @known_nr_of_blocks > @nr_of_blocks
interesting = true
- interesting = decode_chunk(chunk_data) unless first_decode
+ interesting, informative = decode_chunk(chunk_data) unless first_decode
while interesting
interesting = do_decode(context)
@@ -198,8 +198,9 @@
def do_decode(context)
found_new_block = false
- @known_chunks.each do |chunk_data|
- found_new_block = decode_chunk(chunk_data) || found_new_block
+ @known_chunks.clone.each_with_index do |chunk_data, i|
+ found_new_block, informative = decode_chunk(chunk_data) || found_new_block
+ @known_chunks.delete(i) unless informative
end
found_new_block = aux_decode || found_new_block
return found_new_block
@@ -250,16 +251,18 @@
def decode_chunk(chunk_data)
got_new_block = false
+ got_informative_block = false
blocks = chunk_data[:blocks]
blocks.each_with_index do |block, i|
if block
+ got_informative_block = true
+
block_metadata = chunk_data[:block_metadata][i]
- degree = block_metadata[:degree]
- if degree == 1
+ if (degree = block_metadata[:degree]) == 1
block_nr = block_metadata[:blocks].first
if @blocks[block_nr].nil?
@@ -273,12 +276,12 @@
blocks[i] = nil
end
end
-
+
end
end
- return got_new_block
+ return [got_new_block, got_informative_block]
end
def generate_aux_hash
From nobody at rubyforge.org Wed Mar 28 12:26:31 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Wed, 28 Mar 2007 12:26:31 -0400 (EDT)
Subject: [Archipelago-submits] [255] trunk/oneliner: even more optimization
efforts
Message-ID: <20070328162631.374B85240AF3@rubyforge.org>
Revision: 255
Author: zond
Date: 2007-03-28 12:26:30 -0400 (Wed, 28 Mar 2007)
Log Message:
-----------
even more optimization efforts
Modified Paths:
--------------
trunk/oneliner/ext/oneliner_ext.c
trunk/oneliner/lib/oneliner/superstring.rb
Modified: trunk/oneliner/ext/oneliner_ext.c
===================================================================
--- trunk/oneliner/ext/oneliner_ext.c 2007-03-28 16:04:57 UTC (rev 254)
+++ trunk/oneliner/ext/oneliner_ext.c 2007-03-28 16:26:30 UTC (rev 255)
@@ -218,7 +218,7 @@
// can lead to anything good.
//
static VALUE
-superstring_decode_multiple(VALUE self, VALUE block, VALUE degree, VALUE block_numbers, VALUE blocks, VALUE nr_of_blocks)
+superstring_decode_multiple(VALUE self, VALUE block, VALUE degree, VALUE value_block_numbers, VALUE blocks, VALUE nr_of_blocks)
{
int tmp;
int got_new_block = 0;
@@ -226,13 +226,14 @@
int _nr_of_blocks = NUM2INT(nr_of_blocks);
int missing_block_nr;
char xor_sum[RSTRING(block)->len];
+ unsigned int *block_numbers = (unsigned int *) RSTRING(value_block_numbers)->ptr;
VALUE get_func = rb_intern("[]");
VALUE this_block;
memcpy(xor_sum, RSTRING(block)->ptr, RSTRING(block)->len);
for (tmp = 0; tmp < NUM2INT(degree); tmp++) {
- int block_nr = NUM2INT(rb_funcall(block_numbers, get_func, 1, INT2NUM(tmp)));
+ int block_nr = block_numbers[tmp];
if ((this_block = rb_funcall(blocks, get_func, 1, INT2NUM(block_nr))) != Qnil) {
_string_xor2(xor_sum, RSTRING(this_block)->ptr, RSTRING(this_block)->len);
Modified: trunk/oneliner/lib/oneliner/superstring.rb
===================================================================
--- trunk/oneliner/lib/oneliner/superstring.rb 2007-03-28 16:04:57 UTC (rev 254)
+++ trunk/oneliner/lib/oneliner/superstring.rb 2007-03-28 16:26:30 UTC (rev 255)
@@ -122,7 +122,7 @@
# Misc stuff
#
- def build_chunk_data(chunk, context, the_seed)
+ def old_build_chunk_data(chunk, context, the_seed)
chunk_data = {
:blocks => expand(chunk[8..-1]),
:block_metadata => []
@@ -131,14 +131,41 @@
chunk_data[:blocks].size.times do |n|
chunk_data[:block_metadata][n] = {}
chunk_data[:block_metadata][n][:degree] = get_degree(context)
- chunk_data[:block_metadata][n][:blocks] = []
+ chunk_data[:block_metadata][n][:blocks] = ""
chunk_data[:block_metadata][n][:degree].times do |m|
- chunk_data[:block_metadata][n][:blocks] << context.random(@nr_of_blocks)
+ chunk_data[:block_metadata][n][:blocks] << [context.random(@nr_of_blocks)].pack("I")
end
end
return chunk_data
end
+ def build_chunk_data(chunk, context, the_seed)
+ block_metadata = []
+
+ context.seed(the_seed)
+
+ blocks = expand(chunk[8..-1])
+ blocks.size.times do |n|
+ this_degree = get_degree(context)
+ these_blocks = ""
+ this_degree.times do |m|
+ these_blocks << [context.random(@nr_of_blocks)].pack("I")
+ end
+
+ block_metadata[n] = {
+ :degree => this_degree,
+ :block_numbers => these_blocks
+ }
+ end
+
+ chunk_data = {
+ :blocks => blocks,
+ :block_metadata => block_metadata
+ }
+
+ return chunk_data
+ end
+
def used_blocks
@known_chunks.inject(0) do |sum, chunk_data|
sum + chunk_data[:blocks].size
@@ -263,7 +290,7 @@
block_metadata = chunk_data[:block_metadata][i]
if (degree = block_metadata[:degree]) == 1
- block_nr = block_metadata[:blocks].first
+ block_nr = block_metadata[:block_numbers].unpack("I").first
if @blocks[block_nr].nil?
@blocks[block_nr] = block
@@ -271,7 +298,7 @@
blocks[i] = nil
end
else
- if decode_multiple(block, degree, block_metadata[:blocks], @blocks, @nr_of_blocks)
+ if decode_multiple(block, degree, block_metadata[:block_numbers], @blocks, @nr_of_blocks)
got_new_block = true
blocks[i] = nil
end
From nobody at rubyforge.org Wed Mar 28 13:30:32 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Wed, 28 Mar 2007 13:30:32 -0400 (EDT)
Subject: [Archipelago-submits] [256]
trunk/oneliner/lib/oneliner/superstring.rb: more opti
Message-ID: <20070328173032.7F8425240AB0@rubyforge.org>
Revision: 256
Author: zond
Date: 2007-03-28 13:30:32 -0400 (Wed, 28 Mar 2007)
Log Message:
-----------
more opti
Modified Paths:
--------------
trunk/oneliner/lib/oneliner/superstring.rb
Modified: trunk/oneliner/lib/oneliner/superstring.rb
===================================================================
--- trunk/oneliner/lib/oneliner/superstring.rb 2007-03-28 16:26:30 UTC (rev 255)
+++ trunk/oneliner/lib/oneliner/superstring.rb 2007-03-28 17:30:32 UTC (rev 256)
@@ -77,15 +77,15 @@
the_seed = chunk[4..7].unpack("i").first
- chunk_data = build_chunk_data(chunk, context, the_seed)
+ blocks = build_chunk_data(chunk, context, the_seed)
first_decode = @known_nr_of_blocks < @nr_of_blocks
- @known_nr_of_blocks += chunk_data[:blocks].size
- @known_chunks.unshift(chunk_data)
+ @known_nr_of_blocks += blocks.size
+ @known_chunks.unshift(blocks)
if @known_nr_of_blocks > @nr_of_blocks
interesting = true
- interesting, informative = decode_chunk(chunk_data) unless first_decode
+ interesting, informative = decode_chunk(blocks) unless first_decode
while interesting
interesting = do_decode(context)
@@ -122,53 +122,27 @@
# Misc stuff
#
- def old_build_chunk_data(chunk, context, the_seed)
- chunk_data = {
- :blocks => expand(chunk[8..-1]),
- :block_metadata => []
- }
- context.seed(the_seed)
- chunk_data[:blocks].size.times do |n|
- chunk_data[:block_metadata][n] = {}
- chunk_data[:block_metadata][n][:degree] = get_degree(context)
- chunk_data[:block_metadata][n][:blocks] = ""
- chunk_data[:block_metadata][n][:degree].times do |m|
- chunk_data[:block_metadata][n][:blocks] << [context.random(@nr_of_blocks)].pack("I")
- end
- end
- return chunk_data
- end
-
def build_chunk_data(chunk, context, the_seed)
- block_metadata = []
+ blocks_to_return = Set.new
context.seed(the_seed)
- blocks = expand(chunk[8..-1])
- blocks.size.times do |n|
+ expand(chunk[8..-1]).each do |block|
this_degree = get_degree(context)
these_blocks = ""
this_degree.times do |m|
these_blocks << [context.random(@nr_of_blocks)].pack("I")
end
- block_metadata[n] = {
- :degree => this_degree,
- :block_numbers => these_blocks
- }
+ blocks_to_return << [block, this_degree, these_blocks]
end
- chunk_data = {
- :blocks => blocks,
- :block_metadata => block_metadata
- }
-
- return chunk_data
+ return blocks_to_return
end
def used_blocks
- @known_chunks.inject(0) do |sum, chunk_data|
- sum + chunk_data[:blocks].size
+ @known_chunks.inject(0) do |sum, blocks|
+ sum + blocks.size
end
end
@@ -225,8 +199,8 @@
def do_decode(context)
found_new_block = false
- @known_chunks.clone.each_with_index do |chunk_data, i|
- found_new_block, informative = decode_chunk(chunk_data) || found_new_block
+ @known_chunks.clone.each_with_index do |blocks, i|
+ found_new_block, informative = decode_chunk(blocks) || found_new_block
@known_chunks.delete(i) unless informative
end
found_new_block = aux_decode || found_new_block
@@ -276,36 +250,29 @@
return got_new_block
end
- def decode_chunk(chunk_data)
+ def decode_chunk(blocks)
got_new_block = false
got_informative_block = false
- blocks = chunk_data[:blocks]
- blocks.each_with_index do |block, i|
+ blocks.clone.each do |block, degree, block_numbers|
- if block
-
- got_informative_block = true
-
- block_metadata = chunk_data[:block_metadata][i]
+ got_informative_block = true
+
+ if degree == 1
+ block_nr = block_numbers.unpack("I").first
- if (degree = block_metadata[:degree]) == 1
- block_nr = block_metadata[:block_numbers].unpack("I").first
-
- if @blocks[block_nr].nil?
- @blocks[block_nr] = block
- got_new_block = true
- blocks[i] = nil
- end
- else
- if decode_multiple(block, degree, block_metadata[:block_numbers], @blocks, @nr_of_blocks)
- got_new_block = true
- blocks[i] = nil
- end
+ if @blocks[block_nr].nil?
+ @blocks[block_nr] = block
+ got_new_block = true
+ blocks.delete([block, degree, block_numbers])
end
-
+ else
+ if decode_multiple(block, degree, block_numbers, @blocks, @nr_of_blocks)
+ got_new_block = true
+ blocks.delete([block, degree, block_numbers])
+ end
end
-
+
end
return [got_new_block, got_informative_block]
From nobody at rubyforge.org Thu Mar 29 05:15:06 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Thu, 29 Mar 2007 05:15:06 -0400 (EDT)
Subject: [Archipelago-submits] [257] trunk/oneliner: more optimization
preparation,
sending arrays into c instead of the parts of the arrays.
Message-ID: <20070329091506.2CFA45240B55@rubyforge.org>
Revision: 257
Author: zond
Date: 2007-03-29 05:15:05 -0400 (Thu, 29 Mar 2007)
Log Message:
-----------
more optimization preparation, sending arrays into c instead of the parts of the arrays. fixing a sometimes-broken test.
Modified Paths:
--------------
trunk/oneliner/ext/oneliner_ext.c
trunk/oneliner/lib/oneliner/superstring.rb
trunk/oneliner/tests/superstring_test.rb
Modified: trunk/oneliner/ext/oneliner_ext.c
===================================================================
--- trunk/oneliner/ext/oneliner_ext.c 2007-03-28 17:30:32 UTC (rev 256)
+++ trunk/oneliner/ext/oneliner_ext.c 2007-03-29 09:15:05 UTC (rev 257)
@@ -164,6 +164,9 @@
static VALUE
string_hack(VALUE self, VALUE size_value)
{
+ if (NUM2INT(size_value) < 1)
+ rb_raise(rb_eTypeError, "argument to hack must be a positive integer > 0");
+
int size = NUM2INT(size_value);
VALUE rval = rb_ary_new();
int tmp;
@@ -218,8 +221,13 @@
// can lead to anything good.
//
static VALUE
-superstring_decode_multiple(VALUE self, VALUE block, VALUE degree, VALUE value_block_numbers, VALUE blocks, VALUE nr_of_blocks)
+superstring_decode_multiple(VALUE self, VALUE block_data, VALUE blocks, VALUE nr_of_blocks)
{
+ VALUE get_func = rb_intern("[]");
+ VALUE this_block;
+ VALUE block = rb_funcall(block_data, get_func, 1, INT2NUM(0));
+ VALUE degree = rb_funcall(block_data, get_func, 1, INT2NUM(1));
+ VALUE value_block_numbers = rb_funcall(block_data, get_func, 1, INT2NUM(2));
int tmp;
int got_new_block = 0;
int missing_blocks = NUM2INT(degree);
@@ -227,8 +235,6 @@
int missing_block_nr;
char xor_sum[RSTRING(block)->len];
unsigned int *block_numbers = (unsigned int *) RSTRING(value_block_numbers)->ptr;
- VALUE get_func = rb_intern("[]");
- VALUE this_block;
memcpy(xor_sum, RSTRING(block)->ptr, RSTRING(block)->len);
@@ -275,7 +281,7 @@
rb_define_method(string, "^=", string_xor2, 1);
rb_define_method(string, "hack", string_hack, 1);
rb_define_method(rb_superstring, "get_degree", superstring_get_degree, 1);
- rb_define_method(rb_superstring, "decode_multiple", superstring_decode_multiple, 5);
+ rb_define_method(rb_superstring, "decode_multiple", superstring_decode_multiple, 3);
}
#ifdef __cplusplus
}
Modified: trunk/oneliner/lib/oneliner/superstring.rb
===================================================================
--- trunk/oneliner/lib/oneliner/superstring.rb 2007-03-28 17:30:32 UTC (rev 256)
+++ trunk/oneliner/lib/oneliner/superstring.rb 2007-03-29 09:15:05 UTC (rev 257)
@@ -123,18 +123,18 @@
#
def build_chunk_data(chunk, context, the_seed)
- blocks_to_return = Set.new
+ blocks_to_return = {}
context.seed(the_seed)
- expand(chunk[8..-1]).each do |block|
+ expand(chunk[8..-1]).each_with_index do |block, index|
this_degree = get_degree(context)
these_blocks = ""
this_degree.times do |m|
these_blocks << [context.random(@nr_of_blocks)].pack("I")
end
- blocks_to_return << [block, this_degree, these_blocks]
+ blocks_to_return[index] = [block, this_degree, these_blocks]
end
return blocks_to_return
@@ -254,8 +254,10 @@
got_new_block = false
got_informative_block = false
- blocks.clone.each do |block, degree, block_numbers|
+ blocks.clone.each do |index, block_data|
+ block, degree, block_numbers = block_data
+
got_informative_block = true
if degree == 1
@@ -264,12 +266,12 @@
if @blocks[block_nr].nil?
@blocks[block_nr] = block
got_new_block = true
- blocks.delete([block, degree, block_numbers])
+ blocks.delete(index)
end
else
- if decode_multiple(block, degree, block_numbers, @blocks, @nr_of_blocks)
+ if decode_multiple(block_data, @blocks, @nr_of_blocks)
got_new_block = true
- blocks.delete([block, degree, block_numbers])
+ blocks.delete(index)
end
end
Modified: trunk/oneliner/tests/superstring_test.rb
===================================================================
--- trunk/oneliner/tests/superstring_test.rb 2007-03-28 17:30:32 UTC (rev 256)
+++ trunk/oneliner/tests/superstring_test.rb 2007-03-29 09:15:05 UTC (rev 257)
@@ -15,7 +15,7 @@
20.times do
size = rand(1024)
s = "x" * size
- bites = rand(128)
+ bites = rand(128) + 1
l = s.hack(bites)
assert_equal((size.to_f / bites).ceil, l.size)
assert_equal(s, l.join)
From nobody at rubyforge.org Thu Mar 29 05:20:15 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Thu, 29 Mar 2007 05:20:15 -0400 (EDT)
Subject: [Archipelago-submits] [258] trunk/oneliner: preparation for more
optimization
Message-ID: <20070329092015.1B4385240B7A@rubyforge.org>
Revision: 258
Author: zond
Date: 2007-03-29 05:20:14 -0400 (Thu, 29 Mar 2007)
Log Message:
-----------
preparation for more optimization
Modified Paths:
--------------
trunk/oneliner/ext/oneliner_ext.c
trunk/oneliner/lib/oneliner/superstring.rb
Modified: trunk/oneliner/ext/oneliner_ext.c
===================================================================
--- trunk/oneliner/ext/oneliner_ext.c 2007-03-29 09:15:05 UTC (rev 257)
+++ trunk/oneliner/ext/oneliner_ext.c 2007-03-29 09:20:14 UTC (rev 258)
@@ -234,12 +234,11 @@
int _nr_of_blocks = NUM2INT(nr_of_blocks);
int missing_block_nr;
char xor_sum[RSTRING(block)->len];
- unsigned int *block_numbers = (unsigned int *) RSTRING(value_block_numbers)->ptr;
memcpy(xor_sum, RSTRING(block)->ptr, RSTRING(block)->len);
for (tmp = 0; tmp < NUM2INT(degree); tmp++) {
- int block_nr = block_numbers[tmp];
+ int block_nr = NUM2INT(rb_funcall(value_block_numbers, get_func, 1, INT2NUM(tmp)));
if ((this_block = rb_funcall(blocks, get_func, 1, INT2NUM(block_nr))) != Qnil) {
_string_xor2(xor_sum, RSTRING(this_block)->ptr, RSTRING(this_block)->len);
Modified: trunk/oneliner/lib/oneliner/superstring.rb
===================================================================
--- trunk/oneliner/lib/oneliner/superstring.rb 2007-03-29 09:15:05 UTC (rev 257)
+++ trunk/oneliner/lib/oneliner/superstring.rb 2007-03-29 09:20:14 UTC (rev 258)
@@ -129,9 +129,9 @@
expand(chunk[8..-1]).each_with_index do |block, index|
this_degree = get_degree(context)
- these_blocks = ""
+ these_blocks = []
this_degree.times do |m|
- these_blocks << [context.random(@nr_of_blocks)].pack("I")
+ these_blocks << context.random(@nr_of_blocks)
end
blocks_to_return[index] = [block, this_degree, these_blocks]
@@ -261,7 +261,7 @@
got_informative_block = true
if degree == 1
- block_nr = block_numbers.unpack("I").first
+ block_nr = block_numbers.first
if @blocks[block_nr].nil?
@blocks[block_nr] = block
From nobody at rubyforge.org Thu Mar 29 05:49:18 2007
From: nobody at rubyforge.org (nobody at rubyforge.org)
Date: Thu, 29 Mar 2007 05:49:18 -0400 (EDT)
Subject: [Archipelago-submits] [259] trunk/oneliner: more optimization?
Message-ID: <20070329094918.6D2C95240B50@rubyforge.org>
Revision: 259
Author: zond
Date: 2007-03-29 05:49:18 -0400 (Thu, 29 Mar 2007)
Log Message:
-----------
more optimization?
Modified Paths:
--------------
trunk/oneliner/ext/oneliner_ext.c
trunk/oneliner/lib/oneliner/superstring.rb
Modified: trunk/oneliner/ext/oneliner_ext.c
===================================================================
--- trunk/oneliner/ext/oneliner_ext.c 2007-03-29 09:20:14 UTC (rev 258)
+++ trunk/oneliner/ext/oneliner_ext.c 2007-03-29 09:49:18 UTC (rev 259)
@@ -224,32 +224,33 @@
superstring_decode_multiple(VALUE self, VALUE block_data, VALUE blocks, VALUE nr_of_blocks)
{
VALUE get_func = rb_intern("[]");
+ VALUE set_func = rb_intern("[]=");
VALUE this_block;
VALUE block = rb_funcall(block_data, get_func, 1, INT2NUM(0));
- VALUE degree = rb_funcall(block_data, get_func, 1, INT2NUM(1));
- VALUE value_block_numbers = rb_funcall(block_data, get_func, 1, INT2NUM(2));
+ VALUE block_numbers = rb_funcall(block_data, get_func, 1, INT2NUM(1));
+ int degree = RARRAY(block_numbers)->len;
int tmp;
int got_new_block = 0;
- int missing_blocks = NUM2INT(degree);
+ int missing_blocks = degree;
int _nr_of_blocks = NUM2INT(nr_of_blocks);
int missing_block_nr;
- char xor_sum[RSTRING(block)->len];
- memcpy(xor_sum, RSTRING(block)->ptr, RSTRING(block)->len);
-
- for (tmp = 0; tmp < NUM2INT(degree); tmp++) {
- int block_nr = NUM2INT(rb_funcall(value_block_numbers, get_func, 1, INT2NUM(tmp)));
-
+ for (tmp = 0; tmp < degree; tmp++) {
+ int block_nr = NUM2INT(rb_funcall(block_numbers, get_func, 1, INT2NUM(tmp)));
if ((this_block = rb_funcall(blocks, get_func, 1, INT2NUM(block_nr))) != Qnil) {
- _string_xor2(xor_sum, RSTRING(this_block)->ptr, RSTRING(this_block)->len);
+ rb_str_modify(block);
+ _string_xor2(RSTRING(block)->ptr, RSTRING(this_block)->ptr, RSTRING(this_block)->len);
+ rb_funcall(block_numbers, set_func, 2, INT2NUM(tmp), Qnil);
missing_blocks--;
} else {
missing_block_nr = block_nr;
}
}
+ rb_funcall(block_numbers, rb_intern("compact!"), 0);
+
if (missing_blocks == 1) {
- rb_funcall(blocks, rb_intern("[]="), 2, INT2NUM(missing_block_nr), rb_str_new(xor_sum, RSTRING(block)->len));
+ rb_funcall(blocks, set_func, 2, INT2NUM(missing_block_nr), block);
got_new_block = 1;
}
Modified: trunk/oneliner/lib/oneliner/superstring.rb
===================================================================
--- trunk/oneliner/lib/oneliner/superstring.rb 2007-03-29 09:20:14 UTC (rev 258)
+++ trunk/oneliner/lib/oneliner/superstring.rb 2007-03-29 09:49:18 UTC (rev 259)
@@ -134,7 +134,7 @@
these_blocks << context.random(@nr_of_blocks)
end
- blocks_to_return[index] = [block, this_degree, these_blocks]
+ blocks_to_return[index] = [block, these_blocks]
end
return blocks_to_return
@@ -256,11 +256,11 @@
blocks.clone.each do |index, block_data|
- block, degree, block_numbers = block_data
+ block, block_numbers = block_data
got_informative_block = true
- if degree == 1
+ if block_numbers.size == 1
block_nr = block_numbers.first
if @blocks[block_nr].nil?