utilrb 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
data/Changes.txt CHANGED
@@ -1,3 +1,21 @@
1
+ === Version 0.2.3
2
+
3
+ * new features:
4
+ - BenchmarkAllocation, a Benchmark-like interface to benchmark object
5
+ allocation
6
+ - ValueSet#delete_if
7
+ - Time#from_hms takes a time as "h:m:s.ms" and builds the corresponding
8
+ Time object. Note that "s", "s.ms", "m:s", "m:s.ms", ... are accepted
9
+ formats
10
+
11
+ * changes and fixes:
12
+ - define Queue#get only if we are using Ruby's core Queue. The current
13
+ implementation is incompatible with fastthread for instance (and
14
+ fastthhread's maintainer does not want #get on its Queue). Included
15
+ a patch to define Queue#get on fastthread
16
+ - fix ValueSet#== raising if the argument is not a ValueSet
17
+ - fix brain-dead SequenceEnumerator#+, which was changing its receiver
18
+
1
19
  === Version 0.2.2
2
20
  The "don't forget to bump version number" release. 0.2 was supposed to be 0.2.1
3
21
 
data/License.txt CHANGED
@@ -1,4 +1,5 @@
1
- Copyright (c) The Regents of the University of California.
1
+ Copyright (c) 2006-2007 LAAS/CNRS <openrobots@laas.fr>
2
+ 2006-2007 Sylvain Joyeux <sylvain.joyeux@laas.fr>
2
3
  All rights reserved.
3
4
 
4
5
  Redistribution and use in source and binary forms, with or without
data/Manifest.txt CHANGED
@@ -1,58 +1,61 @@
1
+ Changes.txt
2
+ License.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
1
6
  bm/allocation.rb
2
7
  bm/speed.rb
3
- Changes.txt
4
8
  ext/extconf.rb
5
9
  ext/faster.cc
6
10
  ext/swap.cc
7
11
  ext/value_set.cc
12
+ lib/utilrb.rb
8
13
  lib/utilrb/array.rb
9
14
  lib/utilrb/array/to_s.rb
10
15
  lib/utilrb/common.rb
16
+ lib/utilrb/enumerable.rb
11
17
  lib/utilrb/enumerable/null.rb
12
18
  lib/utilrb/enumerable/random_element.rb
13
- lib/utilrb/enumerable.rb
14
19
  lib/utilrb/enumerable/sequence.rb
15
20
  lib/utilrb/enumerable/uniq.rb
16
- lib/utilrb/exception/full_message.rb
17
21
  lib/utilrb/exception.rb
18
- lib/utilrb/gc/force.rb
22
+ lib/utilrb/exception/full_message.rb
19
23
  lib/utilrb/gc.rb
24
+ lib/utilrb/gc/force.rb
20
25
  lib/utilrb/hash.rb
21
26
  lib/utilrb/hash/slice.rb
22
27
  lib/utilrb/hash/to_s.rb
23
28
  lib/utilrb/hash/to_sym_keys.rb
29
+ lib/utilrb/kernel.rb
24
30
  lib/utilrb/kernel/arity.rb
25
31
  lib/utilrb/kernel/options.rb
26
32
  lib/utilrb/kernel/poll.rb
27
- lib/utilrb/kernel.rb
28
33
  lib/utilrb/kernel/require.rb
29
34
  lib/utilrb/kernel/swap.rb
35
+ lib/utilrb/logger.rb
30
36
  lib/utilrb/logger/forward.rb
31
37
  lib/utilrb/logger/hierarchy.rb
32
- lib/utilrb/logger.rb
38
+ lib/utilrb/module.rb
33
39
  lib/utilrb/module/ancestor_p.rb
34
40
  lib/utilrb/module/attr_enumerable.rb
35
41
  lib/utilrb/module/define_method.rb
36
42
  lib/utilrb/module/include.rb
37
43
  lib/utilrb/module/inherited_enumerable.rb
38
- lib/utilrb/module.rb
44
+ lib/utilrb/object.rb
39
45
  lib/utilrb/object/address.rb
40
46
  lib/utilrb/object/attribute.rb
41
- lib/utilrb/object.rb
42
47
  lib/utilrb/object/singleton_class.rb
43
48
  lib/utilrb/objectstats.rb
44
- lib/utilrb/queue/get.rb
45
49
  lib/utilrb/queue.rb
46
- lib/utilrb.rb
50
+ lib/utilrb/queue/get.rb
51
+ lib/utilrb/set.rb
52
+ lib/utilrb/set/to_s.rb
47
53
  lib/utilrb/time.rb
48
54
  lib/utilrb/time/to_hms.rb
49
- lib/utilrb/unbound_method/call.rb
50
55
  lib/utilrb/unbound_method.rb
56
+ lib/utilrb/unbound_method/call.rb
51
57
  lib/utilrb/value_set.rb
52
- License.txt
53
- Manifest.txt
54
- Rakefile
55
- README.txt
58
+ patches/fastthread-queue-get.patch
56
59
  test/test_array.rb
57
60
  test/test_config.rb
58
61
  test/test_enumerable.rb
@@ -63,4 +66,5 @@ test/test_misc.rb
63
66
  test/test_module.rb
64
67
  test/test_object.rb
65
68
  test/test_objectstats.rb
69
+ test/test_time.rb
66
70
  test/test_unbound_method.rb
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ Hoe.new('utilrb', Utilrb::VERSION) do |p|
8
8
  p.summary = 'Yet another Ruby toolkit'
9
9
  p.description = p.paragraphs_of('README.txt', 3..6).join("\n\n")
10
10
  p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
11
- p.changes = p.paragraphs_of('Changes.txt', 0..1).join("\n\n")
11
+ p.changes = p.paragraphs_of('Changes.txt', 0..2).join("\n\n")
12
12
 
13
13
  p.extra_deps << 'facets'
14
14
  end
data/ext/faster.cc CHANGED
@@ -25,7 +25,11 @@ static VALUE enumerable_each_uniq(VALUE self)
25
25
  return self;
26
26
  }
