ruby-ole 1.2.8.2 → 1.2.9

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog CHANGED
@@ -1,3 +1,7 @@
1
+ == 1.2.9 / 2009-07-14
2
+
3
+ - Lots of performance enhancements for RangesIO.
4
+
1
5
  == 1.2.8.2 / 2009-01-01
2
6
 
3
7
  - Update code to support ruby 1.9.1
@@ -57,23 +57,14 @@ class RangesIO
57
57
  @params = {:close_parent => false}.merge params
58
58
  @mode = IO::Mode.new mode
59
59
  @io = io
60
- # convert ranges to arrays. check for negative ranges?
61
- ranges ||= [0, io.size]
62
- @ranges = ranges.map { |r| Range === r ? [r.begin, r.end - r.begin] : r }
63
- # calculate size
64
- @size = @ranges.inject(0) { |total, (pos, len)| total + len }
65
60
  # initial position in the file
66
61
  @pos = 0
67
-
62
+ self.ranges = ranges || [[0, io.size]]
68
63
  # handle some mode flags
69
64
  truncate 0 if @mode.truncate?
70
65
  seek size if @mode.append?
71
66
  end
72
-
73
- #IOError: closed stream
74
- # get this for reading, writing, everything...
75
- #IOError: not opened for writing
76
-
67
+
77
68
  # add block form. TODO add test for this
78
69
  def self.open(*args, &block)
79
70
  ranges_io = new(*args)
@@ -86,6 +77,36 @@ class RangesIO
86
77
  end
87
78
  end
88
79
 
80
+ def ranges= ranges
81
+ # convert ranges to arrays. check for negative ranges?
82
+ ranges = ranges.map { |r| Range === r ? [r.begin, r.end - r.begin] : r }
83
+ # combine ranges
84
+ if @params[:combine] == false
85
+ # might be useful for debugging...
86
+ @ranges = ranges
87
+ else
88
+ @ranges = []
89
+ next_pos = nil
90
+ ranges.each do |pos, len|
91
+ if next_pos == pos
92
+ @ranges.last[1] += len
93
+ next_pos += len
94
+ else
95
+ @ranges << [pos, len]
96
+ next_pos = pos + len
97
+ end
98
+ end
99
+ end
100
+ # calculate cumulative offsets from range sizes
101
+ @size = 0
102
+ @offsets = []
103
+ @ranges.each do |pos, len|
104
+ @offsets << @size
105
+ @size += len
106
+ end
107
+ self.pos = @pos
108
+ end
109
+
89
110
  def pos= pos, whence=IO::SEEK_SET
90
111
  case whence
91
112
  when IO::SEEK_SET
@@ -95,30 +116,36 @@ class RangesIO
95
116
  pos = @size + pos
96
117
  else raise Errno::EINVAL
97
118
  end
98
- raise Errno::EINVAL unless (0...@size) === pos
119
+ raise Errno::EINVAL unless (0..@size) === pos
99
120
  @pos = pos
121
+
122
+ # do a binary search throuh @offsets to find the active range.
123
+ a, c, b = 0, 0, @offsets.length
124
+ while a < b
125
+ c = (a + b) / 2
126
+ pivot = @offsets[c]
127
+ if pos == pivot
128
+ @active = c
129
+ return
130
+ elsif pos < pivot
131
+ b = c
132
+ else
133
+ a = c + 1
134
+ end
135
+ end
136
+
137
+ @active = a - 1
100
138
  end
101
139
 
102
140
  alias seek :pos=
103
141
  alias tell :pos
104
142
 
105
- def close
106
- @io.close if @params[:close_parent]
143
+ def rewind
144
+ seek 0
107
145
  end
108
146
 
109
- # returns the [+offset+, +size+], pair inorder to read/write at +pos+
110
- # (like a partial range), and its index.
111
- def offset_and_size pos
112
- total = 0
113
- ranges.each_with_index do |(offset, size), i|
114
- if pos <= total + size
115
- diff = pos - total
116
- return [offset + diff, size - diff], i
117
- end
118
- total += size
119
- end
120
- # should be impossible for any valid pos, (0...size) === pos
121
- raise ArgumentError, "no range for pos #{pos.inspect}"
147
+ def close
148
+ @io.close if @params[:close_parent]
122
149
  end
123
150
 
124
151
  def eof?
@@ -130,24 +157,26 @@ class RangesIO
130
157
  data = ''
131
158
  return data if eof?
