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 +10 -0
- data/README +3 -1
- data/Rakefile +1 -94
- data/lib/ole/storage/base.rb +62 -34
- data/lib/ole/storage/file_system.rb +40 -69
- data/lib/ole/types/base.rb +42 -17
- metadata +2 -2
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.
|
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 =
|
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
|
-
|
data/lib/ole/storage/base.rb
CHANGED
@@ -21,7 +21,7 @@ module Ole # :nodoc:
|
|
21
21
|
class FormatError < StandardError # :nodoc:
|
22
22
|
end
|
23
23
|
|
24
|
-
VERSION = '1.2.
|
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.
|
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
|
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
|
569
|
-
super
|
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
|
-
|
719
|
-
|
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] ?
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
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
|
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/
|
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
|
-
#
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
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,
|
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
|
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 =
|
222
|
+
from_parent = dirent.parent
|
217
223
|
to_parent = @ole.dir.send :dirent_from_path, to_parent_path, to_path
|
218
|
-
from_parent.
|
224
|
+
from_parent.delete dirent, false
|
219
225
|
# and also change its name
|
220
226
|
dirent.name = to_basename
|
221
|
-
to_parent
|
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 =
|
229
|
-
|
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
|
-
#
|
242
|
+
# An *instance* of this class is supposed to provide similar methods
|
247
243
|
# to the class methods of Dir itself.
|
248
244
|
#
|
249
|
-
#
|
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
|
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
|
-
|
275
|
-
|
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).
|
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
|
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
|
-
|
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
|
data/lib/ole/types/base.rb
CHANGED
@@ -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
|
-
#
|
72
|
-
|
73
|
-
|
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
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
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.
|
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-
|
12
|
+
date: 2009-07-20 00:00:00 +10:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|