27
27
 
28
- /* Returns true if +self+ is a singleton class */
28
+ /* call-seq:
29
+ * Kernel.is_singleton?(object)
30
+ *
31
+ * Returns true if +self+ is a singleton class
32
+ */
29
33
  static VALUE kernel_is_singleton_p(VALUE self)
30
34
  {
31
35
  if (BUILTIN_TYPE(self) == T_CLASS && FL_TEST(self, FL_SINGLETON))
data/ext/value_set.cc CHANGED
@@ -65,6 +65,27 @@ static VALUE value_set_each(VALUE self)
65
65
  }
66
66
  return self;
67
67
  }
68
+
69
+ /* call-seq:
70
+ * set.delete_if { |obj| ... } => set
71
+ *
72
+ * Deletes all objects for which the block returns true
73
+ */
74
+ static VALUE value_set_delete_if(VALUE self)
75
+ {
76
+ ValueSet& set = get_wrapped_set(self);
77
+ for (ValueSet::iterator it = set.begin(); it != set.end();)
78
+ {
79
+ // Increment before calling yield() so that
80
+ // the current element can be deleted safely
81
+ ValueSet::iterator this_it = it++;
82
+ bool do_delete = RTEST(rb_yield(*this_it));
83
+ if (do_delete)
84
+ set.erase(this_it);
85
+ }
86
+ return self;
87
+ }
88
+
68
89
  /* call-seq:
69
90
  * set.include?(value) => true or false
70
91
  *
@@ -198,6 +219,9 @@ static VALUE value_set_delete(VALUE vself, VALUE v)
198
219
  static VALUE value_set_equal(VALUE vself, VALUE vother)
199
220
  {
200
221
  ValueSet const& self = get_wrapped_set(vself);
222
+ if (!rb_respond_to(vother, id_to_value_set))
223
+ return Qfalse;
224
+
201
225
  ValueSet const& other = get_wrapped_set(rb_funcall(vother, id_to_value_set, 0));
202
226
  return (self == other) ? Qtrue : Qfalse;
203
227
  }
@@ -285,6 +309,7 @@ extern "C" void Init_value_set()
285
309
  rb_define_method(cValueSet, "size", RUBY_METHOD_FUNC(value_set_size), 0);
286
310
  rb_define_method(cValueSet, "clear", RUBY_METHOD_FUNC(value_set_clear), 0);
287
311
  rb_define_method(cValueSet, "initialize_copy", RUBY_METHOD_FUNC(value_set_initialize_copy), 1);
312
+ rb_define_method(cValueSet, "delete_if", RUBY_METHOD_FUNC(value_set_delete_if), 0);
288
313
  }
289
314
 
290
315
 
@@ -1,4 +1,6 @@
1
1
  class Array
2
+ # Displays arrays as [ a, b, [c, d], ... ] instead of the standard #join
3
+ # Unlike #inspect, it calls #to_s on the elements too
2
4
  def to_s
3
5
  stack = (Thread.current[:array_to_s] ||= [])
4
6
  if stack.include?(self)
data/lib/utilrb/common.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Utilrb
2
- VERSION = "0.2.2" unless defined? Utilrb::VERSION
2
+ VERSION = "0.2.3" unless defined? Utilrb::VERSION
3
3
 
4
4
  unless defined? UTILRB_FASTER_MODE
5
5
  if ENV['UTILRB_FASTER_MODE'] == 'no'
@@ -2,6 +2,8 @@
2
2
  # Kernel#null_enum
3
3
  class NullEnumerator
4
4
  def each; self end
5
+ def +(other); other end
6
+
5
7
  include Enumerable
6
8
  end
7
9
 
@@ -1,15 +1,23 @@
1
+ class Array
2
+ # Returns a random element of the array
3
+ def random_element; self[rand(size)] end
4
+ end
5
+
1
6
  module Enumerable
7
+ # Returns a random element in the enumerable. In the worst case scenario,
8
+ # it converts the enumerable into an array
2
9
  def random_element
3
- if Array === self
4
- self[rand(size)]
5
- elsif respond_to?(:to_ary)
10
+ if respond_to?(:to_ary)
6
11
  to_ary.random_element
7
12
  elsif respond_to?(:size)
13
+ return if size == 0
8
14
  element = rand(size)
9
15
  each_with_index { |e, i| return e if i == element }
10
- nil
16
+ raise "something wrong here ..."
11
17
  elsif respond_to?(:to_a)
12
18
  to_a.random_element
19
+ else
20
+ raise ArgumentError, "cannot ue #random_element on this enumerable"
13
21
  end
14
22
  end
15
23
  end
@@ -3,7 +3,6 @@ class SequenceEnumerator
3
3
  def initialize; @sequence = Array.new end
4
4
  # Adds +object+ at the back of the sequence
5
5
  def <<(object); @sequence << object; self end
6
- alias :+ :<<
7
6
 
8
7
  def each
9
8
  @sequence.each { |enum| enum.each { |o| yield(o) } } if block_given?
@@ -1,5 +1,21 @@
1
1
 
2
2
  class Exception
3
+ # Returns the full exception message, with backtrace, like the one we get from
4
+ # the Ruby interpreter when the program is aborted (well, almost like that)
5
+ #
6
+ # For instance,
7
+ # def test
8
+ # raise RuntimeError, "this is a test"
9
+ # rescue
10
+ # puts $!.full_message
11
+ # end
12
+ # test
13
+ #
14
+ # displays
15
+ #
16
+ # test.rb:3:in `test': this is a test (RuntimeError)
17
+ # from test.rb:7
18
+ #
3
19
  def full_message
4
20
  first, *remaining = backtrace
5
21
  "#{first}: #{message} (#{self.class})\n\tfrom " + remaining.join("\n\tfrom ")
@@ -1,4 +1,6 @@
1
1
  class Hash
2
+ # Displays hashes as { a => A, b => B, ... } instead of the standard #join
3
+ # Unlike #inspect, it calls #to_s on the elements too
2
4
  def to_s
3
5
  "{" << map { |k, v| "#{k} => #{v}" }.join(", ") << "}"
4
6
  end
@@ -1,4 +1,8 @@
1
1
  class Hash
2
+ # Returns a hash for which all keys have been converted to symbols (only
3
+ # valid for string/symbol keys of course)
4
+ #
5
+ # { 'a' => 1, :b => '2' }.to_sym_keys => { :a => 1, :b => '2' }
2
6
  def to_sym_keys
3
7
  inject({}) { |h, (k, v)| h[k.to_sym] = v; h }
4
8
  end
@@ -5,6 +5,12 @@ module Kernel
5
5
  # arguments, with default value support. All option keys are
6
6
  # converted to symbols for consistency.
7
7
  #
8
+ # The following rules apply:
9
+ # * if a hash is given, non-nil values are treated as default values.
10
+ # * an array is equivalent to a hash where all values are 'nil'
11
+ #
12
+ # See #validate_options and #filter_and_validate_options
13
+ #
8
14
  # call-seq:
9
15
  # filter_options(option, hash) -> known, unknown
10
16
  # filter_options(option, array) -> known, unknown
@@ -38,7 +44,7 @@ module Kernel
38
44
  # as an empty option hash, all keys are converted into symbols.
39
45
  #
40
46
  def validate_options(options, known_options)
41
- opt, unknown = filter_options(options, known_options)
47
+ opt, unknown = Kernel.filter_options(options, known_options)
42
48
  unless unknown.empty?
43
49
  not_valid = unknown.keys.map { |m| "'#{m}'" }.join(" ")
44
50
  raise ArgumentError, "unknown options #{not_valid}", caller(1)
@@ -3,7 +3,7 @@ require 'utilrb/object/singleton_class'
3
3
 
4
4
  Utilrb.unless_faster do
5
5
  class Object
6
- # :call-seq
6
+ # call-seq:
7
7
  # attribute :name => default_value
8
8
  # attribute(:name) { default_value }
9
9
  #
@@ -1,7 +1,18 @@
1
1
  require 'utilrb/object/address'
2
+
3
+ class Object
4
+ # Returns true if this object has its own singleton class
5
+ def has_singleton?; defined? @singleton_class end
6
+ end
7
+
2
8
  if RUBY_VERSION >= "1.9"
3
9
  class Object
4
- def has_singleton?; defined? @singleton_class end
10
+ # Returns the singleton class for this object
11
+ #
12
+ # The first element of #ancestors on the returned singleton class is
13
+ # the singleton class itself. A #singleton_instance accessor is also
14
+ # defined, which returns the object instance the class is the singleton
15
+ # of.
5
16
  def singleton_class
6
17
  if defined? @singleton_class
7
18
  return @singleton_class
@@ -21,9 +32,15 @@ if RUBY_VERSION >= "1.9"
21
32
  end
22
33
  else
23
34
  class Object
24
- def has_singleton?
25
- defined? @singleton_class
26
- end
35
+ # Returns the singleton class for this object.
36
+ #
37
+ # In Ruby 1.8, makes sure that the #superclass method of the singleton class
38
+ # returns the object's class (instead of Class), as Ruby 1.9 does
39
+ #
40
+ # The first element of #ancestors on the returned singleton class is
41
+ # the singleton class itself. A #singleton_instance accessor is also
42
+ # defined, which returns the object instance the class is the singleton
43
+ # of.
27
44
  def singleton_class
28
45
  if defined? @singleton_class
29
46
  return @singleton_class
@@ -1,15 +1,23 @@
1
1
  require 'utilrb/gc/force'
2
+ require 'utilrb/object/attribute'
2
3
 
3
4
  module ObjectStats
4
- # Allocates no object
5
+ # The count of objects currently allocated
6
+ #
7
+ # It allocates no objects, which means that if
8
+ # a = ObjectStats.count
9
+ # b = ObjectStats.count
10
+ # then a == b
5
11
  def self.count
6
12
  count = 0
7
- ObjectSpace.each_object { |obj| count += 1}
13
+ ObjectSpace.each_object { |obj| count += 1 }
8
14
 
9
15
  count
10
16
  end
11
17
 
12
- # Allocates 1 Hash, which is included in the count
18
+ # Returns a klass => count hash counting the currently allocated objects
19
+ #
20
+ # It allocates 1 Hash, which is included in the count
13
21
  def self.count_by_class
14
22
  by_class = Hash.new(0)
15
23
  ObjectSpace.each_object { |obj|
@@ -19,10 +27,19 @@ module ObjectStats
19
27
  by_class
20
28
  end
21
29
 
22
- # Profiles the memory allocation in the block
23
- # If alive is true, then only non-gcable objects
24
- # are returned.
30
+ # Profiles how much objects has been allocated by the block. Returns a
31
+ # klass => count hash like count_by_class
32
+ #
33
+ # If alive is true, then only live objects are returned.
25
34
  def self.profile(alive = false)
35
+ if alive
36
+ GC.force
37
+ profile do
38
+ yield
39
+ GC.force
40
+ end
41
+ end
42
+
26
43
  already_disabled = GC.disable
27
44
  before = count_by_class
28
45
  yield
@@ -34,19 +51,173 @@ module ObjectStats
34
51
  merge(after) { |klass, old, new| new - old }.
35
52
  delete_if { |klass, count| count == 0 }
36
53
  end
54
+ end
37
55
 
38
- def self.stats(filter = nil)
39
- total_count = 0
40
- output = ""
41
- count_by_class.each do |klass, obj_count|
42
- total_count += obj_count
43
- if !filter || klass.name =~ filter
44
- output << klass.name << " " << obj_count.to_s << "\n"
45
- end
46
- end
47
-
48
- (output << "Total object count: #{total_count}")
56
+ # BenchmarkAllocation is a Benchmark-like interface to benchmark object allocation.
57
+ #
58
+ # == Formatting
59
+ # BenchmarkAllocation formats its output in two ways (see examples below)
60
+ # * first, each part of a class path is displayed in its own line, to reduce
61
+ # the output width
62
+ # * then, output is formatted so that it does not exceed
63
+ # BenchmarkAllocation::SCREEN_WIDTH width
64
+ #
65
+ #
66
+ # == Examples
67
+ #
68
+ # For instance,
69
+ #
70
+ # require 'utilrb/objectstats'
71
+ #
72
+ # module Namespace
73
+ # class MyClass
74
+ # end
75
+ # end
76
+ #
77
+ # BenchmarkAllocation.bm do |x|
78
+ # x.report("array") { Array.new }
79
+ # x.report("hash") { Hash.new }
80
+ # x.report("myclass") { MyClass.new }
81
+ # end
82
+ #
83
+ # will produce the output
84
+ #
85
+ # Array Hash Namespace::
86
+ # MyClass
87
+ # array 1 - -
88
+ # hash - 1 -
89
+ # myclass - - 1
90
+ #
91
+ # Like Benchmark, a rehearsal benchmark method, BenchmarkAllocation.bmbm
92
+ # is provided:
93
+ #
94
+ # require 'utilrb/objectstats'
95
+ # require 'delegate'
96
+ #
97
+ # module Namespace
98
+ # class MyClass
99
+ # end
100
+ # end
101
+ #
102
+ # delegate_klass = nil
103
+ # BenchmarkAllocation.bmbm do |x|
104
+ # x.report("array") { Array.new }
105
+ # x.report("hash") { Hash.new }
106
+ # x.report("myclass") { Namespace::MyClass.new }
107
+ # x.report("delegate") do
108
+ # delegate_klass ||= Class.new(DelegateClass(Namespace::MyClass)) do
109
+ # def self.name; "Delegate(MyClass)" end
110
+ # end
111
+ # delegate_klass.new(Namespace::MyClass.new)
112
+ # end
113
+ # end
114
+ #
115
+ # produces
116
+ #
117
+ # Rehearsal --------------------------------------------------------------------------------
118
+ #
119
+ # Array Class Delegate(MyClass) Hash Namespace:: String
120
+ # MyClass
121
+ # array 1 - - - - -
122
+ # hash - - - 1 - -
123
+ # myclass - - - - 1 -
124
+ # delegate 5 2 1 2 1 247
125
+ # ------------------------------------------------------------------------------------------
126
+ #
127
+ # Array Delegate(MyClass) Hash Namespace::
128
+ # MyClass
129
+ # array 1 - - -
130
+ # hash - - 1 -
131
+ # myclass - - - 1
132
+ # delegate - 1 - 1
133
+ #
134
+ class BenchmarkAllocation
135
+ MARGIN = 2
136
+ SCREEN_WIDTH = 90
137
+
138
+ def self.bm(label_width = nil)
139
+ yield(gather = new)
140
+ gather.format
49
141
  end
50
- end
142
+ def self.bmbm(label_width = nil)
143
+ yield(gather = new)
144
+
145
+ title = "Rehearsal"
146
+ puts title + " " + "-" * (SCREEN_WIDTH - title.length - 1)
147
+ gather.format
148
+ puts "-" * SCREEN_WIDTH
149
+
150
+ yield(gather = new)
151
+ gather.format
152
+ end
153
+
154
+ def format(screen_width = SCREEN_WIDTH, margin = MARGIN)
155
+ label_width ||= 0
156
+ column_names = profiles.inject([]) do |column_names, (label, profile)|
157
+ label_width = label_width < label.length ? label.length : label_width
158
+ column_names |= profile.keys
159
+ end.sort
51
160
 
161
+ # split at the :: to separate modules and klasses (reduce header sizes)
162
+ blocks = []
163
+ header, columns, current_width = nil
164
+ column_names.each_with_index do |name, col_index|
165
+ splitted = name.gsub(/::/, "::\n").split("\n")
166
+ width = splitted.map { |part| part.length }.max + MARGIN
167
+
168
+ if !current_width || (current_width + label_width + width > screen_width)
169
+ # start a new block
170
+ blocks << [[], []]
171
+ header, columns = blocks.last
172
+ current_width = 0
173
+ end
174
+
175
+ splitted.each_with_index do |part, line_index|
176
+ if !header[line_index]
177
+ header[line_index] = columns.map { |_, w| " " * w }
178
+ end
179
+ header[line_index] << "% #{width}s" % [part]
180
+ end
181
+ # Pad the remaining lines
182
+ header[splitted.size..-1].each_with_index do |line, index|
183
+ line << " " * width
184
+ end
185
+
186
+ columns << [name, width]
187
+ current_width += width
188
+ end
189
+
190
+ blocks.each do |header, columns|
191
+ puts
192
+ header.each { |line| puts " " * label_width + line.join("") }
193
+
194
+ profiles.each do |label, profile|
195
+ print "% #{label_width}s" % [label]
196
+ columns.each do |name, width|
197
+
198
+ if count = profile[name]
199
+ print "% #{width}i" % [profile[name] || 0]
200
+ else
201
+ print " " * (width - 1) + "-"
202
+ end
203
+ end
204
+ puts
205
+ end
206
+ end
207
+ end
208
+
209
+ attribute(:profiles) { Array.new }
210
+ def report(label)
211
+ result = ObjectStats.profile do
212
+ yield
213
+ end
214
+ result.inject({}) do |result, (klass, count)|
215
+ klass = klass.name
216
+ klass = "unknown" if !klass || klass.empty?
217
+ result[klass] = count
218
+ result
219
+ end
220
+ profiles << [label, result]
221
+ end
222
+ end
52
223
 
@@ -1,17 +1,22 @@
1
1
  class Queue
2
- # Returns all elements currently in the queue.
3
- # If +non_block+ is true, returns an empty array
4
- # if the queue is empty
5
- def get(non_block = false)
6
- while (Thread.critical = true; @que.empty?)
7
- return [] if non_block
8
- @waiting.push Thread.current
9
- Thread.stop
2
+ # Returns all elements currently in the queue. If +non_block+ is true,
3
+ # returns an empty array if the queue is empty
4
+ #
5
+ # WARNING: this method is NOT compatible with fastthread's Queue implementation
6
+ # If you rely on Queue#get and want to use fastthread, you will have to patch
7
+ # fastthread with patches/fastthread-queue-get.patch
8
+ unless instance_methods.include?("get")
9
+ def get(non_block = false)
10
+ while (Thread.critical = true; @que.empty?)
11
+ return [] if non_block
12
+ @waiting.push Thread.current
13
+ Thread.stop
14
+ end
15
+ result = @que.dup
16
+ @que.clear
17
+ result
18
+ ensure
19
+ Thread.critical = false
10
20
  end
11
- result = @que.dup
12
- @que.clear
13
- result
14
- ensure
15
- Thread.critical = false
16
21
  end
17
22
  end
data/lib/utilrb/set.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'utilrb/kernel/require'
2
+ require_dir(__FILE__)
@@ -0,0 +1,7 @@
1
+ require 'set'
2
+ class Set
3
+ def to_s
4
+ "{ #{map { |o| o.to_s }.join(", ")} }"
5
+ end
6
+ end
7
+
@@ -1,6 +1,38 @@
1
1
  class Time
2
+ # Converts this time into a h:m:s.ms representation
2
3
  def to_hms
3
4
  sec, usec = tv_sec, tv_usec
4
5
  "%i:%02i:%02i.%03i" % [sec / 3600, (sec % 3600) / 60, sec % 60, usec / 1000]
5
6
  end
7
+
8
+ # Creates a Time object from a h:m:s.ms representation. The following formats are allowed:
9
+ # s, s.ms, m:s, m:s.ms, h:m:s, h:m:s.ms
10
+ def self.from_hms(string)
11
+ unless string =~ /(?:^|:)(\d+)(?:$|\.)/
12
+ raise "invalid format #{string}"
13
+ end
14
+ hm, ms = $`, $'
15
+
16
+ s = Integer($1)
17
+ unless hm =~ /^(?:(\d*):)?(?:(\d*))?$/
18
+ raise "invalid format #{string}. found #{hm}, expected nothing, m: or h:m:"
19
+ end
20
+ h, m = if $2.empty? then
21
+ if $1 then [0, Integer($1)]
22
+ else [0, 0]
23
+ end
24
+ else [Integer($1), Integer($2)]
25
+ end
26
+
27
+ ms = if ms.empty? then 0
28
+ else
29
+ unless ms =~ /^\d*$/
30
+ raise "invalid format "#{string}"
31
+ end
32
+ ms += "0" * (3 - ms.size)
33
+ Integer(ms)
34
+ end
35
+
36
+ Time.at(Float(h * 3600 + m * 60 + s) + ms * 1.0e-3)
37
+ end
6
38
  end
@@ -0,0 +1,72 @@
1
+ --- fastthread-0.6.2/ext/fastthread/fastthread.c 2007-01-25 12:09:54.956708000 +0100
2
+ +++ fastthread-0.6.2.new/ext/fastthread/fastthread.c 2007-01-25 11:49:49.151455000 +0100
3
+ @@ -737,6 +737,61 @@ rb_queue_marshal_load(self, data)
4
+
5
+ static VALUE rb_queue_marshal_dump _((VALUE));
6
+
7
+ +
8
+ +/* call-seq:
9
+ + * Queue#get(non_block = false) => array
10
+ + *
11
+ + * Returns an array containing all elements currently in the queue. After a call
12
+ + * to #get, the queue is empty. If non_block is false, the call will block until
13
+ + * there is something in it. Otherwise, if the queue is empty, it returns an empty
14
+ + * array.
15
+ + */
16
+ +static VALUE
17
+ +rb_queue_get(argc, argv, self)
18
+ + int argc;
19
+ + VALUE *argv;
20
+ + VALUE self;
21
+ +{
22
+ + Queue *queue;
23
+ + int should_block;
24
+ + VALUE result;
25
+ + Data_Get_Struct(self, Queue, queue);
26
+ +
27
+ + if ( argc == 0 ) {
28
+ + should_block = 1;
29
+ + } else if ( argc == 1 ) {
30
+ + should_block = !RTEST(argv[0]);
31
+ + } else {
32
+ + rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
33
+ + }
34
+ +
35
+ + lock_mutex(&queue->mutex);
36
+ + if ( !queue->values.entries ) {
37
+ + /* NOTE: locking is not needed to check if the queue is empty
38
+ + since AFAIK C extensions are never preempted. I do as in #pop
39
+ + where it is done.
40
+ + */
41
+ + if ( !should_block ) {
42
+ + unlock_mutex(&queue->mutex);
43
+ + return rb_ary_new();
44
+ + }
45
+ + while (!queue->values.entries) {
46
+ + wait_condvar(&queue->value_available, &queue->mutex);
47
+ + }
48
+ + }
49
+ +
50
+ + result = rb_ary_new();
51
+ + while ( queue->values.entries ) {
52
+ + rb_ary_push(result, shift_list(&queue->values));
53
+ + }
54
+ + if ( queue->capacity ) {
55
+ + signal_condvar(&queue->space_available);
56
+ + }
57
+ + unlock_mutex(&queue->mutex);
58
+ +
59
+ + return result;
60
+ +}
61
+ +
62
+ static VALUE
63
+ rb_queue_marshal_dump(self)
64
+ VALUE self;
65
+ @@ -991,6 +1046,7 @@ static VALUE setup_classes(unused)
66
+ rb_define_method(rb_cQueue, "num_waiting", rb_queue_num_waiting, 0);
67
+ rb_define_method(rb_cQueue, "pop", rb_queue_pop, -1);
68
+ rb_define_method(rb_cQueue, "push", rb_queue_push, 1);
69
+ + rb_define_method(rb_cQueue, "get", rb_queue_get, -1);
70
+ rb_alias(rb_cQueue, rb_intern("<<"), rb_intern("push"));
71
+ rb_alias(rb_cQueue, rb_intern("deq"), rb_intern("pop"));
72
+ rb_alias(rb_cQueue, rb_intern("shift"), rb_intern("pop"));
@@ -74,6 +74,7 @@ class TC_Enumerable < Test::Unit::TestCase
74
74
  assert_equal([1, 2, 3, 4, 6, 8, 11], (a.union(b)).to_a)