132
159
  limit ||= size
133
- partial_range, i = offset_and_size @pos
134
- # this may be conceptually nice (create sub-range starting where we are), but
135
- # for a large range array its pretty wasteful. even the previous way was. but
136
- # i'm not trying to optimize this atm. it may even go to c later if necessary.
137
- ([partial_range] + ranges[i+1..-1]).each do |pos, len|
160
+ pos, len = @ranges[@active]
161
+ diff = @pos - @offsets[@active]
162
+ pos += diff
163
+ len -= diff
164
+ loop do
138
165
  @io.seek pos
139
166
  if limit < len
140
- # convoluted, to handle read errors. s may be nil
141
- s = @io.read limit
142
- @pos += s.length if s
143
- break data << s
167
+ s = @io.read(limit).to_s
168
+ @pos += s.length
169
+ data << s
170
+ break
144
171
  end
145
- # convoluted, to handle ranges beyond the size of the file
146
- s = @io.read len
147
- @pos += s.length if s
172
+ s = @io.read(len).to_s
173
+ @pos += s.length
148
174
  data << s
149
175
  break if s.length != len
150
176
  limit -= len
177
+ break if @active == @ranges.length - 1
178
+ @active += 1
179
+ pos, len = @ranges[@active]
151
180
  end
152
181
  data
153
182
  end
@@ -164,8 +193,6 @@ class RangesIO
164
193
  end
165
194
 
166
195
  def write data
167
- # short cut. needed because truncate 0 may return no ranges, instead of empty range,
168
- # thus offset_and_size fails.
169
196
  return 0 if data.empty?
170
197
  data_pos = 0
171
198
  # if we don't have room, we can use the truncate hook to make more space.
@@ -176,8 +203,11 @@ class RangesIO
176
203
  raise IOError, "unable to grow #{inspect} to write #{data.length} bytes"
177
204
  end
178
205
  end
179
- partial_range, i = offset_and_size @pos
180
- ([partial_range] + ranges[i+1..-1]).each do |pos, len|
206
+ pos, len = @ranges[@active]
207
+ diff = @pos - @offsets[@active]
208
+ pos += diff
209
+ len -= diff
210
+ loop do
181
211
  @io.seek pos
182
212
  if data_pos + len > data.length
183
213
  chunk = data[data_pos..-1]
@@ -189,6 +219,9 @@ class RangesIO
189
219
  @io.write data[data_pos, len]
190
220
  @pos += len
191
221
  data_pos += len
222
+ break if @active == @ranges.length - 1
223
+ @active += 1
224
+ pos, len = @ranges[@active]
192
225
  end
193
226
  data_pos
194
227
  end
@@ -202,17 +235,13 @@ class RangesIO
202
235
  def gets
203
236
  s = read 1024
204
237
  i = s.index "\n"
205
- @pos -= s.length - (i+1)
238
+ self.pos -= s.length - (i+1)
206
239
  s[0..i]
207
240
  end
208
241
  alias readline :gets
209
242
 
210
243
  def inspect
211
- # the rescue is for empty files
212
- pos, len = (@ranges[offset_and_size(@pos).last] rescue [nil, nil])
213
- range_str = pos ? "#{pos}..#{pos+len}" : 'nil'
214
- "#<#{self.class} io=#{io.inspect}, size=#@size, pos=#@pos, "\
215
- "range=#{range_str}>"
244
+ "#<#{self.class} io=#{io.inspect}, size=#{@size}, pos=#{@pos}>"
216
245
  end
217
246
  end
218
247
 
@@ -21,7 +21,7 @@ module Ole # :nodoc:
21
21
  class FormatError < StandardError # :nodoc:
22
22
  end
23
23
 
24
- VERSION = '1.2.8.2'
24
+ VERSION = '1.2.9'
25
25
 
26
26
  # options used at creation time
27
27
  attr_reader :params
@@ -61,7 +61,10 @@ module Ole # :nodoc:
61
61
  else
62
62
  @io.flush
63
63
  # this is for the benefit of ruby-1.9
64
- @io.syswrite('') if @io.respond_to?(:syswrite)
64
+ # generates warnings on jruby though... :/
65
+ if RUBY_PLATFORM != 'java' and @io.respond_to?(:syswrite)
66
+ @io.syswrite('')
67
+ end
65
68
  true
66
69
  end
67
70
  rescue IOError
