ruby-ole 1.2.9 → 1.2.10

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog CHANGED
@@ -1,3 +1,13 @@
1
+ == 1.2.10 / 2009-07-20
2
+
3
+ - Mostly more performance enhancements, significantly faster for
4
+ certain operations.
5
+ - Using lots of files is faster due to new hash lookup for dirents by name.
6
+ - Writes of many files are faster now too as Dirent & FileTime serialization
7
+ has been improved.
8
+ - Certain operations from the filesystem api have been profiled and sped up.
9
+ - Don't use syswrite on jruby to avoid the buffered stream warnings.
10
+
1
11
  == 1.2.9 / 2009-07-14
2
12
 
3
13
  - Lots of performance enhancements for RangesIO.
data/README CHANGED
@@ -82,8 +82,9 @@ See the documentation for each class for more details.
82
82
 
83
83
  = TODO
84
84
 
85
- == 1.2.9
85
+ == 1.2.11
86
86
 
87
+ * internal api cleanup
87
88
  * add buffering to rangesio so that performance for small reads and writes
88
89
  isn't so awful. maybe try and remove the bottlenecks of unbuffered first
89
90
  with more profiling, then implement the buffering on top of that.
@@ -98,6 +99,7 @@ See the documentation for each class for more details.
98
99
 
99
100
  == 1.3.1
100
101
 
102
+ * case insensitive open mode would be nice
101
103
  * fix property sets a bit more. see TODO in Ole::Storage::MetaData
102
104
  * ability to zero out padding and unused blocks
103
105
  * case insensitive mode for ole/file_system?
data/Rakefile CHANGED
@@ -73,7 +73,7 @@ end
73
73
 
74
74
  Rake::GemPackageTask.new(spec) do |t|
75
75
  t.gem_spec = spec
76
- t.need_tar = true
76
+ t.need_tar = false
77
77
  t.need_zip = false
78
78
  t.package_dir = 'build'
79
79
  end
@@ -114,96 +114,3 @@ task :benchmark do
114
114
  end
115
115
  end
116
116
 