75
75
  assert_equal([1, 3, 4], (a.intersection(b)).to_a)
76
76
  assert_equal([6, 8], (a.difference(b)).to_a)
77
+ assert(! (a == :bla)) # check #== behaves correctly with a non-enumerable
77
78
 
78
79
  a.delete(1)
79
80
  assert(! a.include?(1))
@@ -83,6 +84,8 @@ class TC_Enumerable < Test::Unit::TestCase
83
84
  assert([].to_value_set.empty?)
84
85
 
85
86
  assert([1, 2, 4, 3].to_value_set.clear.empty?)
87
+
88
+ assert_equal([1,3,5].to_value_set, [1, 2, 3, 4, 5, 6].to_value_set.delete_if { |v| v % 2 == 0 })
86
89
  end
87
90
  end
88
91
  end
@@ -3,17 +3,17 @@ require 'test_config'
3
3
  require 'utilrb/objectstats'
4
4
 
5
5
  class TC_ObjectStats < Test::Unit::TestCase
6
+ def teardown
7
+ GC.enable
8
+ end
9
+
10
+ def allocate_dead_hash; Hash.new; nil end
11
+
6
12
  def test_object_stats
7
13
  assert( ObjectStats.profile { ObjectStats.count }.empty?, "Object allocation profile changed" )