@@ -602,8 +605,8 @@ module Ole # :nodoc:
602
605
  # note that old_blocks is != @ranges.length necessarily. i'm planning to write a
603
606
  # merge_ranges function that merges sequential ranges into one as an optimization.
604
607
  @bat.resize_chain @blocks, size
605
- @ranges = @bat.ranges @blocks, size
606
- @pos = @size if @pos > size
608
+ @pos = size if @pos > size
609
+ self.ranges = @bat.ranges(@blocks, size)
607
610
  self.first_block = @blocks.empty? ? AllocationTable::EOC : @blocks.first
608
611
 
609
612
  # don't know if this is required, but we explicitly request our @io to grow if necessary
@@ -612,8 +615,6 @@ module Ole # :nodoc:
612
615
  # maybe its ok to just seek out there later??
613
616
  max = @ranges.map { |pos, len| pos + len }.max || 0
614
617
  @io.truncate max if max > @io.size
615
-
616
- @size = size
617
618
  end
618
619
  end
619
620
 
@@ -633,8 +634,8 @@ module Ole # :nodoc:
633
634
  # bat migration needed! we need to backup some data. the amount of data
634
635
  # should be <= @ole.header.threshold, so we can just hold it all in one buffer.
635
636
  # backup this
636
- pos = @pos
637
- @pos = 0
637
+ pos = [@pos, size].min
638
+ self.pos = 0
638
639
  keep = read [@size, size].min
639
640
  # this does a normal truncate to 0, removing our presence from the old bat, and
640
641
  # rewrite the dirent's first_block
@@ -645,9 +646,9 @@ module Ole # :nodoc:
645
646
  # important to do this now, before the write. as the below write will always
646
647
  # migrate us back to sbat! this will now allocate us +size+ in the new bat.
647
648
  super
648
- @pos = 0
649
+ self.pos = 0
649
650
  write keep
650
- @pos = pos
651
+ self.pos = pos
651
652
  else
652
653
  super
653
654
  end
@@ -31,20 +31,13 @@ class TestRangesIO < Test::Unit::TestCase
31
31
 
32
32
  def test_basics
33
33
  assert_equal 160, @io.size
34
- assert_match %r{size=160,.*range=100\.\.200}, @io.inspect
34
+ assert_match %r{size=160}, @io.inspect
35
35
  end
36
36
 
37
37
  def test_truncate
38
38
  assert_raises(NotImplementedError) { @io.size += 10 }
39
39
  end
40
40
 
41
- def test_offset_and_size
42
- assert_equal [[100, 100], 0], @io.offset_and_size(0)
43
- assert_equal [[150, 50], 0], @io.offset_and_size(50)
44
- assert_equal [[5, 5], 1], @io.offset_and_size(105)
45
- assert_raises(ArgumentError) { @io.offset_and_size 1000 }
46
- end
47
-
48
41
  def test_seek
49
42
  @io.pos = 10
50
43
  @io.seek(-100, IO::SEEK_END)
@@ -1,14 +1,13 @@
1
1
  #! /usr/bin/ruby
2
2
 
3
3
  $: << File.dirname(__FILE__) + '/../lib'
4
+ #require 'rubygems'
4
5
 
5
6
  require 'test/unit'
6
7
  require 'ole/storage'
7
8
  require 'digest/sha1'
8
9
  require 'stringio'
9
10
  require 'tempfile'
10
- require 'zlib'
11
- require 'base64'
12
11
 
13
12
  #
14
13
  # = TODO
@@ -108,49 +107,18 @@ class TestStorageRead < Test::Unit::TestCase
108
107
  def test_read
109
108
  # the regular String#hash was different on the mac, so asserting
110
109
  # against full strings. switch this to sha1 instead of this fugly blob