117
- =begin
118
-
119
- 1.2.1:
120
-
121
- user system total real
122
- write_1mb_1x5 73.920000 8.400000 82.320000 ( 91.893138)
123
-
124
- revision 17 (speed up AllocationTable#free_block by using
125
- @sparse attribute, and using Array#index otherwise):
126
-
127
- user system total real
128
- write_1mb_1x5 57.910000 6.190000 64.100000 ( 66.207993)
129
- write_1mb_2x5266.310000 31.750000 298.060000 (305.877203)
130
-
131
- add in extra resize_chain fix (return blocks to avoid calling
132
- AllocationTable#chain twice):
133
-
134
- user system total real
135
- write_1mb_1x5 43.140000 5.480000 48.620000 ( 51.835942)
136
-
137
- add in RangesIOResizeable fix (cache @blocks, to avoid calling
138
- AllocationTable#chain at all when resizing now, just pass it
139
- to AllocationTable#resize_chain):
140
-
141
- user system total real
142
- write_1mb_1x5 29.770000 5.180000 34.950000 ( 39.916747)
143
-
144
- 40 seconds is still a really long time to write out 5 megs.
145
- of course, this is all with a 1_000 byte block size, which is
146
- a very small wite. upping this to 100_000 bytes:
147
-
148
- user system total real
149
- write_1mb_1x5 0.540000 0.130000 0.670000 ( 1.051862)
150
-
151
- so it seems that that makes a massive difference. so i really
152
- need buffering in RangesIO if I don't want it to really hurt
153
- for small writes, as all the resize code is kind of expensive.
154
-
155
- one of the costly things at the moment, is RangesIO#offset_and_size,
156
- which is called for each write, and re-finds which range we are in.
157
- that should obviously be changed, to a fixed one that is invalidated
158
- on seeks. buffering would hide that problem to some extent, but i
159
- should fix it anyway.
160
-
161
- re-running the original 1.2.1 with 100_000 byte block size:
162
-
163
- user system total real
164
- write_1mb_1x5 15.590000 2.230000 17.820000 ( 18.704910)
165
-
166
- so there the really badly non-linear AllocationTable#resize_chain is
167
- being felt.
168
-
169
- back to current working copy, running full benchmark:
170
-
171
- user system total real
172
- write_1mb_1x5 0.530000 0.150000 0.680000 ( 0.708919)
173
- write_1mb_2x5227.940000 31.260000 259.200000 (270.200960)
174
-
175
- not surprisingly, the second case hasn't been helped much by the fixes
176
- so far, as they only really help multiple resizes and writes for a file.
177
- this could be pain in the new file system code - potentially searching
178
- through Dirent#children at creation time.
179
-
180
- to test, i'll profile creating 1_000 files, without writing anything:
181
-
182
- user system total real
183
- write_1mb_2x5 16.990000 1.830000 18.820000 ( 19.900568)
184
-
185
- hmmm, so thats not all of it. maybe its the initial chain calls, etc?
186
- writing 1 byte:
187
-
188
- user system total real
189
- write_1mb_1x5 0.520000 0.120000 0.640000 ( 0.660638)
190
- write_1mb_2x5 19.810000 2.280000 22.090000 ( 22.696214)
191
-
192
- weird.
193
-
194
- 100 bytes:
195
-
196
- user system total real
197
- write_1mb_1x5 0.560000 0.140000 0.700000 ( 1.424974)
198
- write_1mb_2x5 22.940000 2.840000 25.780000 ( 26.556346)
199
-
200
- 500 bytes:
201
-
202
- user system total real
203
- write_1mb_1x5 0.530000 0.150000 0.680000 ( 1.139738)
204
- write_1mb_2x5 77.260000 10.130000 87.390000 ( 91.671086)
205
-
206
- what happens there? very strange.
207
-
208
- =end
209
-
@@ -21,7 +21,7 @@ module Ole # :nodoc:
21
21
  class FormatError < StandardError # :nodoc:
22
22
  end
23
23
 
24
- VERSION = '1.2.9'
24
+ VERSION = '1.2.10'
25
25
 
26
26
  # options used at creation time
27
27
  attr_reader :params
@@ -180,7 +180,7 @@ module Ole # :nodoc:
180
180
 
181
181
  # serialize the dirents using the bbat
182
182
  RangesIOResizeable.open @bbat, 'w', :first_block => @header.dirent_start do |io|
183
- @dirents.each { |dirent| io.write dirent.to_s }
183
+ io.write @dirents.map { |dirent| dirent.to_s }.join
184
184
  padding = (io.size / @bbat.block_size.to_f).ceil * @bbat.block_size - io.size
185
185
  io.write 0.chr * padding
186
186
  @header.dirent_start = io.first_block
@@ -203,7 +203,7 @@ module Ole # :nodoc:
203
203
  @bbat.map! do |b|
204
204
  b == AllocationTable::BAT || b == AllocationTable::META_BAT ? AllocationTable::AVAIL : b
205
205
  end
206
-
206
+
207
207
  # currently we use a loop. this could be better, but basically,
208
208
  # the act of writing out the bat, itself requires blocks which get
209
209
  # recorded in the bat.
@@ -482,7 +482,7 @@ module Ole # :nodoc:
482
482
  # The blocks are Big or Small blocks depending on the table type.
483
483
  def blocks_to_ranges chain, size=nil
484
484
  # truncate the chain if required
485
- chain = chain[0...(size.to_f / block_size).ceil] if size
485
+ chain = chain[0, (size.to_f / block_size).ceil] if size
486
486
  # convert chain to ranges of the block size
487
487
  ranges = chain.map { |i| [block_size * i, block_size] }
488
488
  # truncate final range if required
@@ -526,8 +526,8 @@ module Ole # :nodoc:
526
526
  def free_block
527
527
  if @sparse
528
528
  i = index(AVAIL) and return i
529
+ @sparse = false
529
530
  end
530
- @sparse = false
531
531
  push AVAIL
532
532
  length - 1
533
533
  end
@@ -565,8 +565,14 @@ module Ole # :nodoc:
565
565
  end
566
566
 
567
567
  # Big blocks are kind of -1 based, in order to not clash with the header.
568
- def blocks_to_ranges blocks, size
569
- super blocks.map { |b| b + 1 }, size
568
+ def blocks_to_ranges chain, size=nil
569
+ #super chain.map { |b| b + 1 }, size
570
+ # duplicated from AllocationTable#blocks_to_ranges to avoid chain.map
571
+ # which was decent part of benchmark profile
572
+ chain = chain[0, (size.to_f / block_size).ceil] if size
573
+ ranges = chain.map { |i| [block_size * (i + 1), block_size] }
574
+ ranges.last[1] -= (ranges.length * block_size - size) if ranges.last and size
575
+ ranges
570
576
  end
571
577
  end
572
578
 
@@ -711,13 +717,21 @@ module Ole # :nodoc:
711
717
  AllocationTable::EOC, 0, 0.chr * 4
712
718
  ]
713
719
 
714
- # i think its just used by the tree building
715
- attr_accessor :idx
716
720
  # This returns all the children of this +Dirent+. It is filled in
717
721
  # when the tree structure is recreated.
718
- attr_accessor :children
719
- attr_accessor :name
722
+ attr_reader :children
723
+ attr_reader :name
720
724
  attr_reader :ole, :type, :create_time, :modify_time
725
+ attr_reader :parent
726
+
727
+ # i think its just used by the tree building
728
+ attr_accessor :idx
729
+
730
+ # these are for internal use and are used for faster lookup.
731
+ attr_reader :name_lookup
732
+ attr_writer :parent
733
+ protected :name_lookup, :parent=
734
+
721
735
  def initialize ole, values=DEFAULT, params={}
722
736
  @ole = ole
723
737
  values, params = DEFAULT, values if Hash === values
@@ -737,44 +751,46 @@ module Ole # :nodoc:
737
751
 
738
752
  # further extra type specific stuff
739
753
  if file?
740
- default_time = @ole.params[:update_timestamps] ? Time.now : nil
754
+ default_time = @ole.params[:update_timestamps] ? Types::FileTime.now : nil
741
755
  @create_time ||= default_time
742
756
  @modify_time ||= default_time
743
757
  @create_time = Types::Variant.load(Types::VT_FILETIME, create_time_str) if create_time_str
744
758
  @modify_time = Types::Variant.load(Types::VT_FILETIME, create_time_str) if modify_time_str
745
759
  @children = nil
760
+ @name_lookup = nil
746
761
  else
747
762
  @create_time = nil
748
763
  @modify_time = nil
749
764
  self.size = 0 unless @type == :root
750
765
  @children = []
766
+ @name_lookup = {}
751
767
  end
768
+
769
+ @parent = nil
752
770
 
753
771
  # to silence warnings. used for tree building at load time
754
772
  # only.
755
773
  @idx = nil
756
774
  end
757
775
 
776
+ def name= name
777
+ if @parent
778
+ map = @parent.instance_variable_get :@name_lookup
779
+ map.delete @name
780
+ map[name] = self
781
+ end
782
+ @name = name
783
+ end
784
+
785
+ def children= children
786
+ @children = []
787
+ children.each { |child| self << child }
788
+ end
789
+
758
790
  def open mode='r'
759
791
  raise Errno::EISDIR unless file?
760
792
  io = RangesIOMigrateable.new self, mode
761
- # TODO work on the mode string stuff a bit more.
762
- # maybe let the io object know about the mode, so it can refuse
763
- # to work for read/write appropriately. maybe redefine all unusable
764
- # methods using singleton class to throw errors.
765
- # for now, i just want to implement truncation on use of 'w'. later,
766
- # i need to do 'a' etc.
767
- case mode
768
- when 'r', 'r+'
769
- # as i don't enforce reading/writing, nothing changes here. kind of
770
- # need to enforce tt if i want modify times to work better.
771
- @modify_time = Time.now if mode == 'r+'
772
- when 'w'
773
- @modify_time = Time.now
774
- # io.truncate 0
775
- #else
776
- # raise NotImplementedError, "unsupported mode - #{mode.inspect}"
777
- end
793
+ @modify_time = Types::FileTime.now if io.mode.writeable?
778
794
  if block_given?
779
795
  begin yield io
780
796
  ensure; io.close
@@ -798,7 +814,7 @@ module Ole # :nodoc:
798
814
 
799
815
  # maybe need some options regarding case sensitivity.
800
816
  def / name
801
- children.find { |child| name === child.name }
817
+ @name_lookup[name]
802
818
  end
803
819
 
804
820
  def [] idx
@@ -887,11 +903,23 @@ module Ole # :nodoc:
887
903
  str + '>'
888
904
  end
889
905
 
890
- def delete child
906
+ def << child
907
+ child.parent = self
908
+ @name_lookup[child.name] = child
909
+ @children << child
910
+ end
911
+
912
+ # remove the Dirent +child+ from the children array, truncating the data
913
+ # by default.
914
+ def delete child, truncate=true
891
915
  # remove from our child array, so that on reflatten and re-creation of @dirents, it will be gone
892
- raise ArgumentError, "#{child.inspect} not a child of #{self.inspect}" unless @children.delete child
916
+ unless @children.delete(child)
917
+ raise ArgumentError, "#{child.inspect} not a child of #{self.inspect}"
918
+ end
919
+ @name_lookup.delete(child.name)
920
+ child.parent = nil
893
921
  # free our blocks
894
- child.open { |io| io.truncate 0 }
922
+ child.open { |io| io.truncate 0 } if child.file?
895
923
  end
896
924
 
897
925
  def self.copy src, dst
@@ -902,7 +930,7 @@ module Ole # :nodoc:
902
930
  if src.dir?
903
931
  src.children.each do |src_child|
904
932
  dst_child = Dirent.new dst.ole, :type => src_child.type
905
- dst.children << dst_child
933
+ dst << dst_child
906
934
  Dirent.copy src_child, dst_child
907
935
  end
908
936
  else
@@ -45,12 +45,13 @@ module Ole # :nodoc:
45
45
  # (change it)
46
46
  def dirent_from_path path
47
47
  dirent = @root
48
- path = file.expand_path path
49
- path = path.sub(/^\/*/, '').sub(/\/*$/, '').split(/\/+/)
48
+ path = file.expand_path(path).split('/')
50
49
  until path.empty?
50
+ part = path.shift
51
+ next if part.empty?
51
52
  return nil if dirent.file?
52
- return nil unless dirent = dirent/path.shift
53
- end
53
+ return nil unless dirent = dirent/part
54
+ end
54
55
  dirent
55
56
  end
56
57
 
@@ -107,14 +108,20 @@ module Ole # :nodoc:
107
108
  end
108
109
 
109
110
  def expand_path path
110
- # get the raw stored pwd value (its blank for root)
111
- pwd = @ole.dir.instance_variable_get :@pwd
112
- # its only absolute if it starts with a '/'
113
- path = "#{pwd}/#{path}" unless path =~ /^\//
111
+ # its already absolute if it starts with a '/'
112
+ unless path =~ /^\//
113
+ # get the raw stored pwd value (its blank for root)
114
+ pwd = @ole.dir.instance_variable_get :@pwd
115
+ path = "#{pwd}/#{path}"
116
+ end
114
117
  # at this point its already absolute. we use File.expand_path
115
118
  # just for the .. and . handling
116
119
  # No longer use RUBY_PLATFORM =~ /win/ as it matches darwin. better way?
117
- File.expand_path(path)[File::ALT_SEPARATOR == "\\" ? (2..-1) : (0..-1)]
120
+ if File::ALT_SEPARATOR != "\\"
121
+ File.expand_path(path)
122
+ else
123
+ File.expand_path(path)[2..-1]
124
+ end
118
125
  end
119
126
 
120
127
  # +orig_path+ is just so that we can use the requested path
@@ -122,7 +129,7 @@ module Ole # :nodoc:
122
129
  def dirent_from_path path, orig_path=nil
123
130
  orig_path ||= path
124
131
  dirent = @ole.dirent_from_path path
125
- raise Errno::ENOENT, orig_path unless dirent
132
+ raise Errno::ENOENT, orig_path unless dirent
126
133
  raise Errno::EISDIR, orig_path if dirent.dir?
127
134
  dirent
128
135
  end
@@ -152,7 +159,7 @@ module Ole # :nodoc:
152
159
  # a get_parent_dirent function.
153
160
  parent_path, basename = File.split expand_path(path)
154
161
  parent = @ole.dir.send :dirent_from_path, parent_path, path
155
- parent.children << dirent = Dirent.new(@ole, :type => :file, :name => basename)
162
+ parent << dirent = Dirent.new(@ole, :type => :file, :name => basename)
156
163
  end
157
164
  else
158
165
  dirent = dirent_from_path path
@@ -211,31 +218,20 @@ module Ole # :nodoc:
211
218
  1 + 1
212
219
  end
213
220
  # reparent the dirent
214
- from_parent_path, from_basename = File.split expand_path(from_path)
215
221
  to_parent_path, to_basename = File.split expand_path(to_path)
216
- from_parent = @ole.dir.send :dirent_from_path, from_parent_path, from_path
222
+ from_parent = dirent.parent
217
223
  to_parent = @ole.dir.send :dirent_from_path, to_parent_path, to_path
218
- from_parent.children.delete dirent
224
+ from_parent.delete dirent, false
219
225
  # and also change its name
220
226
  dirent.name = to_basename
221
- to_parent.children << dirent
227
+ to_parent << dirent
222
228
  0
223
229
  end
224
230
 
225
- # crappy copy from Dir.
226
231
  def unlink(*paths)
227
232
  paths.each do |path|
228
- dirent = @ole.dirent_from_path path
229
- # i think we should free all of our blocks from the
230
- # allocation table.
231
- # i think if you run repack, all free blocks should get zeroed,
232
- # but currently the original data is there unmodified.
233
- open(path) { |f| f.truncate 0 }
234
- # remove ourself from our parent, so we won't be part of the dir
235
- # tree at save time.
236
- parent_path, basename = File.split expand_path(path)
237
- parent = @ole.dir.send :dirent_from_path, parent_path, path
238
- parent.children.delete dirent
233
+ dirent = dirent_from_path path
234
+ dirent.parent.delete dirent
239
235
  end
240
236
  paths.length # hmmm. as per ::File ?
241
237
  end
@@ -243,15 +239,17 @@ module Ole # :nodoc:
243
239
  end
244
240
 
245
241
  #
246
- # an *instance* of this class is supposed to provide similar methods
242
+ # An *instance* of this class is supposed to provide similar methods
247
243
  # to the class methods of Dir itself.
248
244
  #
249
- # pretty complete. like zip/zipfilesystem's implementation, i provide
245
+ # Fairly complete - like zip/zipfilesystem's implementation, i provide
250
246
  # everything except chroot and glob. glob could be done with a glob
251
- # to regex regex, and then simply match in the entries array... although
252
- # recursive glob complicates that somewhat.
247
+ # to regex conversion, and then simply match in the entries array...
248
+ # although recursive glob complicates that somewhat.
249
+ #
250
+ # Dir.chroot, Dir.glob, Dir.[], and Dir.tmpdir is the complete list of
251
+ # methods still missing.
253
252
  #
254
- # Dir.chroot, Dir.glob, Dir.[], and Dir.tmpdir is the complete list.
255
253
  class DirClass
256
254
  def initialize ole
257
255
  @ole = ole
@@ -271,11 +269,8 @@ module Ole # :nodoc:
271
269
 
272
270
  def open path
273
271
  dir = Dir.new path, entries(path)
274
- if block_given?
275
- yield dir
276
- else
277
- dir
278
- end
272
+ return dir unless block_given?
273
+ yield dir
279
274
  end
280
275
 
281
276
  # as for file, explicit alias to inhibit block
@@ -286,17 +281,14 @@ module Ole # :nodoc:
286
281
  # pwd is always stored without the trailing slash. we handle
287
282
  # the root case here
288
283
  def pwd
289
- if @pwd.empty?
290
- '/'
291
- else
292
- @pwd
293
- end
284
+ return '/' if @pwd.empty?
285
+ @pwd
294
286
  end
295
287
  alias getwd :pwd
296
288
 
297
289
  def chdir orig_path
298
290
  # make path absolute, squeeze slashes, and remove trailing slash
299
- path = @ole.file.expand_path(orig_path).gsub(/\/+/, '/').sub(/\/$/, '')
291
+ path = @ole.file.expand_path(orig_path).squeeze('/').sub(/\/$/, '')
300
292
  # this is just for the side effects of the exceptions if invalid
301
293
  dirent_from_path path, orig_path
302
294
  if block_given?
@@ -331,10 +323,7 @@ module Ole # :nodoc:
331
323
  entries(path).each(&block)
332
324
  end
333
325
 
334
- # there are some other important ones, like:
335
- # chroot (!), glob etc etc. for now, i think
336
326
  def mkdir path
337
- # as for rmdir below:
338
327
  parent_path, basename = File.split @ole.file.expand_path(path)
339
328
  # note that we will complain about the full path despite accessing
340
329
  # the parent path. this is consistent with ::Dir
@@ -342,29 +331,14 @@ module Ole # :nodoc:
342
331
  # now, we first should ensure that it doesn't already exist
343
332
  # either as a file or a directory.
344
333
  raise Errno::EEXIST, path if parent/basename
345
- parent.children << Dirent.new(@ole, :type => :dir, :name => basename)
334
+ parent << Dirent.new(@ole, :type => :dir, :name => basename)
346
335
  0
347
336
  end
348
337
 
349
338
  def rmdir path
350
339
  dirent = dirent_from_path path
351
340
  raise Errno::ENOTEMPTY, path unless dirent.children.empty?
352
-
353
- # now delete it, how to do that? the canonical representation that is
354
- # maintained is the root tree, and the children array. we must remove it
355
- # from the children array.
356
- # we need the parent then. this sucks but anyway:
357
- # we need to split the path. but before we can do that, we need
358
- # to expand it first. eg. say we need the parent to unlink
359
- # a/b/../c. the parent should be a, not a/b/.., or a/b.
360
- parent_path, basename = File.split @ole.file.expand_path(path)
361
- # this shouldn't be able to fail if the above didn't
362
- parent = dirent_from_path parent_path
363
- # note that the way this currently works, on save and repack time this will get
364
- # reflected. to work properly, ie to make a difference now it would have to re-write
365
- # the dirent. i think that Ole::Storage#close will handle that. and maybe include a
366
- # #repack.
367
- parent.children.delete dirent
341
+ dirent.parent.delete dirent
368
342
  0 # hmmm. as per ::Dir ?
369
343
  end
370
344
  alias delete :rmdir
@@ -373,7 +347,6 @@ module Ole # :nodoc:
373
347
  # note that there is nothing remotely ole specific about
374
348
  # this class. it simply provides the dir like sequential access
375
349
  # methods on top of an array.
376
- # hmm, doesn't throw the IOError's on use of a closed directory...
377
350
  class Dir
378
351
  include Enumerable
379
352
 
@@ -408,14 +381,12 @@ module Ole # :nodoc:
408
381
  raise IOError if @closed
409
382
  @pos = [[0, pos].max, @entries.length].min
410
383
  end
384
+ alias tell :pos
385
+ alias seek :pos=
411
386
 
412
387
  def rewind
413
- raise IOError if @closed
414
- @pos = 0
388
+ seek 0
415
389
  end
416
-
417
- alias tell :pos
418
- alias seek :pos=
419
390
  end
420
391
  end
421
392
  end
@@ -57,8 +57,34 @@ module Ole # :nodoc:
57
57
  # for VT_FILETIME
58
58
  class FileTime < DateTime
59
59
  SIZE = 8
60
+
61
+ # DateTime.new is slow... faster version for FileTime
62
+ def self.new year, month, day, hour=0, min=0, sec=0, usec=0
63
+ # DateTime will remove leap and leap-leap seconds
64
+ sec = 59 if sec > 59
65
+ if month <= 2
66
+ month += 12
67
+ year -= 1
68
+ end
69
+ y = year + 4800
70
+ m = month - 3
71
+ jd = day + (153 * m + 2) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 32045
72
+ fr = hour / 24.0 + min / 1440.0 + sec / 86400.0
73
+ new! jd + fr - 0.5, 0, ITALY
74
+ end
75
+
76
+ def self.from_time time
77
+ new(*(time.to_a[0, 6].reverse + [time.usec]))
78
+ end
79
+
80
+ def self.now
81
+ from_time Time.now
82
+ end
83
+
60
84
  EPOCH = new 1601, 1, 1
61
85
 
86
+ #def initialize year, month, day, hour, min, sec
87
+
62
88
  # Create a +DateTime+ object from a struct +FILETIME+
63
89
  # (http://msdn2.microsoft.com/en-us/library/ms724284.aspx).
64
90
  #
@@ -68,27 +94,26 @@ module Ole # :nodoc:
68
94
  low, high = str.to_s.unpack 'V2'
69
95
  # we ignore these, without even warning about it
70
96
  return nil if low == 0 and high == 0
71
- # switched to rational, and fixed the off by 1 second error i sometimes got.
72
- # time = EPOCH + (high * (1 << 32) + low) / 1e7 / 86400 rescue return
73
- # use const_get to ensure we can return anything which subclasses this (VT_DATE?)
74
- const_get('EPOCH') + Rational(high * (1 << 32) + low, 1e7.to_i * 86400) rescue return
75
- # extra sanity check...
76
- #unless (1800...2100) === time.year
77
- # Log.warn "ignoring unlikely time value #{time.to_s}"
78
- # return nil
79
- #end
80
- #time
97
+ # the + 0.00001 here stinks a bit...
98
+ seconds = (high * (1 << 32) + low) / 1e7 + 0.00001
99
+ EPOCH + seconds / 86400 rescue return
81
100
  end
82
-
101
+
83
102
  # +time+ should be able to be either a Time, Date, or DateTime.
84
103
  def self.dump time
85
- # i think i'll convert whatever i get to be a datetime, because of
86
- # the covered range.
87
104
  return 0.chr * SIZE unless time
88
- time = time.send(:to_datetime) if Time === time
89
- # don't bother to use const_get here
90
- bignum = (time - EPOCH) * 86400 * 1e7.to_i
91
- high, low = bignum.divmod 1 << 32
105
+ # convert whatever is given to be a datetime, to handle the large range
106
+ case time
107
+ when Date # this includes DateTime & FileTime
108
+ when Time
109
+ time = from_time time
110
+ else
111
+ raise ArgumentError, 'unknown time argument - %p' % [time]
112
+ end
113
+ # round to milliseconds (throwing away nanosecond precision) to
114
+ # compensate for using Float-based DateTime
115
+ nanoseconds = ((time - EPOCH).to_f * 864000000).round * 1000
116
+ high, low = nanoseconds.divmod 1 << 32
92
117
  [low, high].pack 'V2'
93
118
  end
94
119
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-ole
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.9
4
+ version: 1.2.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charles Lowe
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-07-14 00:00:00 +10:00
12
+ date: 2009-07-20 00:00:00 +10:00
13
13
  default_executable:
14
14
  dependencies: []
15
15