8
14
  assert_equal({ Hash => 1 }, ObjectStats.profile { ObjectStats.count_by_class }, "Object allocation profile changed")
9
15
  assert_equal({ Array => 1 }, ObjectStats.profile { test = [] })
10
16
  assert_equal({ Array => 2, Hash => 1 }, ObjectStats.profile { a, b = [], {} })
11
-
12
- GC.start
13
- GC.disable
14
- Hash.new
15
- assert([Hash, 1], ObjectStats.profile { Hash.new }.collect { |klass, count| [klass, count] })
16
- assert([Hash, -1], ObjectStats.profile { GC.start }.collect { |klass, count| [klass, count] })
17
17
  end
18
18
  end
19
19
 
data/test/test_time.rb ADDED
@@ -0,0 +1,31 @@
1
+ require 'test_config'
2
+ require 'utilrb/time'
3
+
4
+ class TC_Time < Test::Unit::TestCase
5
+ def test_to_hms
6
+ assert_equal("0:00:00.000", Time.at(0).to_hms)
7
+ assert_equal("0:00:00.100", Time.at(0.1).to_hms)
8
+ assert_equal("0:00:34.100", Time.at(34.1).to_hms)
9
+ assert_equal("0:21:34.100", Time.at(21 * 60 + 34.1).to_hms)
10
+ assert_equal("236:21:34.100", Time.at(236 * 3600 + 21 * 60 + 34.1).to_hms)
11
+ end
12
+ def test_from_hms
13
+ assert_equal(Time.at(0), Time.from_hms("0:00:00.000"))
14
+ assert_equal(Time.at(0.1), Time.from_hms("0:00:00.100"))
15
+ assert_equal(Time.at(34.1), Time.from_hms("0:00:34.100"))
16
+ assert_equal(Time.at(21 * 60 + 34.1), Time.from_hms("0:21:34.100"))
17
+ assert_equal(Time.at(236 * 3600 + 21 * 60 + 34.1), Time.from_hms("236:21:34.100"))
18
+
19
+ assert_equal(Time.at(0), Time.from_hms("0"))
20
+ assert_equal(Time.at(0), Time.from_hms(":0"))
21
+ assert_equal(Time.at(62), Time.from_hms("1:2"))
22
+ assert_equal(Time.at(3723), Time.from_hms("1:2:3"))
23
+
24
+ assert_equal(Time.at(0), Time.from_hms("0"))
25
+ assert_equal(Time.at(0), Time.from_hms("0."))
26
+ assert_in_delta(0, Time.at(1.2) - Time.from_hms("1.2"), 0.001)
27
+ assert_in_delta(0, Time.at(121.3) - Time.from_hms("2:1.3"), 0.001)
28
+ assert_in_delta(0, Time.at(3723.4) - Time.from_hms("1:2:3.4"), 0.001)
29
+ end
30
+ end
31
+
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.0
2
+ rubygems_version: 0.9.1
3
3
  specification_version: 1
