sequence 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +58 -0
- data/GPL +340 -0
- data/Manifest.txt +36 -0
- data/README.txt +320 -0
- data/Rakefile +32 -0
- data/lib/assert.rb +16 -0
- data/lib/sequence/arraylike.rb +57 -0
- data/lib/sequence/buffered.rb +188 -0
- data/lib/sequence/circular.rb +272 -0
- data/lib/sequence/enum.rb +260 -0
- data/lib/sequence/file.rb +172 -0
- data/lib/sequence/functional.rb +152 -0
- data/lib/sequence/generator.rb +290 -0
- data/lib/sequence/indexed.rb +234 -0
- data/lib/sequence/io.rb +102 -0
- data/lib/sequence/list.rb +292 -0
- data/lib/sequence/ofhash.rb +38 -0
- data/lib/sequence/ofobjectivars.rb +29 -0
- data/lib/sequence/ofobjectmethods.rb +87 -0
- data/lib/sequence/position.rb +100 -0
- data/lib/sequence/reversed.rb +180 -0
- data/lib/sequence/shifting.rb +190 -0
- data/lib/sequence/singleitem.rb +50 -0
- data/lib/sequence/stringlike.rb +482 -0
- data/lib/sequence/subseq.rb +90 -0
- data/lib/sequence/usedata.rb +35 -0
- data/lib/sequence/version.rb +5 -0
- data/lib/sequence.rb +721 -0
- data/lib/weakrefset.rb +254 -0
- data/test/test.rb +609 -0
- data/test/test_all.rb +6 -0
- data/test/test_changes.rb +44 -0
- data/test/test_circulars.rb +89 -0
- data/test/test_rexscan.rb +899 -0
- data/test/test_seqrex.rb +204 -0
- data/test/test_sequences.rb +106 -0
- metadata +90 -0
@@ -0,0 +1,272 @@
|
|
1
|
+
# $Id$
|
2
|
+
# Copyright (C) 2006 Caleb Clausen
|
3
|
+
# Distributed under the terms of Ruby's license.
|
4
|
+
|
5
|
+
require 'sequence'
|
6
|
+
|
7
|
+
class Sequence
|
8
|
+
# This Sequence class is used to represent a circular buffer. You can think of
|
9
|
+
# this as having no beginning/end or the current location is always both at the
|
10
|
+
# beginning/end. Because of the circular nature, the methods
|
11
|
+
# #scan_until, #modify,
|
12
|
+
# #each, #collect!, and #map!
|
13
|
+
# are not defined.
|
14
|
+
class Circular < Sequence
|
15
|
+
# Create a circular sequence from a normal finite one.
|
16
|
+
def initialize(sequence,pos=sequence.pos)
|
17
|
+
@seq = sequence
|
18
|
+
@pos=pos
|
19
|
+
@size=sequence.size
|
20
|
+
extend sequence.like
|
21
|
+
|
22
|
+
@seq.on_change_notify self
|
23
|
+
end
|
24
|
+
|
25
|
+
#the default _parse_slice_args isn't forgiving enough about large
|
26
|
+
#(positive or negative) indexes
|
27
|
+
def _normalize_pos pos, size=nil
|
28
|
+
pos
|
29
|
+
end
|
30
|
+
|
31
|
+
def change_notification(cu,first,oldsize,newsize)
|
32
|
+
assert(cu.equal?( @seq ))
|
33
|
+
@pos =_adjust_pos_on_change(@pos, first,oldsize,newsize)
|
34
|
+
@size+=newsize-oldsize
|
35
|
+
assert @size==@seq.size
|
36
|
+
notify_change(self,first,oldsize,newsize)
|
37
|
+
end
|
38
|
+
|
39
|
+
=begin
|
40
|
+
def _adjust_delete(len=1,reverse=false)
|
41
|
+
pos = _pos(false)
|
42
|
+
ret = nil
|
43
|
+
@positions.each { |p| ret = p.__send__(:_deletion,pos,len,reverse,ret) }
|
44
|
+
end
|
45
|
+
def _adjust_insert(len=1)
|
46
|
+
pos = _pos(false)
|
47
|
+
ret = nil
|
48
|
+
@positions.each { |p| ret = p.__send__(:_insertion,pos,len,ret) }
|
49
|
+
end
|
50
|
+
=end
|
51
|
+
attr_reader :pos,:size
|
52
|
+
def _pos=(pos)
|
53
|
+
#pos=0 if pos>=size
|
54
|
+
@pos=pos
|
55
|
+
end
|
56
|
+
public
|
57
|
+
|
58
|
+
# :stopdoc:
|
59
|
+
def new_data
|
60
|
+
@seq.new_data
|
61
|
+
end
|
62
|
+
=begin **
|
63
|
+
def read1next
|
64
|
+
v0 = @seq.read1next
|
65
|
+
v0.nil? && @seq.move!(true) && (v0 = @seq.read1next)
|
66
|
+
v0
|
67
|
+
end
|
68
|
+
def read1prev
|
69
|
+
v0 = @seq.read1prev
|
70
|
+
v0.nil? && @seq.move!(false) && (v0 = @seq.read1prev)
|
71
|
+
v0
|
72
|
+
end
|
73
|
+
def read1after
|
74
|
+
v0 = @seq.read1after
|
75
|
+
v0.nil? && begin! && (v0 = @seq.read1after)
|
76
|
+
v0
|
77
|
+
end
|
78
|
+
def read1before
|
79
|
+
v0 = @seq.read1before
|
80
|
+
v0.nil? && @seq.move!(false) && (v0 = @seq.read1before)
|
81
|
+
v0
|
82
|
+
end
|
83
|
+
def skip1next
|
84
|
+
@seq.skip1next || @seq.skip!(true) && @seq.skip1next
|
85
|
+
end
|
86
|
+
def skip1prev
|
87
|
+
@seq.skip1prev || @seq.skip!(false) && @seq.skip1prev
|
88
|
+
end
|
89
|
+
def skip1after
|
90
|
+
@seq.skip1after || @seq.skip!(true) && @seq.skip1after
|
91
|
+
end
|
92
|
+
def skip1before
|
93
|
+
@seq.skip1before || @seq.skip!(false) && @seq.skip1before
|
94
|
+
end
|
95
|
+
def delete1after
|
96
|
+
v0 = @seq.delete1after || @seq.skip!(true) && @seq.delete1after
|
97
|
+
v0.nil? || @positions && _adjust_delete
|
98
|
+
v0
|
99
|
+
end
|
100
|
+
def delete1before
|
101
|
+
v0 = @seq.delete1before || @seq.skip!(false) && @seq.delete1before
|
102
|
+
v0.nil? || @positions && _adjust_delete
|
103
|
+
v0
|
104
|
+
end
|
105
|
+
def delete1after?
|
106
|
+
v0 = @seq.delete1after?
|
107
|
+
v0.nil? && @seq.skip!(true) && (v0 = @seq.delete1after?)
|
108
|
+
v0.nil? || @positions && _adjust_delete
|
109
|
+
v0
|
110
|
+
end
|
111
|
+
def delete1before?
|
112
|
+
v0 = @seq.delete1before?
|
113
|
+
v0.nil? && @seq.skip!(false) && (v0 = @seq.delete1before?)
|
114
|
+
v0.nil? || @positions && _adjust_delete
|
115
|
+
v0
|
116
|
+
end
|
117
|
+
def write1next(v)
|
118
|
+
@seq.write1next(v) || @seq.skip!(true) && @seq.write1next(v)
|
119
|
+
end
|
120
|
+
def write1prev(v)
|
121
|
+
@seq.write1prev(v) || @seq.skip!(false) && @seq.write1prev(v)
|
122
|
+
end
|
123
|
+
def write1after(v)
|
124
|
+
@seq.write1after(v) || @seq.skip!(true) && @seq.write1after(v)
|
125
|
+
end
|
126
|
+
def write1before(v)
|
127
|
+
@seq.write1before(v) || @seq.skip!(false) && @seq.write1before(v)
|
128
|
+
end
|
129
|
+
def write1next?(v)
|
130
|
+
v0 = @seq.write1next?(v)
|
131
|
+
v0.nil? && @seq.skip!(true) && (v0 = @seq.write1next?(v))
|
132
|
+
v0
|
133
|
+
end
|
134
|
+
def write1prev?(v)
|
135
|
+
v0 = @seq.write1prev?(v)
|
136
|
+
v0.nil? && @seq.skip!(false) && (v0 = @seq.write1prev?(v))
|
137
|
+
v0
|
138
|
+
end
|
139
|
+
def write1after?(v)
|
140
|
+
v0 = @seq.write1after?(v)
|
141
|
+
v0.nil? && @seq.skip!(true) && (v0 = @seq.write1after?(v))
|
142
|
+
v0
|
143
|
+
end
|
144
|
+
def write1before?(v)
|
145
|
+
v0 = @seq.write1before?(v)
|
146
|
+
v0.nil? && @seq.skip!(false) && (v0 = @seq.write1before?(v))
|
147
|
+
v0
|
148
|
+
end
|
149
|
+
def insert1before(v)
|
150
|
+
@positions && _adjust_insert
|
151
|
+
@seq.insert1before(v)
|
152
|
+
end
|
153
|
+
def insert1after(v)
|
154
|
+
@positions && _adjust_insert
|
155
|
+
@seq.insert1after(v)
|
156
|
+
end
|
157
|
+
def scan1next(v)
|
158
|
+
v0 = read1next
|
159
|
+
(v0.nil? || v==v0) ? v0 : (skip1prev;nil)
|
160
|
+
end
|
161
|
+
def scan1prev(v)
|
162
|
+
v0 = read1prev
|
163
|
+
(v0.nil? || v==v0) ? v0 : (skip1next;nil)
|
164
|
+
end
|
165
|
+
def modify1next(r)
|
166
|
+
v0 = read1after
|
167
|
+
(v0.nil? || (v = r[v0]).nil?) ? nil : (write1next!(v);v0)
|
168
|
+
end
|
169
|
+
def modify1prev(r)
|
170
|
+
v0 = read1before
|
171
|
+
(v0.nil? || (v = r[v0]).nil?) ? nil : (write1prev!(v);v0)
|
172
|
+
end
|
173
|
+
=end
|
174
|
+
# :startdoc:
|
175
|
+
# read over one pass of the data to return where you started
|
176
|
+
def read!(reverse=false)
|
177
|
+
unless reverse
|
178
|
+
read(size)
|
179
|
+
else
|
180
|
+
readback(-size)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
# skip over one pass of the data to return where you started
|
184
|
+
def move!(reverse=false)
|
185
|
+
size
|
186
|
+
end
|
187
|
+
alias end! begin!
|
188
|
+
alias end begin
|
189
|
+
# Compare to +other+.
|
190
|
+
def <=>(other)
|
191
|
+
position?(other) and pos<=>other.pos
|
192
|
+
end
|
193
|
+
|
194
|
+
=begin ***
|
195
|
+
# insert an element before the position and return self
|
196
|
+
def << (value)
|
197
|
+
insert1before(value)
|
198
|
+
self
|
199
|
+
end
|
200
|
+
# insert an element after the position and return self
|
201
|
+
def >> (value)
|
202
|
+
insert1after(value)
|
203
|
+
self
|
204
|
+
end
|
205
|
+
=end
|
206
|
+
# :stopdoc:
|
207
|
+
|
208
|
+
def data
|
209
|
+
@seq
|
210
|
+
end
|
211
|
+
|
212
|
+
def each
|
213
|
+
po=position
|
214
|
+
yield read1 until self==position
|
215
|
+
po.close
|
216
|
+
end
|
217
|
+
|
218
|
+
def eof?; false end
|
219
|
+
|
220
|
+
def readahead(len)
|
221
|
+
result=@seq[@pos%size,len]
|
222
|
+
len-=result.size
|
223
|
+
len.zero? and return result
|
224
|
+
loops=len/size
|
225
|
+
|
226
|
+
result+=@seq[0...size]*loops if loops.nonzero?
|
227
|
+
|
228
|
+
len%=size
|
229
|
+
|
230
|
+
len.zero? and return result
|
231
|
+
|
232
|
+
result+=@seq[0,len]
|
233
|
+
end
|
234
|
+
|
235
|
+
def read len
|
236
|
+
result=readahead len
|
237
|
+
move len
|
238
|
+
result
|
239
|
+
end
|
240
|
+
|
241
|
+
def modify(*args)
|
242
|
+
data=args.last
|
243
|
+
first,len,only1=_parse_slice_args(*args[0...-1])
|
244
|
+
first %= size
|
245
|
+
|
246
|
+
len>size and raise( ArgumentError, "dst len too long")
|
247
|
+
first+len>size and raise( ArgumentError, "wraparound modify in circular")
|
248
|
+
|
249
|
+
@seq.modify(*args)
|
250
|
+
end
|
251
|
+
|
252
|
+
#when reversed and circular, always put the Circular outermost.
|
253
|
+
def reverse
|
254
|
+
Circular.new @seq.reverse
|
255
|
+
end
|
256
|
+
|
257
|
+
def nearbegin(len,at=pos)
|
258
|
+
false
|
259
|
+
end
|
260
|
+
|
261
|
+
def nearend(len,at=pos)
|
262
|
+
false
|
263
|
+
end
|
264
|
+
|
265
|
+
def closed?
|
266
|
+
super or @seq.closed?
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
|
272
|
+
|
@@ -0,0 +1,260 @@
|
|
1
|
+
# Copyright (C) 2006 Caleb Clausen
|
2
|
+
# Distributed under the terms of Ruby's license.
|
3
|
+
require 'thread'
|
4
|
+
require 'sequence/arraylike'
|
5
|
+
|
6
|
+
=begin discussion
|
7
|
+
|
8
|
+
Sequence::Enum is pretty much the same thing as Generator from the
|
9
|
+
ruby standard library, but faster.
|
10
|
+
(Newer versions of the standard library include an improved
|
11
|
+
Generator, which should be about as fast.)
|
12
|
+
|
13
|
+
The key insight is to realize that Continuations were used in the
|
14
|
+
original because an independant call stack is needed (to run #each
|
15
|
+
in), separate from the call stack of the user of Generator. However,
|
16
|
+
Continuations are only one way to get another call stack; you could
|
17
|
+
also use a Thread (plus a Queue, to communicate back the results),
|
18
|
+
which doesn't have the same performance problems in ruby.
|
19
|
+
|
20
|
+
In order to implement #readahead1, I had to invent Queue#peek,
|
21
|
+
which tells you what the next element is without taking it from the
|
22
|
+
Queue.
|
23
|
+
|
24
|
+
Actually, I'm using a SizedQueue, not a plain Queue. Otherwise, the
|
25
|
+
memory used by the queue could grow without bounds.
|
26
|
+
|
27
|
+
#begin! was also a small challenge, until I realized that you could
|
28
|
+
just restart the Thread. (Hopefully, the enum or block will return
|
29
|
+
the same results the second time through.)
|
30
|
+
|
31
|
+
It's not allowed in Generator, but Sequence::Enum permits you to
|
32
|
+
pass in both an enum and a block. The results of the
|
33
|
+
block are passed up once the enum is exhausted.
|
34
|
+
|
35
|
+
#<< allows you to
|
36
|
+
add more items to the Sequence::Enum once it has been created. At first,
|
37
|
+
this was also aliased to #yield, because I didn't quite realize that
|
38
|
+
#yield should only be used inside the constructor's block. Once I
|
39
|
+
straightened out #yield, I decided that adding items after
|
40
|
+
construction was a cool feature, so I left the capability in.
|
41
|
+
|
42
|
+
It's clear that this version is faster than the original callcc-
|
43
|
+
based Generator, but I'm not sure how much. I was unable to run
|
44
|
+
the relevant benchmark to completion on my machine. Even after reducing
|
45
|
+
the number of loops in the test by 10x, the callcc version was
|
46
|
+
still taking more than 2 hours. (I don't know how much more because
|
47
|
+
at that point I grew impatient and gave it the ^C.) My own version
|
48
|
+
finishes in about a second or less.
|
49
|
+
|
50
|
+
I also found that bumping the queue size up to 400 from the original
|
51
|
+
32 made about a 4x difference in running time. This implies to me
|
52
|
+
that context switches in ruby are rather more expensive than I
|
53
|
+
expected.
|
54
|
+
=end
|
55
|
+
|
56
|
+
if true
|
57
|
+
|
58
|
+
require 'sequence/generator'
|
59
|
+
|
60
|
+
class Sequence
|
61
|
+
class Enum < Sequence
|
62
|
+
include ArrayLike
|
63
|
+
|
64
|
+
def initialize(enum=nil,&block)
|
65
|
+
@gen=Generator.new(enum,&block)
|
66
|
+
end
|
67
|
+
|
68
|
+
def eof?; @gen.end? end
|
69
|
+
def pos; @gen.index end
|
70
|
+
def begin!; @gen.rewind; 0 end
|
71
|
+
|
72
|
+
def each &block
|
73
|
+
@gen.dup.each &block
|
74
|
+
end
|
75
|
+
|
76
|
+
def read len
|
77
|
+
return [] if len.zero? or eof?
|
78
|
+
result=[]
|
79
|
+
begin
|
80
|
+
len.times{ result<<@gen.next }
|
81
|
+
rescue EOFError:
|
82
|
+
end
|
83
|
+
|
84
|
+
return result
|
85
|
+
end
|
86
|
+
|
87
|
+
def readahead1
|
88
|
+
@gen.current
|
89
|
+
rescue EOFError:
|
90
|
+
return nil
|
91
|
+
end
|
92
|
+
|
93
|
+
def read1
|
94
|
+
@gen.next
|
95
|
+
rescue EOFError:
|
96
|
+
return nil
|
97
|
+
end
|
98
|
+
|
99
|
+
alias size pos
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
%w[pos= _pos= scan scan_until write [] []=
|
105
|
+
holding holding? holding! position
|
106
|
+
].each{|mname| undef_method mname}
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
else
|
112
|
+
|
113
|
+
|
114
|
+
class Queue
|
115
|
+
# Retrieves next data from the queue, without pulling it off the queue.
|
116
|
+
# If the queue is empty, the calling thread is
|
117
|
+
# suspended until data is pushed onto the queue.
|
118
|
+
# If +non_block+ is true, the
|
119
|
+
# thread isn't suspended, and an exception is raised.
|
120
|
+
def peek(non_block=false)
|
121
|
+
raise ThreadError, "queue empty" if non_block and empty?
|
122
|
+
Thread.pass while (empty?)
|
123
|
+
Thread.critical=true
|
124
|
+
result=@que.first
|
125
|
+
Thread.critical=false
|
126
|
+
result
|
127
|
+
end
|
128
|
+
|
129
|
+
def read(len)
|
130
|
+
Thread.critical=true
|
131
|
+
result=@que.slice![0...len]
|
132
|
+
Thread.critical=false
|
133
|
+
result
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
class Sequence
|
139
|
+
class Enum < Sequence
|
140
|
+
include ArrayLike
|
141
|
+
|
142
|
+
def initialize(enum=[],qsize=400,&block)
|
143
|
+
@extras=[]
|
144
|
+
@extrasmutex=Mutex.new
|
145
|
+
init(enum,qsize,&block)
|
146
|
+
end
|
147
|
+
|
148
|
+
def init(enum,qsize,&block)
|
149
|
+
@block=block
|
150
|
+
@pos=0
|
151
|
+
@enum=enum
|
152
|
+
@q=q=SizedQueue.new(qsize)
|
153
|
+
@thread=Thread.new{
|
154
|
+
enum.each{|item| q<<item }
|
155
|
+
block[self] if block
|
156
|
+
i=0
|
157
|
+
while i<@extras.size
|
158
|
+
q.push @extrasmutex.synchronize { @extras[i] }
|
159
|
+
i+=1
|
160
|
+
end
|
161
|
+
}
|
162
|
+
end
|
163
|
+
|
164
|
+
#should only be called from inside constructor's block
|
165
|
+
def yield(item)
|
166
|
+
@q.push item
|
167
|
+
end
|
168
|
+
|
169
|
+
def <<(item)
|
170
|
+
@extrasmutex.synchronize { @extras<<item }
|
171
|
+
end
|
172
|
+
|
173
|
+
def begin!
|
174
|
+
@thread.kill
|
175
|
+
init(@enum,@q.max,&@block)
|
176
|
+
0
|
177
|
+
end
|
178
|
+
|
179
|
+
def readahead1
|
180
|
+
current
|
181
|
+
rescue EOFError:
|
182
|
+
return nil
|
183
|
+
end
|
184
|
+
|
185
|
+
def read1
|
186
|
+
self.next
|
187
|
+
rescue EOFError:
|
188
|
+
return nil
|
189
|
+
end
|
190
|
+
|
191
|
+
def current
|
192
|
+
raise EOFError if eof?
|
193
|
+
@q.peek
|
194
|
+
rescue ThreadError:
|
195
|
+
raise EOFError
|
196
|
+
end
|
197
|
+
|
198
|
+
def next
|
199
|
+
result=@q.pop
|
200
|
+
raise EOFError if !result && eof?
|
201
|
+
@pos+=1
|
202
|
+
result
|
203
|
+
rescue ThreadError:
|
204
|
+
raise EOFError
|
205
|
+
end
|
206
|
+
|
207
|
+
def read(len)
|
208
|
+
len.zero? and return []
|
209
|
+
raise ThreadError if @thread.status=="sleep" and @q.empty?
|
210
|
+
result=[]
|
211
|
+
begin #loop
|
212
|
+
len.times{ result<<@q.pop(true) }
|
213
|
+
#feh, should fetch more at a time... Queue needs a #read
|
214
|
+
rescue ThreadError:
|
215
|
+
len-=result.length
|
216
|
+
end until @q.empty? and result.length.zero?
|
217
|
+
|
218
|
+
ensure
|
219
|
+
@pos+=result.length
|
220
|
+
return result
|
221
|
+
end
|
222
|
+
|
223
|
+
def size
|
224
|
+
@pos+@q.size
|
225
|
+
end
|
226
|
+
|
227
|
+
def eof?
|
228
|
+
Thread.pass while @q.empty? and @thread.alive?
|
229
|
+
@q.empty? and !@thread.alive?
|
230
|
+
end
|
231
|
+
|
232
|
+
def each(&block)
|
233
|
+
copy=dup
|
234
|
+
copy.begin!
|
235
|
+
until(copy.eof?)
|
236
|
+
block.call copy.read1
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
attr :pos
|
241
|
+
|
242
|
+
#methods for Generator compatibility:
|
243
|
+
def rewind; begin!; self end
|
244
|
+
alias end? eof?
|
245
|
+
alias index pos
|
246
|
+
def next?; !end? end
|
247
|
+
|
248
|
+
%w[pos= _pos= scan scan_until write [] []=
|
249
|
+
holding holding? holding! position
|
250
|
+
].each{|mname| undef_method mname}
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
end
|
255
|
+
|
256
|
+
module Enumerable
|
257
|
+
def to_sequence
|
258
|
+
Sequence::Enum.new(self)
|
259
|
+
end
|
260
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# Copyright (C) 2006 Caleb Clausen
|
2
|
+
# Distributed under the terms of Ruby's license.
|
3
|
+
# $Id$
|
4
|
+
|
5
|
+
require 'sequence'
|
6
|
+
require 'sequence/stringlike'
|
7
|
+
|
8
|
+
class Sequence
|
9
|
+
# This class treats an IO (or StringIO) as an Sequence. An IO is already
|
10
|
+
# like an Sequence, but with a differing interface.
|
11
|
+
# Actually, we assume that the IO is capable of seeking, so it most likely
|
12
|
+
# must be a File.
|
13
|
+
# delete/insert at arbitrary location is not supported.
|
14
|
+
class File < Sequence
|
15
|
+
# include UseNext
|
16
|
+
include StringLike
|
17
|
+
def initialize(file,mode="r")
|
18
|
+
|
19
|
+
case file
|
20
|
+
when Integer: file=IO.new(file,mode)
|
21
|
+
when String: file=File.new(file,mode)
|
22
|
+
else #do nothing, file is of a right type (we hope) already
|
23
|
+
end
|
24
|
+
|
25
|
+
@io = file
|
26
|
+
end
|
27
|
+
# :stopdoc:
|
28
|
+
def new_data
|
29
|
+
''
|
30
|
+
end
|
31
|
+
def data; @io end
|
32
|
+
def read1
|
33
|
+
@io.getc
|
34
|
+
end
|
35
|
+
=begin ***
|
36
|
+
def write1next(v)
|
37
|
+
@io.eof? ? nil : (@io.putc(v);true)
|
38
|
+
end
|
39
|
+
def write1next!(v)
|
40
|
+
@io.putc(v)
|
41
|
+
true
|
42
|
+
end
|
43
|
+
def skip1prev
|
44
|
+
@io.pos.nonzero? && (@io.seek(-1,::IO::SEEK_CUR);true)
|
45
|
+
end
|
46
|
+
=end
|
47
|
+
def readahead1
|
48
|
+
v0 = @io.getc
|
49
|
+
v0 && @io.ungetc(v0)
|
50
|
+
v0
|
51
|
+
end
|
52
|
+
=begin ***
|
53
|
+
def write1after!(v)
|
54
|
+
@io.putc(v)
|
55
|
+
@io.seek(-1,::IO::SEEK_CUR)
|
56
|
+
true
|
57
|
+
end
|
58
|
+
def skip1next
|
59
|
+
@io.getc ? true : nil
|
60
|
+
end
|
61
|
+
def skip1after
|
62
|
+
@io.eof? ? nil : true
|
63
|
+
end
|
64
|
+
def skip1before
|
65
|
+
@io.pos.zero? ? nil : true
|
66
|
+
end
|
67
|
+
def scan1next(v)
|
68
|
+
v0 = @io.getc
|
69
|
+
(v0.nil? || v==v0) ? v0 : (@io.ungetc(v0);nil)
|
70
|
+
end
|
71
|
+
=end
|
72
|
+
def size
|
73
|
+
@io.stat.size
|
74
|
+
end
|
75
|
+
def read(len)
|
76
|
+
@io.read(len) or ""
|
77
|
+
end
|
78
|
+
def readahead(len)
|
79
|
+
buffer1 = read(len)
|
80
|
+
@io.seek(-buffer1.size,::IO::SEEK_CUR)
|
81
|
+
buffer1
|
82
|
+
end
|
83
|
+
def readback(len)
|
84
|
+
result=readbehind(len)
|
85
|
+
@io.seek(-result.size,::IO::SEEK_CUR)
|
86
|
+
result
|
87
|
+
end
|
88
|
+
def readbehind(len)
|
89
|
+
p = @io.pos
|
90
|
+
len>p and len=p
|
91
|
+
@io.seek(-len,::IO::SEEK_CUR)
|
92
|
+
@io.read(len)
|
93
|
+
end
|
94
|
+
def read!(reverse=false)
|
95
|
+
if reverse
|
96
|
+
len = @io.pos.nonzero? or return ""
|
97
|
+
@io.seek(0, ::IO::SEEK_SET)
|
98
|
+
#@io.pos = 0 # BUGGY in v1.8.2
|
99
|
+
buffer1 = @io.read(len) || ""
|
100
|
+
@io.seek(0, ::IO::SEEK_SET)
|
101
|
+
else
|
102
|
+
buffer1 = @io.read(nil)
|
103
|
+
end
|
104
|
+
buffer1
|
105
|
+
end
|
106
|
+
|
107
|
+
def eof?; @io.eof? end
|
108
|
+
|
109
|
+
def _pos=(p)
|
110
|
+
@io.seek(p,::IO::SEEK_SET)
|
111
|
+
p
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
def modify(*args)
|
116
|
+
data=args.pop
|
117
|
+
first,len,only1=_parse_slice_args(*args)
|
118
|
+
if first+len==size #working at end of data?
|
119
|
+
holding{
|
120
|
+
@io.truncate first if len.nonzero?
|
121
|
+
goto first
|
122
|
+
@io.write data
|
123
|
+
}
|
124
|
+
elsif len==data.size #inserted data is same size?
|
125
|
+
holding{
|
126
|
+
goto first
|
127
|
+
@io.write data
|
128
|
+
}
|
129
|
+
else
|
130
|
+
raise ArgumentError,"replace data must be same size or modification must be at very end"
|
131
|
+
end
|
132
|
+
notify_change(self,first,len,data.size)
|
133
|
+
data
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
|
138
|
+
def append(str)
|
139
|
+
Integer===str and str=str.chr
|
140
|
+
first=nil
|
141
|
+
holding{
|
142
|
+
end!
|
143
|
+
first=pos
|
144
|
+
@io.write str
|
145
|
+
}
|
146
|
+
notify_change(self,first,0,str.size)
|
147
|
+
self
|
148
|
+
end
|
149
|
+
|
150
|
+
def close
|
151
|
+
@io.close
|
152
|
+
super
|
153
|
+
end
|
154
|
+
|
155
|
+
def pos; @io.tell end
|
156
|
+
# :startdoc:
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
class File
|
161
|
+
# convert a File to a seq
|
162
|
+
def to_sequence
|
163
|
+
Sequence::File.new(self)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
class StringIO
|
168
|
+
# convert an StringIO to a seq
|
169
|
+
def to_sequence
|
170
|
+
Sequence::File.new(self)
|
171
|
+
end
|
172
|
+
end
|