sequence 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,482 @@
|
|
1
|
+
# Copyright (C) 2006 Caleb Clausen
|
2
|
+
# Distributed under the terms of Ruby's license.
|
3
|
+
require 'sequence/subseq'
|
4
|
+
|
5
|
+
class Sequence
|
6
|
+
module StringLike
|
7
|
+
def data_class; String end
|
8
|
+
|
9
|
+
def like; StringLike end
|
10
|
+
|
11
|
+
#-------------------------------------
|
12
|
+
FFS_4BITTABLE=[nil,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0]
|
13
|
+
def ffs
|
14
|
+
holding{
|
15
|
+
begin!
|
16
|
+
zeros=read_til_charset(/[^\0]/)
|
17
|
+
byte=read1
|
18
|
+
lo=byte&0xF
|
19
|
+
rem=FFS_4BITTABLE[lo]||FFS_4BITTABLE[byte>>4]+4
|
20
|
+
return zeros.size<<3+rem
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
#-------------------------------------
|
25
|
+
def fns(bitnum)
|
26
|
+
holding{
|
27
|
+
goto bitnum>>3
|
28
|
+
bitnum&=0x7
|
29
|
+
byte=read1
|
30
|
+
byte&=~((1<<(bitnum+1))-1)
|
31
|
+
if byte.nonzero?
|
32
|
+
zeros_size=0
|
33
|
+
else
|
34
|
+
zeros_size=read_til_charset(/[^\0]/).size
|
35
|
+
byte=read1
|
36
|
+
end
|
37
|
+
lo=byte&0xF
|
38
|
+
rem=FFS_4BITTABLE[lo]||FFS_4BITTABLE[byte>>4]+4
|
39
|
+
return zeros_size<<3+rem
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
#-------------------------------------
|
44
|
+
#read until a character in a user-supplied set is found.
|
45
|
+
#charrex must be a regexp that contains _only_ a single character class
|
46
|
+
def read_til_charset(charrex,blocksize=16)
|
47
|
+
blocks=[]
|
48
|
+
m=nil
|
49
|
+
until eof?
|
50
|
+
block=read blocksize
|
51
|
+
#if near eof, less than a full block may have been read
|
52
|
+
|
53
|
+
if m=charrex .match(block)
|
54
|
+
self.pos-=m.post_match.length+1
|
55
|
+
#'self.' shouldn't be needed... but is
|
56
|
+
|
57
|
+
blocks.push m.pre_match if m.pre_match.length>0
|
58
|
+
break
|
59
|
+
end
|
60
|
+
blocks<<block
|
61
|
+
end
|
62
|
+
return blocks.to_s
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
#-------------------------------------
|
67
|
+
#this version is fast and simple, but anchors do not work right,
|
68
|
+
#matches are NOT implicitly anchored to the current position, and
|
69
|
+
#the file position is not advanced. post_match (or pre_match if
|
70
|
+
#going backwards) is always nil.
|
71
|
+
def match_fast(rex,backwards=false,len=maxmatchlen(backwards))
|
72
|
+
str=send backwards ? :readbehind : :readahead, len
|
73
|
+
if result=rex.match(str)
|
74
|
+
if backwards
|
75
|
+
def result.pre_match; end
|
76
|
+
else
|
77
|
+
def result.post_match ; end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
return result
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
#-------------------------------------
|
86
|
+
#like match, but goes backwards
|
87
|
+
def matchback(rex,anchored=true, len=maxmatchlen(true))
|
88
|
+
nearbegin=nearbegin(len)
|
89
|
+
newrex,addedgroups=
|
90
|
+
if nearbegin && !anchored
|
91
|
+
[rex,[]]
|
92
|
+
else group_anchors(rex,:back,anchored)
|
93
|
+
end
|
94
|
+
#do the match against what input we have
|
95
|
+
|
96
|
+
matchdata=match_fast(newrex,true,len)
|
97
|
+
#fail if any ^ or \A matched at begin of buffer,
|
98
|
+
#but buffer isn't begin of file
|
99
|
+
return if !matchdata or #not actually a match
|
100
|
+
addedgroups.find{|i| matchdata.end(i)==0 } && !nearbegin
|
101
|
+
|
102
|
+
matchpos=pos-len
|
103
|
+
matchpos>=0 or matchpos=0
|
104
|
+
assert(matchpos>=0)
|
105
|
+
match1st=position matchpos+matchdata.begin(0)
|
106
|
+
result=fixup_match_result(matchdata,addedgroups,matchpos,:pre) do
|
107
|
+
result=SubSeq.new(self,0,match1st.pos)
|
108
|
+
result.pos=match1st.pos
|
109
|
+
result
|
110
|
+
end
|
111
|
+
#note: pre_match is a subseq.
|
112
|
+
|
113
|
+
#rex.last_match=
|
114
|
+
self.last_match=Thread.current[:last_match]=result
|
115
|
+
end
|
116
|
+
|
117
|
+
#-------------------------------------
|
118
|
+
#like match_fast, but anchors work correctly and post_match is
|
119
|
+
#set to something, if not exactly what you expected. (an Sequence, not String.)
|
120
|
+
#2nd parameter determines if match is anchored on the left side to the
|
121
|
+
#current position or not.
|
122
|
+
def match(rex,anchored=true, len=maxmatchlen(false))
|
123
|
+
|
124
|
+
newrex=nearend(len)? rex : group_anchors(rex,false,false).first
|
125
|
+
|
126
|
+
#do the match against what input we have
|
127
|
+
matchdata=match_fast(newrex,false,len) or return
|
128
|
+
|
129
|
+
anchored and matchdata.begin(0).nonzero? and return
|
130
|
+
posi=position;posi.move matchdata.end(0)
|
131
|
+
result=fixup_match_result(matchdata,[],pos,:post) { posi.subseq(posi.pos..-1) }
|
132
|
+
#note: post_match is a SubSeq
|
133
|
+
|
134
|
+
#rex.last_match=
|
135
|
+
self.last_match=Thread.current[:last_match]=result
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
#-------------------------------------
|
140
|
+
#if not backwards:
|
141
|
+
#replace \Z with (?!)
|
142
|
+
#replace $ with (?=\n)
|
143
|
+
#if backwards:
|
144
|
+
#replace \A with (?!)
|
145
|
+
#replace ^ with (^) (and adjust addedgroups)
|
146
|
+
#there's no lookback in ruby regexp (yet)
|
147
|
+
#so, ^ in reverse regexp will perhaps lead to unexpected
|
148
|
+
#results. some matches with ^ in them will fail, when they
|
149
|
+
#should have succeeded even if the ^ couldn't match.
|
150
|
+
#you should be pretty much ok if you
|
151
|
+
#don't use ^ within alternation (|) in backwards match.
|
152
|
+
#if anchored, an implicit anchor is added at the end (begin if backwards)
|
153
|
+
#there's also a nice cache,so that the cost of regexp rebuilding is reduced
|
154
|
+
#returns: the modified regex and addedgroups
|
155
|
+
def group_anchors(rex,backwards,anchored=false)
|
156
|
+
@@fs_cache||={}
|
157
|
+
result=@@fs_cache[[rex,backwards,anchored]] and return result
|
158
|
+
if backwards
|
159
|
+
caret,dollar,buffanchor='^',nil,'A'
|
160
|
+
else
|
161
|
+
caret,dollar,buffanchor=nil,'$','Z'
|
162
|
+
end
|
163
|
+
newrex=(anchored ? _anchor(rex,backwards,false) : rex.to_s)
|
164
|
+
|
165
|
+
rewritten=incclass=false
|
166
|
+
groupnum=0
|
167
|
+
addedgroups=[]
|
168
|
+
result=''
|
169
|
+
(frags=newrex.split( /((?:[^\\(\[\]$^]+|\\(?:[CM]-)*[^CMZA])*)/ )).each_index{|i|
|
170
|
+
frag=frags[i]
|
171
|
+
case frag
|
172
|
+
when "\\":
|
173
|
+
if !incclass and frags[i+1][0,1]==buffanchor
|
174
|
+
frags[i+1].slice! 0
|
175
|
+
frag='(?!)'
|
176
|
+
rewritten=true
|
177
|
+
end
|
178
|
+
when caret
|
179
|
+
unless incclass
|
180
|
+
addedgroups<<(groupnum+=1)
|
181
|
+
frag="(^)"
|
182
|
+
rewritten=true
|
183
|
+
end
|
184
|
+
when dollar
|
185
|
+
unless incclass
|
186
|
+
frag="(?=\n)"
|
187
|
+
rewritten=true
|
188
|
+
end
|
189
|
+
when "(": incclass or frags[i+1][0]==?? or groupnum+=1
|
190
|
+
when "[": incclass=true #ignore stuff til ]
|
191
|
+
when "]": incclass=false #stop ignoring stuff
|
192
|
+
end
|
193
|
+
result<<frag
|
194
|
+
}
|
195
|
+
|
196
|
+
newrex=rewritten ? Regexp.new(result) : rex
|
197
|
+
|
198
|
+
@@fs_cache[[rex,backwards,anchored]]=[newrex,addedgroups]
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
#-------------------------------------
|
203
|
+
@@anchor_cache={}
|
204
|
+
#add an anchor to a Regexp-string. normally,
|
205
|
+
def _anchor(str,backwards=false,cache=true)
|
206
|
+
cache and result=@@anchor_cache[[str,backwards]] and return result
|
207
|
+
result=backwards ? "(?:#{str})\\Z" : "\\A(?:#{str})"
|
208
|
+
cache and return @@anchor_cache[[str,backwards]]||=Regexp.new( result )
|
209
|
+
return result
|
210
|
+
end
|
211
|
+
|
212
|
+
#-------------------------------------
|
213
|
+
def fixup_match_result(matchdata,addedgroups,pos_adjust,namelet,&body)
|
214
|
+
|
215
|
+
#remove extra capture results from () we inserted from MatchData
|
216
|
+
#..first extract groups, begin and end idxs from old
|
217
|
+
groups=matchdata.to_a
|
218
|
+
begins=[]
|
219
|
+
ends=[]
|
220
|
+
(0...matchdata.length).each{|i|
|
221
|
+
begins<<matchdata.begin(i)+pos_adjust
|
222
|
+
ends<<matchdata.end(i)+pos_adjust
|
223
|
+
}
|
224
|
+
|
225
|
+
#..remove data at group indexes we added above
|
226
|
+
addedgroups.reverse_each{|groupidx|
|
227
|
+
[groups,begins,ends].each{|arr| arr.delete_at groupidx }
|
228
|
+
}
|
229
|
+
|
230
|
+
#..now change matchdata to use fixed-up arrays
|
231
|
+
result=CorrectedMatchData.new
|
232
|
+
result.begins=begins
|
233
|
+
result.ends=ends
|
234
|
+
result.groups=groups
|
235
|
+
if namelet==:pre
|
236
|
+
result.set_pre_match_body( &body)
|
237
|
+
result.set_post_match_body {matchdata.post_match}
|
238
|
+
else
|
239
|
+
result.set_pre_match_body {matchdata.pre_match}
|
240
|
+
result.set_post_match_body( &body)
|
241
|
+
end
|
242
|
+
result.pos=pos_adjust
|
243
|
+
|
244
|
+
result
|
245
|
+
end
|
246
|
+
|
247
|
+
|
248
|
+
|
249
|
+
#-------------------------------------
|
250
|
+
class CorrectedMatchData < MatchData
|
251
|
+
class<<self
|
252
|
+
alias new allocate
|
253
|
+
end
|
254
|
+
|
255
|
+
def initialize; end
|
256
|
+
|
257
|
+
attr_reader :pos
|
258
|
+
attr_writer :begins,:ends,:groups,:pos
|
259
|
+
|
260
|
+
def set_pre_match_body &body
|
261
|
+
@pre_match_body=body
|
262
|
+
end
|
263
|
+
|
264
|
+
def set_post_match_body &body
|
265
|
+
@post_match_body=body
|
266
|
+
end
|
267
|
+
|
268
|
+
def pre_match
|
269
|
+
@pre_match_body[]
|
270
|
+
end
|
271
|
+
|
272
|
+
def post_match
|
273
|
+
@post_match_body[]
|
274
|
+
end
|
275
|
+
|
276
|
+
def [](*args); @groups[*args] end
|
277
|
+
|
278
|
+
def begin n; @begins[n] end
|
279
|
+
def end n; @ends[n] end
|
280
|
+
def offset n; [@begins[n],@ends[n]] if n<size end
|
281
|
+
|
282
|
+
def to_a; @groups end
|
283
|
+
def to_s; @groups.first end
|
284
|
+
def size; @groups.size end
|
285
|
+
alias length size
|
286
|
+
|
287
|
+
|
288
|
+
|
289
|
+
end
|
290
|
+
|
291
|
+
|
292
|
+
|
293
|
+
def scan(pat)
|
294
|
+
holding? {case pat
|
295
|
+
when Integer:
|
296
|
+
pat==read1 and pat.chr
|
297
|
+
#when SetOfChar: ...
|
298
|
+
when String:
|
299
|
+
pat==read(pat.size) and pat
|
300
|
+
when Regexp:
|
301
|
+
if m=match(pat,true)
|
302
|
+
goto m.end(0)
|
303
|
+
m.to_s
|
304
|
+
end
|
305
|
+
else raise ArgumentError.new("bad scan pattern for Sequence::StringLike")
|
306
|
+
end}
|
307
|
+
end
|
308
|
+
|
309
|
+
def scanback(pat)
|
310
|
+
holding? {case pat
|
311
|
+
when Integer:
|
312
|
+
pat==readback1 and pat.chr
|
313
|
+
#when SetOfChar: ...
|
314
|
+
when String:
|
315
|
+
pat==readback(pat.size) and pat
|
316
|
+
when Regexp:
|
317
|
+
if m=matchback(pat,true)
|
318
|
+
goto m.begin(0)
|
319
|
+
m.to_s
|
320
|
+
end
|
321
|
+
else raise ArgumentError.new("bad scan pattern for Sequence::StringLike")
|
322
|
+
end}
|
323
|
+
end
|
324
|
+
|
325
|
+
def scan_until(pat)
|
326
|
+
at=index( pat,pos) or return
|
327
|
+
newpos=case pat
|
328
|
+
when Regexp:
|
329
|
+
m=last_match
|
330
|
+
s=slice(pos...m.begin(0))
|
331
|
+
m.set_pre_match_body{s}
|
332
|
+
m.end(0)
|
333
|
+
when String: at+pat.size
|
334
|
+
when Integer: at+1
|
335
|
+
#when SetOfChar: huh
|
336
|
+
else raise ArgumentError
|
337
|
+
end
|
338
|
+
return( read newpos-pos)
|
339
|
+
|
340
|
+
=begin
|
341
|
+
holding? {
|
342
|
+
if Regexp===pat
|
343
|
+
until_buffer_len=4*maxmatchlen(false)
|
344
|
+
until_step_len=3*maxmatchlen(false)
|
345
|
+
holding_position{|posi|
|
346
|
+
until posi.eof?
|
347
|
+
if m=posi.match(pat,false,until_buffer_len)
|
348
|
+
pre=read(posi.pos-pos)+m.pre_match
|
349
|
+
m.set_prematch_body {pre} #readjust matchdata to include data between my own pos and posi
|
350
|
+
goto m.end(0) #advance my own position to end of match
|
351
|
+
return m.pre_match+m.to_s #return match and what preceded it
|
352
|
+
end
|
353
|
+
posi.move until_step_len
|
354
|
+
end
|
355
|
+
nil
|
356
|
+
}
|
357
|
+
#elsif SetOfChar===pat: ...
|
358
|
+
else #string or integer
|
359
|
+
i=index(pat,pos)
|
360
|
+
result=read(i-pos)<<pat
|
361
|
+
move(pat.is_a?( Integer ) ? 1 : pat.size)
|
362
|
+
result
|
363
|
+
end
|
364
|
+
}
|
365
|
+
=end
|
366
|
+
end
|
367
|
+
|
368
|
+
def scanback_until(pat)
|
369
|
+
at=rindex( pat,pos) or return
|
370
|
+
newpos=
|
371
|
+
if Regexp===pat
|
372
|
+
m=last_match
|
373
|
+
s=slice(m.end(0)+1..pos)
|
374
|
+
m.set_post_match_body{s}
|
375
|
+
m.begin(0)
|
376
|
+
else at
|
377
|
+
end
|
378
|
+
assert(newpos<=pos)
|
379
|
+
return( readback pos-newpos)
|
380
|
+
|
381
|
+
=begin
|
382
|
+
holding? {
|
383
|
+
if Regexp===pat
|
384
|
+
huh #need to scan til eof, like #scan_until does
|
385
|
+
m=matchback(pat,false) or break
|
386
|
+
goto= m.begin(0)
|
387
|
+
m.to_s+m.post_match
|
388
|
+
#elsif SetOfChar===pat: ...
|
389
|
+
else #string or integer
|
390
|
+
i=rindex(pat,pos)
|
391
|
+
result=readback(pos-i-pat.size)<<pat
|
392
|
+
move( -(pat.is_a? Integer ? 1 : pat.size))
|
393
|
+
result
|
394
|
+
end
|
395
|
+
}
|
396
|
+
=end
|
397
|
+
end
|
398
|
+
|
399
|
+
def push(str)
|
400
|
+
Integer===str and str=str.chr
|
401
|
+
insert size, str
|
402
|
+
end
|
403
|
+
|
404
|
+
def unshift(str)
|
405
|
+
Integer===str and str=str.chr
|
406
|
+
insert 0, str
|
407
|
+
end
|
408
|
+
|
409
|
+
def index pat,pos=0
|
410
|
+
posi= self.begin()
|
411
|
+
until_buffer_len=4*maxmatchlen(false)
|
412
|
+
if Regexp===pat
|
413
|
+
until_step_len=3*maxmatchlen(false)
|
414
|
+
until posi.eof?
|
415
|
+
if m=posi.match(pat,false,until_buffer_len)
|
416
|
+
range=0...m.begin(0)
|
417
|
+
pre=subseq(range)
|
418
|
+
m.set_pre_match_body { pre }
|
419
|
+
self.last_match=m
|
420
|
+
return m.begin(0) #return match and what preceded it
|
421
|
+
end
|
422
|
+
posi.move until_step_len
|
423
|
+
end
|
424
|
+
#elsif SetOfChar===pat; ...
|
425
|
+
else
|
426
|
+
until_step_len=until_buffer_len
|
427
|
+
String===pat and until_step_len-=pat.size-1
|
428
|
+
until posi.eof?
|
429
|
+
buf=posi.readahead(until_buffer_len)
|
430
|
+
if i=buf.index( pat)
|
431
|
+
result=posi.pos+i
|
432
|
+
return result
|
433
|
+
end
|
434
|
+
posi.move until_step_len
|
435
|
+
end
|
436
|
+
end
|
437
|
+
return nil
|
438
|
+
ensure
|
439
|
+
posi.close
|
440
|
+
end
|
441
|
+
|
442
|
+
def rindex pat,pos=size-1
|
443
|
+
posi= self.end()
|
444
|
+
until_buffer_len=4*maxmatchlen(false)
|
445
|
+
if Regexp===pat
|
446
|
+
until_step_len=3*maxmatchlen(false)
|
447
|
+
until posi.pos.zero?
|
448
|
+
if m=posi.matchback(pat,false,until_buffer_len)
|
449
|
+
range=m.end(0)+1..-1
|
450
|
+
post=subseq(range)
|
451
|
+
m.set_post_match_body { post }
|
452
|
+
self.last_match=m
|
453
|
+
posi.close
|
454
|
+
return m.begin(0) #return match and what preceded it
|
455
|
+
end
|
456
|
+
posi.move( -until_step_len )
|
457
|
+
end
|
458
|
+
#elsif SetOfChar===pat; ...
|
459
|
+
else
|
460
|
+
until_step_len=until_buffer_len
|
461
|
+
String===pat and until_step_len-=pat.size-1
|
462
|
+
until posi.pos.zero?
|
463
|
+
buf=posi.readbehind(until_buffer_len)
|
464
|
+
if i=buf.rindex( pat)
|
465
|
+
result=posi.pos-until_buffer_len+i
|
466
|
+
posi.close
|
467
|
+
return result
|
468
|
+
end
|
469
|
+
posi.move( -until_step_len )
|
470
|
+
end
|
471
|
+
end
|
472
|
+
return nil
|
473
|
+
ensure
|
474
|
+
posi.close
|
475
|
+
end
|
476
|
+
|
477
|
+
|
478
|
+
|
479
|
+
|
480
|
+
#be nice to have #pack and #unpack too
|
481
|
+
end
|
482
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# Copyright (C) 2006 Caleb Clausen
|
2
|
+
# Distributed under the terms of Ruby's license.
|
3
|
+
require 'sequence'
|
4
|
+
require 'sequence/usedata'
|
5
|
+
|
6
|
+
class Sequence
|
7
|
+
class SubSeq < Sequence
|
8
|
+
def initialize(seq, first,len)
|
9
|
+
first+len-1>=seq.size and len=seq.size-first
|
10
|
+
@data=seq
|
11
|
+
@pos=0
|
12
|
+
@first,@size=first,len
|
13
|
+
extend seq.like
|
14
|
+
|
15
|
+
#ask for notifications on the parent seq...
|
16
|
+
@data.on_change_notify self
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def change_notification data,first,oldsize,newsize
|
21
|
+
assert @data==data
|
22
|
+
old_first=@first
|
23
|
+
old_size=@size
|
24
|
+
@pos=(_adjust_pos_on_change @first+@pos,first,oldsize,newsize)-@first
|
25
|
+
@size=(_adjust_pos_on_change @first+@size,first,oldsize,newsize)-@first
|
26
|
+
@first=_adjust_pos_on_change @first,first,oldsize,newsize
|
27
|
+
|
28
|
+
notify_change(self, first-@first, oldsize, newsize)
|
29
|
+
end
|
30
|
+
|
31
|
+
def offset; @first end
|
32
|
+
|
33
|
+
def readahead(len)
|
34
|
+
eof? and return new_data
|
35
|
+
len>rest=rest_size and len=rest
|
36
|
+
@data[@pos+offset,len]
|
37
|
+
end
|
38
|
+
|
39
|
+
def readbehind(len)
|
40
|
+
@pos.zero? and return new_data
|
41
|
+
@pos>=len or len=@pos
|
42
|
+
@data[@pos+offset-len,len]
|
43
|
+
end
|
44
|
+
|
45
|
+
def read(len)
|
46
|
+
result=readahead(len)
|
47
|
+
move result.size
|
48
|
+
result
|
49
|
+
end
|
50
|
+
|
51
|
+
def readback(len)
|
52
|
+
result=readbehind(len)
|
53
|
+
move( -result.size)
|
54
|
+
result
|
55
|
+
end
|
56
|
+
|
57
|
+
def eof?
|
58
|
+
@pos>=@size
|
59
|
+
end
|
60
|
+
|
61
|
+
attr_reader :size,:pos
|
62
|
+
|
63
|
+
def _pos=newp
|
64
|
+
@pos=newp
|
65
|
+
end
|
66
|
+
|
67
|
+
def_delegators :@data, :data_class, :new_data
|
68
|
+
|
69
|
+
attr :data
|
70
|
+
|
71
|
+
def subseq *args
|
72
|
+
first,len,only1=_parse_slice_args( *args)
|
73
|
+
SubSeq.new(@data,@first+first,len)
|
74
|
+
end
|
75
|
+
|
76
|
+
def modify(*args)
|
77
|
+
data=args.pop
|
78
|
+
first,len,only1=_parse_slice_args( *args)
|
79
|
+
first+=@first
|
80
|
+
only1 ? @data.modify(first,data) : @data.modify(first,len,data)
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
def closed?
|
85
|
+
super or @data.closed?
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
SubSequence=SubSeq
|
90
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Copyright (C) 2006 Caleb Clausen
|
2
|
+
# Distributed under the terms of Ruby's license.
|
3
|
+
require 'sequence'
|
4
|
+
class Sequence
|
5
|
+
# define #read in terms of #data and @pos.
|
6
|
+
# #data must support #[]
|
7
|
+
class UseData < Sequence
|
8
|
+
|
9
|
+
def read(len)
|
10
|
+
result=readahead(len)
|
11
|
+
@pos+=result.size
|
12
|
+
result
|
13
|
+
end
|
14
|
+
|
15
|
+
def readback(len)
|
16
|
+
result=readbehind(len)
|
17
|
+
@pos-=result.size
|
18
|
+
result
|
19
|
+
end
|
20
|
+
|
21
|
+
def readahead(len)
|
22
|
+
@data[@pos,len]
|
23
|
+
end
|
24
|
+
|
25
|
+
def readbehind(len)
|
26
|
+
len>@pos and len=@pos
|
27
|
+
@data[@pos-len,len]
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def size; data.size end
|
32
|
+
def_delegators :@data, :<<
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|