sequence 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/weakrefset.rb ADDED
@@ -0,0 +1,254 @@
1
+ # $Id$
2
+ # Copyright (C) 2006 Caleb Clausen
3
+ # Distributed under the terms of Ruby's license.
4
+
5
+ require 'yaml'
6
+ require 'assert'
7
+
8
+ # WeakRefSet implements an unordered collection of weak references to objects.
9
+ # These references don't prevent garbage collection on these objects. As these
10
+ # objects are thrown away so does their entry in a WeakRefSet. Immmediate
11
+ # objects are not handled by this class (and wouldn't be useful).
12
+ class WeakRefSet
13
+ include Enumerable
14
+ # create a new WeakRefSet from an optional Enumerable (of objects)
15
+ # which is optionally processed through a block
16
+ def initialize(items) # :yield: obj
17
+ replace(items)
18
+ end
19
+ class<<self
20
+ def [] *items
21
+ new(items)
22
+ end
23
+ end
24
+
25
+ private
26
+ def finalizer(id)
27
+ @ids.delete(id)
28
+ end
29
+
30
+ public
31
+
32
+
33
+
34
+ # add a weak reference to the set
35
+ def add(obj)
36
+ Symbol===obj || Fixnum===obj || nil==obj || true==obj || false==obj and
37
+ raise ArgumentError, "no immediates in weakrefset"
38
+ id=obj.object_id
39
+ case (o2=ObjectSpace._id2ref id) #test id for validity
40
+ when Symbol,Fixnum,true,false,nil: id=obj #hopefully rare
41
+ else obj.equal? o2 or raise
42
+ ObjectSpace.define_finalizer(obj,method(:finalizer))
43
+ end
44
+ @ids[id] = true
45
+ self
46
+ end
47
+ alias << add
48
+ # iterate over remaining valid objects in the set
49
+ def each
50
+ @ids.each_key { |id|
51
+ case id
52
+ when Integer:
53
+ begin
54
+ o = ObjectSpace._id2ref(id)
55
+ rescue RangeError
56
+ next
57
+ end
58
+ @ids.include?(id) or next
59
+ #i don't know where the random symbols come from, but at least they're always symbols...
60
+ else
61
+ o=id
62
+ end
63
+ case o
64
+ when Symbol,Fixnum,true,false,nil: warn "immediate value #{o.inspect} found in weakrefset"
65
+ else yield(o)
66
+ end
67
+ }
68
+ self
69
+ end
70
+
71
+ def == other
72
+ size==other.size and
73
+ each{|x|
74
+ other.include? x or return
75
+ }
76
+
77
+ end
78
+
79
+ # clear the set (return self)
80
+ def clear
81
+ @ids = {}
82
+ self
83
+ end
84
+ # merge some more objects into the set (return self)
85
+ def merge(enum)
86
+ enum.each { |obj| add(obj) }
87
+ self
88
+ end
89
+ # replace the objects in the set (return self)
90
+ def replace(enum)
91
+ clear
92
+ merge(enum)
93
+ self
94
+ end
95
+ # delete an object in the set (return self)
96
+ def delete(obj)
97
+ delete?(obj)
98
+ self
99
+ end
100
+ # delete an object in the set (return self or nil if nothing deleted)
101
+ def delete?(obj)
102
+ x=include?(obj) and @ids.delete(x.__id__)||@ids.delete(x) and self
103
+ end
104
+ # is this object in the set?
105
+ def include?(obj)
106
+ find{|x| obj==x}
107
+ end
108
+ alias member? include?
109
+
110
+ # return a human-readable string showing the set
111
+ def inspect
112
+ #unless $weakrefset_verbose_inspect
113
+ # return sprintf('#<%s:0x%x {...}>', self.class.name, object_id)
114
+ #end
115
+ ids = (Thread.current[:__weakrefset__inspect_key__] ||= [])
116
+
117
+ if ids.include?(object_id)
118
+ return sprintf('#<%s {...}>', self.class.name)
119
+ end
120
+
121
+ begin
122
+ ids << object_id
123
+ return sprintf('#<%s {%s}>', self.class.name, to_a.inspect[1..-2])
124
+ ensure
125
+ ids.pop
126
+ Thread.current[:__weakrefset__inspect_key__].empty? and
127
+ Thread.current[:__weakrefset__inspect_key__]=nil
128
+ end
129
+ end
130
+
131
+ if false #this is broken; emits yaml for a hash.
132
+
133
+ YAML::add_domain_type( "inforadical.net,2005", "object:WeakRefSet" ) do |type, val|
134
+ WeakRefSet.new( *val["items"] )
135
+ end
136
+
137
+ def is_complex_yaml?; true end
138
+
139
+ def to_yaml_type; "!inforadical.net,2005/object:WeakRefSet" end
140
+
141
+ alias to_yaml_properties to_a
142
+
143
+ def to_yaml( opts = {} )
144
+ YAML::quick_emit( object_id, opts ) { |out|
145
+ out.map( to_yaml_type ) { |map|
146
+ map.add( "items", to_yaml_properties)
147
+ }
148
+ }
149
+ end
150
+ end
151
+
152
+ # remove some objects from the set (return self)
153
+ def subtract(enum)
154
+ enum.each { |obj| delete(obj) }
155
+ self
156
+ end
157
+ # any objects in the set still valid?
158
+ def empty?
159
+ @ids.empty?
160
+ end
161
+ # number of objects in the set still valid
162
+ def size
163
+ @ids.size
164
+ end
165
+ alias length size
166
+ end
167
+
168
+ # :stopdoc:
169
+
170
+ if __FILE__==$0
171
+ require 'benchmark'
172
+ class MyString <String;
173
+ def initialize(*)
174
+ @owner=0
175
+ super
176
+ end
177
+
178
+ end
179
+ class MyObject; end
180
+ class MyClass < Module; end
181
+ weakrefsets = (1..10).map {WeakRefSet[]}
182
+ $stdout.sync=true
183
+ obj = nil
184
+ arr=[]
185
+ # srand(2389547343)
186
+ classes=[]
187
+ ObjectSpace.each_object(Class){|ob| classes<<ob}
188
+ classes-=[Symbol,Integer,NilClass,FalseClass,TrueClass,Numeric,Data,Bignum,Fixnum,
189
+ Float,Struct,Method,UnboundMethod,Proc,Thread,Binding,Continuation]
190
+ classes.delete_if{|k| begin k.allocate; rescue; true else false end}
191
+ def shuffle!(arr)
192
+ arr.sort_by{rand}
193
+ end
194
+ times = Benchmark.measure {
195
+ 100000.times { |i|
196
+ # print(weakrefs.size>70?"|":((60..70)===weakrefs.size ? ":" : (weakrefs.size>50?',':'.')))
197
+ print "." if 0==i%128
198
+ #obj = (k=classes[rand(classes.size)]).allocate
199
+ obj = (k=MyString).new "X" #*rand(i+1)
200
+ # obj= (k=Object).new
201
+ # obj= (k=MyObject).new
202
+ #obj= (k=MyClass).new
203
+ k==obj.class or raise
204
+ weakrefs=weakrefsets[rand(weakrefsets.size)]
205
+ obj.instance_eval{@owner=weakrefs}
206
+ obj.instance_eval{@owner}.equal? weakrefs or raise
207
+ weakrefs.each { |o|
208
+ # k==o.class or raise "set contained a #{o.class}. i=#{i}. size=#{weakrefs.size}"
209
+ (o2=o.instance_eval{@owner})==weakrefs or
210
+ raise "expected owner #{weakrefs.map{|w| w.__id__}.inspect}, "+
211
+ "got #{o2.inspect}, item #{o}, id #{o.__id__}, obj #{obj.__id__}"
212
+ }
213
+ weakrefs << obj
214
+ weakrefs.each { |o|
215
+ # k==o.class or raise "set contained a #{o.class}. i=#{i}. size=#{weakrefs.size}"
216
+ (o2=o.instance_eval{@owner})==weakrefs or
217
+ raise "expected owner #{weakrefs.map{|w| w.__id__}.inspect}, "+
218
+ "got #{o2.inspect}, item #{o}, id #{o.__id__}, obj #{obj.__id__}"
219
+ }
220
+ weakrefs.include?(obj) or raise
221
+ weakrefs.each { |o|
222
+ # k==o.class or raise "set contained a #{o.class}. i=#{i}. size=#{weakrefs.size}"
223
+ (o2=o.instance_eval{@owner})==weakrefs or
224
+ raise "expected owner #{weakrefs.map{|w| w.__id__}.inspect}, "+
225
+ "got #{o2.inspect}, item #{o}, id #{o.__id__}, obj #{obj.__id__}"
226
+ }
227
+ weakrefs.include?(obj) or raise
228
+ if rand(10).zero?
229
+ weakrefs.delete?(obj) or raise
230
+ !weakrefs.include?(obj) or raise
231
+ elsif rand(4).zero?
232
+ arr<<obj #prevent garbage collection
233
+ end
234
+ if rand(1000).zero?
235
+ shuffle! arr
236
+ arr.slice!(0..rand(arr.size))
237
+ end
238
+ arr.each{|o| o.instance_eval{@owner}.include? o or raise }
239
+ #rand(100).zero? and GC.start
240
+ }
241
+ }
242
+ puts
243
+ GC.start
244
+ weakrefsets.each{|weakrefs|
245
+ weakrefs.clear
246
+ weakrefs.size.zero? or raise
247
+ weakrefs.empty? or raise
248
+ }
249
+ puts(times)
250
+ end
251
+
252
+ # :stopdoc:
253
+
254
+