111
- data = <<-end
112
- eJxjZGBgYkADAABKAAQ=
113
-
114
- eJxjZPj3n5mLgeE/EDBwMjGAwAEwyeAmAyR8M5OL8ovz00oUwvOLUhTM9Ax0
115
- XfKzS3NT80oYuEDywSBxl/xkBgEgD8TWA3LA8npmYGMAKWQZqA==
116
-
117
- eJztVs9rE0EU/mZ32m5smyZtUlKQunhIezAV6knwYGoRxRLQBG89zCabdiE7
118
- K9kJpt7Es1DwLxCsP+jJqycv/S/8I9SrmPgmuy0pK1ilNZd8MPt233zz7dud
119
- H+99OXBR+pziILgYgglkqaUvIYFs0pWEBSyzIfsHLKeA3Fl0Y6wTX4d2K7bH
120
- uEvP6i90zhuf6P21fxpp0C9nOMOvGuMcUQ1811ZuqOjSVWuzszm8eb52cFR+
121
- K/k7yd9L/kHyV6OO8gJBezwT7/UVaj/xY9QRjXHhMDoM5rapZ39o/ntRZ2+0
122
- sY3xv0C5xkhT1rXo7gHmBr5VWg7f+gbZqU23KTotSqaT5L+Ba2waDmjR1AsQ
123
- qZnf6BVRvv29/5rsUtkJhXpWqiohG6LdCOu7ba+pRFud5veuMBisiKl7rmh4
124
- ckfn8jnkv2KxC4tNYpujfhk2NjKaZyNVo0PadoLGni4sMshDM3XlcD3D2DzL
125
- gW95oaJcmj3Rv6r174gnyguk1p9HvqtHpdmE/pTHDIUBb50VMHFfNlwS5Fig
126
- /ihKrcQH+wPoExCflMZJjWRSxZFf3Cd+zfPd0K64T+1HgS8kZshroLrnO0EL
127
- 00VNKbc90cKqpi9UPN/phBHXrgQ37a2EwpIelI6JVSFD4kQSGxQVs14WgCMK
128
- ZXcQ7WHtYxPWw5JupyuJqLLgeLHPEt5jz4r5CxXWcbY=
129
-
130
- eJzt0z1oU2EcRvE3rR+tWtGtOBQ3hS4tODgWKnQRlM5dHAoZCuIHaLcsBaGT
131
- hIBksQmlQ1wCTgF3Q+Z0cHN1lZBQQhL/N60fUEEKh5bK+cHleXlJw+2hHY5S
132
- yqXjsruvW++/HzzK3/jwdirN3/n4ZSHu2vspXY+9Gc/ro88txN107P3YK7EP
133
- Yq/GPo6diV2LvRabj52KfRl7IbYQeyn2Xezl2N3Yi7H12MnYT7ETsc3Yley+
134
- ndIoZN9xN55n8eyM32F2/M6F9vHfYyTa3lm/wH/Ipjyb8mzKsynPpjyb8mzK
135
- synPpjyb8mzKsynPpjyb8mzKsynPpjyb8mzKsynPpjyb8mzKsynPpjyb8mzK
136
- synPpjyb8mzKsynPpjyb8mzKsynPpjyb8mzKsynPpjyb8mzKsynPpjyb8mzK
137
- synPpjyb8mzKsynPpjyb8mzKsynPpjyb8mzKsynPpjyb8mzKsynPpjyb8mzK
138
- synPpjyb8mzKsynPpjyb8mzKsynPpjyb8mzKsynPpjyb8mzKsynPpjyb8mzK
139
- synPpjyb8mzKsynPpjyb8mzKsynPpjyb8mzKsynPpjyb8mzKsynPpjyb8mzK
140
- synPpjyb8mzKsynPpjyb8mzKsynPpjyb8mzKsynPpjyb8mzKsynPpjyb8mzK
141
- synPpjyb8mzKsynPpjyb8mzKsynPpjyb8mzKsynPpjyb8mzKsynPprx/NO33
142
- +7VaLQ6VSqVUKpXL5cFg0Gq1Go1GtVptNpvFYrFer8dNp9M5lRc+B078dzoc
143
- Dnu93uGh2+3G+deNDvm/z7Mpz6Y8m/JsyrMpz6Y8m/JsyrMpz6a8vYmU0rdB
144
- SnOx2XkxnqX02/j8ZnXzb+fCrcr09r3Puexnc0efz84z8SznnzzfWH9x++HT
145
- V+s/7//8zEnOPwDhN6kw
146
- end
147
- expect = data.split(/\n\s*\n/).map { |chunk| Zlib::Inflate.inflate Base64.decode64(chunk) }
110
+ sha1sums = %w[
111
+ d3d1cde9eb43ed4b77d197af879f5ca8b8837577
112
+ 65b75cbdd1f94ade632baeeb0848dec2a342c844
113
+ cfc230ec7515892cfdb85e4a173e0ce364094970
114
+ ffd859d94647a11b693f06f092d1a2bccc59d50d
115
+ ]
148
116
 
149
117
  # test the ole storage type
150
118
  type = 'Microsoft Word 6.0-Dokument'