4
4
  name: utilrb
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.2.2
7
- date: 2006-12-01 00:00:00 +01:00
6
+ version: 0.2.3
7
+ date: 2007-01-30 00:00:00 +01:00
8
8
  summary: Yet another Ruby toolkit
9
9
  require_paths:
10
10
  - lib
@@ -30,61 +30,64 @@ post_install_message:
30
30
  authors:
31
31
  - Sylvain Joyeux
32
32
  files:
33
+ - Changes.txt
34
+ - License.txt
35
+ - Manifest.txt
36
+ - README.txt
37
+ - Rakefile
33
38
  - bm/allocation.rb
34
39
  - bm/speed.rb
35
- - Changes.txt
36
40
  - ext/extconf.rb
37
41
  - ext/faster.cc
38
42
  - ext/swap.cc
39
43
  - ext/value_set.cc
44
+ - lib/utilrb.rb
40
45
  - lib/utilrb/array.rb
41
46
  - lib/utilrb/array/to_s.rb
42
47
  - lib/utilrb/common.rb
48
+ - lib/utilrb/enumerable.rb
43
49
  - lib/utilrb/enumerable/null.rb
44
50
  - lib/utilrb/enumerable/random_element.rb
45
- - lib/utilrb/enumerable.rb
46
51
  - lib/utilrb/enumerable/sequence.rb
47
52
  - lib/utilrb/enumerable/uniq.rb
48
- - lib/utilrb/exception/full_message.rb
49
53
  - lib/utilrb/exception.rb
50
- - lib/utilrb/gc/force.rb
54
+ - lib/utilrb/exception/full_message.rb
51
55
  - lib/utilrb/gc.rb
56
+ - lib/utilrb/gc/force.rb
52
57
  - lib/utilrb/hash.rb
53
58
  - lib/utilrb/hash/slice.rb
54
59
  - lib/utilrb/hash/to_s.rb
55
60
  - lib/utilrb/hash/to_sym_keys.rb
61
+ - lib/utilrb/kernel.rb
56
62
  - lib/utilrb/kernel/arity.rb
57
63
  - lib/utilrb/kernel/options.rb
58
64
  - lib/utilrb/kernel/poll.rb
59
- - lib/utilrb/kernel.rb
60
65
  - lib/utilrb/kernel/require.rb
61
66
  - lib/utilrb/kernel/swap.rb
67
+ - lib/utilrb/logger.rb
62
68
  - lib/utilrb/logger/forward.rb
63
69
  - lib/utilrb/logger/hierarchy.rb
64
- - lib/utilrb/logger.rb
70
+ - lib/utilrb/module.rb
65
71
  - lib/utilrb/module/ancestor_p.rb