151
119
  assert_equal type, (@ole.root/"\001CompObj").read[32..-1][/([^\x00]+)/m, 1]
152
120
  # i was actually not loading data correctly before, so carefully check everything here
153
- assert_equal expect, @ole.root.children.map { |child| child.read }
121
+ assert_equal sha1sums, @ole.root.children.map { |child| Digest::SHA1.hexdigest child.read }
154
122
  end
155
123
 
156
124
  def test_dirent
@@ -169,9 +137,7 @@ class TestStorageRead < Test::Unit::TestCase
169
137
 
170
138
  dirent.open('r') { |f| assert_equal 2, f.first_block }
171
139
  dirent.open('w') { |f| }
172
- assert_raises Errno::EINVAL do
173
- dirent.open('a') { |f| }
174
- end
140
+ dirent.open('a') { |f| }
175
141
  end
176
142
 
177
143
  def test_delete
@@ -206,8 +172,8 @@ class TestStorageWrite < Test::Unit::TestCase
206
172
  assert_equal '9974e354def8471225f548f82b8d81c701221af7', sha1(io.string)
207
173
  Ole::Storage.open(io, :update_timestamps => false) { }
208
174
  # hash changed. used to be efa8cfaf833b30b1d1d9381771ddaafdfc95305c
209
- # thats because i know truncate the io, and am probably removing some trailing allocated
210
- # available blocks.
175
+ # thats because i now truncate the io, and am probably removing some trailing
176
+ # allocated available blocks.
211
177
  assert_equal 'a39e3c4041b8a893c753d50793af8d21ca8f0a86', sha1(io.string)
212
178
  # add a repack test here
213
179
  Ole::Storage.open io, :update_timestamps => false, &:repack
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.8.2
4
+ version: 1.2.9
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-01-01 00:00:00 +11:00
12
+ date: 2009-07-14 00:00:00 +10:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -28,29 +28,29 @@ files:
28
28
  - ChangeLog
29
29
  - data/propids.yaml
30
30
  - bin/oletool
31
+ - lib/ole/types.rb
32
+ - lib/ole/storage.rb
31
33
  - lib/ole/support.rb
32
34
  - lib/ole/base.rb
33
- - lib/ole/storage.rb
34
- - lib/ole/file_system.rb
35
35
  - lib/ole/ranges_io.rb
36
+ - lib/ole/file_system.rb
37
+ - lib/ole/types/property_set.rb
38
+ - lib/ole/types/base.rb
39
+ - lib/ole/storage/meta_data.rb
36
40
  - lib/ole/storage/base.rb
37
41
  - lib/ole/storage/file_system.rb
38
- - lib/ole/storage/meta_data.rb
39
- - lib/ole/types.rb
40
- - lib/ole/types/base.rb
41
- - lib/ole/types/property_set.rb
42
- - test/test_types.rb
42
+ - test/test_ranges_io.rb
43
+ - test/test_storage.rb
43
44
  - test/test_filesystem.rb
44
45
  - test/test_support.rb
45
- - test/test_storage.rb
46
+ - test/test_mbat.rb
47
+ - test/test_types.rb
46
48
  - test/test_meta_data.rb
47
- - test/test_ranges_io.rb
48
49
  - test/test_property_set.rb
49
- - test/test_mbat.rb
50
- - test/test.doc
51
50
  - test/test_word_6.doc
52
51
  - test/test_word_97.doc
53
52
  - test/test_word_95.doc
53
+ - test/test.doc
54
54
  - test/oleWithDirs.ole
55
55
  - test/test_SummaryInformation
56
56
  has_rdoc: true
@@ -80,16 +80,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
80
  requirements: []
81
81
 
82
82
  rubyforge_project: ruby-ole
83
- rubygems_version: 1.2.0
83
+ rubygems_version: 1.3.1
84
84
  signing_key:
85
85
  specification_version: 2
86
86
  summary: Ruby OLE library.
87
87
  test_files:
88
- - test/test_types.rb
88
+ - test/test_ranges_io.rb
89
+ - test/test_storage.rb
89
90
  - test/test_filesystem.rb
90
91
  - test/test_support.rb
91
- - test/test_storage.rb
92
+ - test/test_mbat.rb
93
+ - test/test_types.rb
92
94
  - test/test_meta_data.rb
93
- - test/test_ranges_io.rb
94
95
  - test/test_property_set.rb
95
- - test/test_mbat.rb