66
72
  - lib/utilrb/module/attr_enumerable.rb
67
73
  - lib/utilrb/module/define_method.rb
68
74
  - lib/utilrb/module/include.rb
69
75
  - lib/utilrb/module/inherited_enumerable.rb
70
- - lib/utilrb/module.rb
76
+ - lib/utilrb/object.rb
71
77
  - lib/utilrb/object/address.rb
72
78
  - lib/utilrb/object/attribute.rb
73
- - lib/utilrb/object.rb
74
79
  - lib/utilrb/object/singleton_class.rb
75
80
  - lib/utilrb/objectstats.rb
76
- - lib/utilrb/queue/get.rb
77
81
  - lib/utilrb/queue.rb
78
- - lib/utilrb.rb
82
+ - lib/utilrb/queue/get.rb
83
+ - lib/utilrb/set.rb
84
+ - lib/utilrb/set/to_s.rb
79
85
  - lib/utilrb/time.rb
80
86
  - lib/utilrb/time/to_hms.rb
81
- - lib/utilrb/unbound_method/call.rb
82
87
  - lib/utilrb/unbound_method.rb
88
+ - lib/utilrb/unbound_method/call.rb
83
89
  - lib/utilrb/value_set.rb
84
- - License.txt
85
- - Manifest.txt
86
- - Rakefile
87
- - README.txt
90
+ - patches/fastthread-queue-get.patch
88
91
  - test/test_array.rb
89
92
  - test/test_config.rb
90
93
  - test/test_enumerable.rb
@@ -95,8 +98,10 @@ files:
95
98
  - test/test_module.rb
96
99
  - test/test_object.rb
97
100
  - test/test_objectstats.rb
101
+ - test/test_time.rb
98
102
  - test/test_unbound_method.rb
99
103
  test_files:
104
+ - test/test_time.rb
100
105
  - test/test_misc.rb
101
106
  - test/test_module.rb
102
107
  - test/test_unbound_method.rb
@@ -120,20 +125,20 @@ requirements: []
120
125
 
121
126
  dependencies:
122
127
  - !ruby/object:Gem::Dependency
123
- name: hoe
128
+ name: facets
124
129
  version_requirement:
125
130
  version_requirements: !ruby/object:Gem::Version::Requirement
126
131
  requirements:
127
- - - ">="
132
+ - - ">"
128
133
  - !ruby/object:Gem::Version
129
- version: 1.1.4
134
+ version: 0.0.0
130
135
  version:
131
136
  - !ruby/object:Gem::Dependency
132
- name: facets
137
+ name: hoe
133
138
  version_requirement:
134
139
  version_requirements: !ruby/object:Gem::Version::Requirement
135
140
  requirements:
136
- - - ">"
141
+ - - ">="
137
142
  - !ruby/object:Gem::Version
138
- version: 0.0.0
143
+ version: 1.1.7
139
144
  version: