cursor 0.5
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/cursor.rb +1138 -0
- metadata +37 -0
data/cursor.rb
ADDED
@@ -0,0 +1,1138 @@
|
|
1
|
+
#!/bin/env ruby
|
2
|
+
# cursor.rb
|
3
|
+
|
4
|
+
# An object in this Cursor class can be best thought of as a cursor in a text
|
5
|
+
# editor. Many of the same operations apply - insert, delete, replace, copy,
|
6
|
+
# paste, move, goto begin/end, mark position, goto mark, etc. Unlike a
|
7
|
+
# text editor, this class can operate on variety of data, not just characters
|
8
|
+
# and strings. It is up to the derived classes to deal with what type of data
|
9
|
+
# is stored (i.e. characters, arbitrary array objects) and how it is stored (in
|
10
|
+
# an Array, String, IO, mapping to another Cursor, etc).
|
11
|
+
class Cursor
|
12
|
+
|
13
|
+
include Comparable
|
14
|
+
include Enumerable
|
15
|
+
|
16
|
+
# Operate on data before the cursor rather than after
|
17
|
+
Reverse = 1
|
18
|
+
# Hold position - do the operation and come back
|
19
|
+
Hold = 2
|
20
|
+
# Just count the elements from get/delete instead of returning their value
|
21
|
+
Ignore = 4
|
22
|
+
# Read the data being replaced using put and return it (instead of the count)
|
23
|
+
Read = 4
|
24
|
+
# Delete instead of retrieve elements (used with get)
|
25
|
+
Delete = 8
|
26
|
+
# Insert instead of replace elements (used with put)
|
27
|
+
Insert = 8
|
28
|
+
# Put a single element instead of a sequence (needed when it looks like a sequence)
|
29
|
+
Single = 16
|
30
|
+
# Operate on what follows the cursor and then move foward
|
31
|
+
Next = 0
|
32
|
+
# Operate on what precedes the cursor and then move backward
|
33
|
+
Prev = Reverse
|
34
|
+
# Operate on what follows the cursor and hold position
|
35
|
+
After = Hold
|
36
|
+
# Operate on what precedes the cursor and hold position
|
37
|
+
Before = Reverse|Hold
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
# Delete one element at the cursor and return it. The observed bits in +flags+
|
42
|
+
# are +Reverse+ (delete before the cursor instead of after) and +Ignore+
|
43
|
+
# (return true instead of the deleted element). When the cursor is at the end (or beginning),
|
44
|
+
# either nil or the code block result is returned (see #get).
|
45
|
+
# <b>This method must be overriden
|
46
|
+
# in a derived class</b>.
|
47
|
+
def _delete1(flags,&more) # :yield: len=1
|
48
|
+
raise(NotImplementedError)
|
49
|
+
end
|
50
|
+
# Insert one element at the cursor. Only the +Reverse+ bit
|
51
|
+
# is observed in +flags+. nil should be returned. <b>This method must be overriden
|
52
|
+
# in a derived class</b>.
|
53
|
+
def _insert1(value,flags)
|
54
|
+
raise(NotImplementedError)
|
55
|
+
end
|
56
|
+
# Get an element at the cursor. The observed bits in +flags+ are +Reverse+
|
57
|
+
# (read before instead of after the cursor), +Hold+ (don't move the
|
58
|
+
# cursor), and +Ignore+ (just return +true+ instead of the value). When the
|
59
|
+
# cursor is at the end (or beginning), either nil or the code block result
|
60
|
+
# is returned (see #get).
|
61
|
+
def _get1(flags,&more) # :yield: len=1
|
62
|
+
value = _delete1(flags&~Ignore) { |len| more&&more[len] || return }
|
63
|
+
if flags&Hold != 0
|
64
|
+
flags ^= Hold
|
65
|
+
flags ^= Reverse
|
66
|
+
end
|
67
|
+
_insert1(value,flags)
|
68
|
+
(flags&Ignore != 0) ? true : value
|
69
|
+
end
|
70
|
+
# Put an element at the cursor. The observed bits in +flags+ are +Reverse+
|
71
|
+
# (put before instead of after the cursor), +Hold+ (don't move the
|
72
|
+
# cursor), and +Read+ (read and return what is being replaced instead of
|
73
|
+
# just returning +true+). When the cursor is at the end (or beginning),
|
74
|
+
# either nil or the code block result is returned (see #get).
|
75
|
+
# The value is inserted at that point when that happens.
|
76
|
+
def _put1(value,flags,&more) # :yield: len=1
|
77
|
+
value0 = _delete1(flags^Read,&more)
|
78
|
+
if flags&Hold != 0
|
79
|
+
flags ^= Hold
|
80
|
+
flags ^= Reverse
|
81
|
+
end
|
82
|
+
_insert1(value,flags)
|
83
|
+
value0
|
84
|
+
end
|
85
|
+
|
86
|
+
public
|
87
|
+
|
88
|
+
# Create a cursor.
|
89
|
+
def initialize
|
90
|
+
@positions = []
|
91
|
+
end
|
92
|
+
# Return the class used for passing/returning a sequence of elements. This
|
93
|
+
# class must behave as a String or an Array ([], []=, slice, slice!, length).
|
94
|
+
# The default in the base class is Array.
|
95
|
+
def data_class
|
96
|
+
Array
|
97
|
+
end
|
98
|
+
# Get a single element or a sequence of them.
|
99
|
+
#
|
100
|
+
# +len+ can be +nil+ (get one element), a positive number (get a
|
101
|
+
# sequence of that many elements), 0 (get all remaining),
|
102
|
+
# a negative number (get all but that many), or what +data_class+ says
|
103
|
+
# (get a sequence up until it finds a match to +len+).
|
104
|
+
#
|
105
|
+
# The observed bits in +flags+ are +Reverse+
|
106
|
+
# (read before instead of after the cursor), +Hold+ (don't move the
|
107
|
+
# cursor), +Ignore+ (just return +true+ or a count instead of the value),
|
108
|
+
# and +Delete+ (delete the element or sequence while retrieving it).
|
109
|
+
#
|
110
|
+
# If there is left, either nil (no code block) or the result from code block is
|
111
|
+
# returned. This code block could set some variable(s) to say signify it
|
112
|
+
# is done or return data from some other source to make it look as though
|
113
|
+
# it is not done. When returning more data, it should return it in a
|
114
|
+
# String or Array of up to +len+ elements.
|
115
|
+
def get(len=nil,flags=Next,&more) # :yield: len
|
116
|
+
g = (flags&Delete != 0) ? :_delete1 : :_get1
|
117
|
+
return(__send__(g,flags,&more)) if not len
|
118
|
+
if flags&Hold != 0 && flags&Delete == 0
|
119
|
+
flags ^= Hold
|
120
|
+
position { get(len,flags,&more) }
|
121
|
+
elsif len.class==data_class
|
122
|
+
if flags&Ignore != 0
|
123
|
+
value = get(len,flags^Ignore,&more)
|
124
|
+
return(value&&value.size)
|
125
|
+
end
|
126
|
+
value = data_class.new
|
127
|
+
if flags&Reverse != 0
|
128
|
+
i = len.size-1
|
129
|
+
step = -1
|
130
|
+
finish = -1
|
131
|
+
else
|
132
|
+
i = 0
|
133
|
+
step = +1
|
134
|
+
finish = len.size
|
135
|
+
end
|
136
|
+
loop do
|
137
|
+
v = __send__(g,flags) { |len|
|
138
|
+
more&&more[len] || begin
|
139
|
+
value = value.reverse if flags&Reverse != 0
|
140
|
+
value = nil if value.size==0
|
141
|
+
return
|
142
|
+
end
|
143
|
+
}
|
144
|
+
value << v
|
145
|
+
if v==len[i]
|
146
|
+
i += step
|
147
|
+
if i==finish
|
148
|
+
value = value.reverse if flags&Reverse != 0
|
149
|
+
return(value)
|
150
|
+
end
|
151
|
+
else
|
152
|
+
i = 0
|
153
|
+
end
|
154
|
+
end
|
155
|
+
else
|
156
|
+
if flags&Ignore != 0
|
157
|
+
len1 = 0
|
158
|
+
if len<=0
|
159
|
+
if len<0 and flags&Delete != 0
|
160
|
+
value = get(len,flags^Ignore,&more)
|
161
|
+
return(value&&value.size)
|
162
|
+
end
|
163
|
+
continue = true
|
164
|
+
loop do
|
165
|
+
__send__(g,flags) { |len| continue = more&&more[len] }
|
166
|
+
break unless continue
|
167
|
+
len1 += 1
|
168
|
+
end
|
169
|
+
(len..-1).each do
|
170
|
+
_get1(flags^Reverse) { |len|
|
171
|
+
more&&more[len] || (return(len1))
|
172
|
+
}
|
173
|
+
len1 -= 1
|
174
|
+
end
|
175
|
+
else
|
176
|
+
len -= 1
|
177
|
+
while len1<=len
|
178
|
+
__send__(g,flags) { |len|
|
179
|
+
more&&more[len] ||
|
180
|
+
(return(len1==0 ? nil : len1))
|
181
|
+
}
|
182
|
+
len1 += 1
|
183
|
+
end
|
184
|
+
end
|
185
|
+
len1
|
186
|
+
else
|
187
|
+
value = data_class.new
|
188
|
+
if len<=0
|
189
|
+
continue = true
|
190
|
+
loop do
|
191
|
+
v = __send__(g,flags) { |len| continue = more&&more[len] }
|
192
|
+
break unless continue
|
193
|
+
value << v
|
194
|
+
end
|
195
|
+
(len..-1).each do
|
196
|
+
return(nil) if value.size==0
|
197
|
+
v = value.slice!(-1)
|
198
|
+
if flags&Delete != 0
|
199
|
+
_insert1(v,flags&~Read^Reverse)
|
200
|
+
else
|
201
|
+
_get1(flags^Reverse|Ignore) { raise(IndexError,"couldn't get back to where we were") }
|
202
|
+
end
|
203
|
+
end
|
204
|
+
else
|
205
|
+
len -= 1
|
206
|
+
while value.size<=len
|
207
|
+
v = __send__(g,flags&~Ignore) { |len|
|
208
|
+
more&&more[len] || begin
|
209
|
+
if value.size==0
|
210
|
+
value = nil
|
211
|
+
elsif flags&Reverse != 0
|
212
|
+
value = value.reverse
|
213
|
+
end
|
214
|
+
return(value)
|
215
|
+
end
|
216
|
+
}
|
217
|
+
value << v
|
218
|
+
end
|
219
|
+
end
|
220
|
+
flags&Reverse != 0 ? value.reverse : value
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
# Put a single element or a sequence of them at the cursor.
|
225
|
+
#
|
226
|
+
# The observed bits in +flags+ are +Reverse+
|
227
|
+
# (read before instead of after the cursor), +Hold+ (don't move the
|
228
|
+
# cursor), +Read+ (return the what is overwritten instead of +true+ or a count),
|
229
|
+
# +Insert+ (insert rather than replace +value+), and +Single+ (force +value+
|
230
|
+
# to be treated as a single element rather than a sequence).
|
231
|
+
#
|
232
|
+
# When the cursor is at the end (or beginning),
|
233
|
+
# either nil or the code block result is returned (see #get).
|
234
|
+
# The value is inserted at that point when that happens.
|
235
|
+
def put(value,flags=Next,&more) # :yield: len
|
236
|
+
if flags&Single != 0 or value.class!=data_class
|
237
|
+
if flags&Insert != 0
|
238
|
+
if flags&Hold != 0
|
239
|
+
flags ^= Hold
|
240
|
+
flags ^= Reverse
|
241
|
+
end
|
242
|
+
return(_insert1(value,flags,&more))
|
243
|
+
else
|
244
|
+
return(_put1(value,flags,&more))
|
245
|
+
end
|
246
|
+
end
|
247
|
+
if flags&Insert != 0
|
248
|
+
flags |= Single
|
249
|
+
if flags&Hold != 0
|
250
|
+
flags ^= Hold
|
251
|
+
flags ^= Reverse
|
252
|
+
end
|
253
|
+
if flags&Reverse != 0
|
254
|
+
start = value.size-1
|
255
|
+
finish = 0
|
256
|
+
step = -1
|
257
|
+
else
|
258
|
+
start = 0
|
259
|
+
finish = value.size-1
|
260
|
+
step = +1
|
261
|
+
end
|
262
|
+
start.step(finish,step) do |i|
|
263
|
+
_insert1(value[i],flags)
|
264
|
+
end
|
265
|
+
nil
|
266
|
+
elsif flags&Hold != 0
|
267
|
+
flags ^= Hold
|
268
|
+
position { put(value,flags,&more) }
|
269
|
+
else
|
270
|
+
flags |= Single
|
271
|
+
if flags&Reverse != 0
|
272
|
+
start = value.size-1
|
273
|
+
finish = 0
|
274
|
+
step = -1
|
275
|
+
else
|
276
|
+
start = 0
|
277
|
+
finish = value.size-1
|
278
|
+
step = +1
|
279
|
+
end
|
280
|
+
if flags&Read != 0
|
281
|
+
value0 = data_class.new
|
282
|
+
replacing = true
|
283
|
+
start.step(finish,step) do |i|
|
284
|
+
v = _put1(value[i],flags) { |len|
|
285
|
+
replacing = more&&more[len]
|
286
|
+
}
|
287
|
+
value0 << v if replacing
|
288
|
+
end
|
289
|
+
if value0.size==0
|
290
|
+
nil
|
291
|
+
elsif step<0
|
292
|
+
value0.reverse
|
293
|
+
else
|
294
|
+
value0
|
295
|
+
end
|
296
|
+
else
|
297
|
+
len0 = 0
|
298
|
+
replacing = true
|
299
|
+
start.step(finish,step) do |i|
|
300
|
+
_put1(value[i],flags) { |len|
|
301
|
+
replacing = more&&more[len]
|
302
|
+
}
|
303
|
+
len0 += 1 if replacing
|
304
|
+
end
|
305
|
+
if len0==0
|
306
|
+
nil
|
307
|
+
else
|
308
|
+
len0
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
# With no +p+ or code block, this will return an numeric position. This
|
314
|
+
# base class uses 0 to mean the beginning and the total number of elements to
|
315
|
+
# represent the end. Derived classes can shift these relative positions
|
316
|
+
# if desired (i.e. Cursor::Reversed uses negative numbers).
|
317
|
+
#
|
318
|
+
# With a +p+ or code block specified, optionally set #pos= +p+,
|
319
|
+
# execute the code block or find the #pos (without a block), and finally return to where it
|
320
|
+
# started (using #pos/#pos=). The return value will be the
|
321
|
+
# result of code block or the inner #pos.
|
322
|
+
def pos(p=nil,&code) # :yield:
|
323
|
+
if code or p
|
324
|
+
start = pos
|
325
|
+
self.pos = p if p
|
326
|
+
ret = code ? code[] : pos
|
327
|
+
self.pos = start
|
328
|
+
ret
|
329
|
+
else
|
330
|
+
p = get(0,Prev|Ignore)
|
331
|
+
return(0) if not p or p==0
|
332
|
+
get(p,Next|Ignore)==p or raise(IndexError,"couldn't get back to where we were: #{p}")
|
333
|
+
p
|
334
|
+
end
|
335
|
+
end
|
336
|
+
# Returns the #pos
|
337
|
+
def to_i
|
338
|
+
pos
|
339
|
+
end
|
340
|
+
# Returns the string "pos=#{#pos}". Derived classes may override this
|
341
|
+
# to provide more information (i.e. line and column).
|
342
|
+
def to_s
|
343
|
+
"pos=#{pos}"
|
344
|
+
end
|
345
|
+
# Set #pos to be +p+. This base class also allows +p+ to be negative
|
346
|
+
# values measured from the end just like the indices for String/Array.
|
347
|
+
def pos=(p)
|
348
|
+
if p<0
|
349
|
+
get(0,Next|Ignore)
|
350
|
+
return(-0.1) if p>-1
|
351
|
+
p = -(get(-p,Prev|Ignore)||0.1)
|
352
|
+
else
|
353
|
+
get(0,Prev|Ignore)
|
354
|
+
return(0) if p<1
|
355
|
+
get(p,Next|Ignore)||0
|
356
|
+
end
|
357
|
+
end
|
358
|
+
def _finalizer(id) # :nodoc:
|
359
|
+
i = @positions.index(id)
|
360
|
+
@positions.slice!(i) if i
|
361
|
+
end
|
362
|
+
protected :_finalizer
|
363
|
+
# Similar to #pos except Cursor::Position objects (which are Cursors) are
|
364
|
+
# used to save and retrieve the position.
|
365
|
+
def position(p=nil,&code) # :yield:
|
366
|
+
if code or p
|
367
|
+
start = position
|
368
|
+
self.position = p if p
|
369
|
+
ret = code ? code[] : position
|
370
|
+
self.position = start
|
371
|
+
start.close
|
372
|
+
ret
|
373
|
+
else
|
374
|
+
p = Position.new(self,pos)
|
375
|
+
@positions << (p.object_id >> 1)
|
376
|
+
ObjectSpace.define_finalizer(p,method(:_finalizer))
|
377
|
+
p
|
378
|
+
end
|
379
|
+
end
|
380
|
+
# Set the position to +p+. +p+ can be numeric (from #pos) or from #position.
|
381
|
+
def position=(p)
|
382
|
+
if p.respond_to?(:pos)
|
383
|
+
position?(p) or raise(TypeError,"invalid position #{p}")
|
384
|
+
self.pos = p.pos
|
385
|
+
else
|
386
|
+
self.pos = p
|
387
|
+
end
|
388
|
+
end
|
389
|
+
# Without a code block, this queries whether a particular #position +p+ is
|
390
|
+
# valid (is a child) or determines if there is any outstanding #position
|
391
|
+
# (when +p+=+nil+).
|
392
|
+
#
|
393
|
+
# With a code block, it sets optionally sets the #position= +p+ and
|
394
|
+
# and executes the code. If the result of the code is false/nil, it will
|
395
|
+
# return to the original #position (intially saved). Otherwise it will stay where the code
|
396
|
+
# left it. The result of the code block is returned. This is useful when
|
397
|
+
# you want the cursor to stay for a pass/match, and return to try something
|
398
|
+
# else for a fail/mismatch.
|
399
|
+
def position?(p=nil,&code) # :yield:
|
400
|
+
if code
|
401
|
+
start = position
|
402
|
+
self.position = p if p
|
403
|
+
ret = code[]
|
404
|
+
self.position = start if not ret
|
405
|
+
start.close
|
406
|
+
ret
|
407
|
+
elsif p
|
408
|
+
i = @positions.rindex(p.object_id >> 1)
|
409
|
+
if not i
|
410
|
+
return(true) if p.object_id==self.object_id
|
411
|
+
return(nil)
|
412
|
+
end
|
413
|
+
if p.closed?
|
414
|
+
@positions.slice!(i)
|
415
|
+
nil
|
416
|
+
else
|
417
|
+
true
|
418
|
+
end
|
419
|
+
else
|
420
|
+
while @positions.size>0
|
421
|
+
return(true) if not ObjectSpace._id2ref(@positions[0] << 1).closed?
|
422
|
+
@positions.shift
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
# Either discard/close the given #position +p+ or discard/close every child #position (+p+=+nil+).
|
427
|
+
def position!(p=nil)
|
428
|
+
if p
|
429
|
+
p.close
|
430
|
+
position?(p) && raise(RuntimeError,"#{p} didn't seem to close")
|
431
|
+
else
|
432
|
+
@positions.each { |p| ObjectSpace._id2ref(p << 1).close }
|
433
|
+
@positions = []
|
434
|
+
end
|
435
|
+
self
|
436
|
+
end
|
437
|
+
# Close the cursor. This will also close every child #position.
|
438
|
+
def close
|
439
|
+
position!
|
440
|
+
# this should make just about any operation fail
|
441
|
+
instance_variables.each { |v| remove_instance_variable(v) }
|
442
|
+
nil
|
443
|
+
end
|
444
|
+
# Is the cursor closed?
|
445
|
+
def closed?
|
446
|
+
instance_variables.size==0
|
447
|
+
end
|
448
|
+
# Are we at the end? Or beginning if +reverse+.
|
449
|
+
def eof?(reverse=false)
|
450
|
+
flags = After|Ignore
|
451
|
+
flags |= Reverse if reverse
|
452
|
+
not get(nil,flags)
|
453
|
+
end
|
454
|
+
alias_method :eof, :eof?
|
455
|
+
# Is +other+ (either #pos or #position) at the same location as the cursor?
|
456
|
+
def ==(other)
|
457
|
+
if other.respond_to?:pos
|
458
|
+
position?(other) or return(false)
|
459
|
+
other = other.pos
|
460
|
+
else
|
461
|
+
other = pos(other)
|
462
|
+
end
|
463
|
+
pos==other
|
464
|
+
end
|
465
|
+
alias_method :===, :==
|
466
|
+
# Compare +other+ (from #pos or #position) to the current position. +1
|
467
|
+
# for the self is after, -1 for self being before, and 0 for it being at
|
468
|
+
# same location.
|
469
|
+
def <=>(other)
|
470
|
+
if other.respond_to?:pos
|
471
|
+
position?(other) or raise(TypeError,"invalid position #{p}")
|
472
|
+
other = other.pos
|
473
|
+
else
|
474
|
+
other = pos(other)
|
475
|
+
end
|
476
|
+
pos<=>other
|
477
|
+
|
478
|
+
end
|
479
|
+
# If +other+ is from #position, this will return the distance (number
|
480
|
+
# or elements) from +other+ to +self+. This can be +, -, or 0.
|
481
|
+
# If +other+ is numeric, this returns a new position that is decreased by
|
482
|
+
# this amount (positive: before, negative: after). Otherwise, it is taken to
|
483
|
+
# be a +len+ for #get (going backward).
|
484
|
+
def -(other)
|
485
|
+
if other.respond_to?:pos
|
486
|
+
position?(other) or raise(TypeError,"invalid position #{p}")
|
487
|
+
pos.to_i-other.pos.to_i
|
488
|
+
elsif other.kind_of?Numeric
|
489
|
+
if other>0
|
490
|
+
position { get(other,Prev|Ignore);position }
|
491
|
+
elsif other<0
|
492
|
+
position { get(other,Next|Ignore);position }
|
493
|
+
else
|
494
|
+
position
|
495
|
+
end
|
496
|
+
else
|
497
|
+
position { get(other,Prev|Ignore);position }
|
498
|
+
end
|
499
|
+
end
|
500
|
+
# If +other+ is numeric, this returns a new position that is increased by
|
501
|
+
# this amount (positive: before, negative: after). Otherwise, it is taken to
|
502
|
+
# be a +len+ for #get (going forward).
|
503
|
+
def +(other)
|
504
|
+
if other.kind_of?Numeric
|
505
|
+
if other>0
|
506
|
+
position { get(other,Next|Ignore);position }
|
507
|
+
elsif other<0
|
508
|
+
position { get(other,Prev|Ignore);position }
|
509
|
+
else
|
510
|
+
position
|
511
|
+
end
|
512
|
+
else
|
513
|
+
position { get(other,Next|Ignore);position }
|
514
|
+
end
|
515
|
+
end
|
516
|
+
# Returns a Reversed cursor.
|
517
|
+
def -@
|
518
|
+
Cursor::Reversed.new(self)
|
519
|
+
end
|
520
|
+
# Increments the cursor by +len+ (same +len+ of #get - defaults to one element)
|
521
|
+
# Returns +nil+ at the end and self otherwise.
|
522
|
+
def succ!(len=nil); get(len,Next|Ignore) && self; end
|
523
|
+
# Decrements the cursor by +len+ (same +len+ of #get - defaults to one element)
|
524
|
+
# Returns +nil+ at the beginning and self otherwise.
|
525
|
+
def pred!(len=nil); get(len,Prev|Ignore) && self; end
|
526
|
+
# Similar to #succ! except a new #position is returned instead of
|
527
|
+
# modifying the current.
|
528
|
+
def succ(len=nil); position { get(len,Next|Ignore) && position }; end
|
529
|
+
# Similar to #pred! except a new #position is returned instead of
|
530
|
+
# modifying the current.
|
531
|
+
def pred(len=nil); position { get(len,Prev|Ignore) && position }; end
|
532
|
+
# Go to the beginning
|
533
|
+
def first!; get(0,Prev|Ignore); self; end
|
534
|
+
# Go to the end
|
535
|
+
def last!; get(0,Next|Ignore); self; end
|
536
|
+
# Similar to #first! except a new #position is returned instead of
|
537
|
+
# modifying the current.
|
538
|
+
def first; position { get(0,Prev|Ignore);position }; end
|
539
|
+
# Similar to #last! except a new #position is returned instead of
|
540
|
+
# modifying the current.
|
541
|
+
def last; position { get(0,Next|Ignore);position }; end
|
542
|
+
alias_method :begin, :first
|
543
|
+
alias_method :begin!, :first!
|
544
|
+
alias_method :end, :last
|
545
|
+
alias_method :end!, :last!
|
546
|
+
# Returns the number of elements.
|
547
|
+
def size
|
548
|
+
l1 = position { get(0,Next|Ignore) } || 0
|
549
|
+
l0 = position { get(0,Prev|Ignore) } || 0
|
550
|
+
l0+l1
|
551
|
+
end
|
552
|
+
alias_method :length, :size
|
553
|
+
# Determines whether there is anything before or after the cursor.
|
554
|
+
def empty?
|
555
|
+
get(nil,After |Ignore)&&(return(false)) ||
|
556
|
+
get(nil,Before|Ignore)&&(return(false)) ||
|
557
|
+
true
|
558
|
+
end
|
559
|
+
# Removes all elements and returns the number removed
|
560
|
+
def clear
|
561
|
+
(get(0,Next|Delete|Ignore)||0)+(get(0,Prev|Delete|Ignore)||0)
|
562
|
+
end
|
563
|
+
# Appends a single element at the end
|
564
|
+
def << (value)
|
565
|
+
get(0,Next|Ignore)
|
566
|
+
put(value,Next|Single)
|
567
|
+
end
|
568
|
+
# Prepends a single element at the beginning
|
569
|
+
def >> (value)
|
570
|
+
get(0,Prev|Ignore)
|
571
|
+
put(value,Prev|Single)
|
572
|
+
end
|
573
|
+
protected
|
574
|
+
def _index(index,len,flags) # :nodoc:
|
575
|
+
if index.respond_to?:exclude_end?
|
576
|
+
len = (index.last-index.first).to_i
|
577
|
+
if index.exclude_end?
|
578
|
+
len = len.ceil
|
579
|
+
else
|
580
|
+
len = len.to_i+1
|
581
|
+
end
|
582
|
+
index = index.first
|
583
|
+
index += len if flags&Reverse != 0
|
584
|
+
len = 0 if len<0
|
585
|
+
elsif len.kind_of?Numeric
|
586
|
+
if len<0
|
587
|
+
flags ^= Reverse
|
588
|
+
len = -len
|
589
|
+
end
|
590
|
+
len = len.to_i
|
591
|
+
end
|
592
|
+
self.position = index if index
|
593
|
+
[len,flags]
|
594
|
+
end
|
595
|
+
public
|
596
|
+
# Provides random access for the cursor like what is in Array/String.
|
597
|
+
# +index+ can be +nil+ (start at the current location), a numeric (possibly range)
|
598
|
+
# (just like Array/String), and even a range from start to end cursors.
|
599
|
+
# +len+ can be positive/0 (just like in Array/String), negative (goes
|
600
|
+
# in reverse), or even a +data_class+ (passed to #get).
|
601
|
+
# +flags+ can take on the same things they can in get. A code block can
|
602
|
+
# be given just like in #get.
|
603
|
+
def [](index=nil,len=nil,flags=After,&more) # :yield: len
|
604
|
+
len,flags = _index(index,len,flags)
|
605
|
+
return(data_class.new) if len==0
|
606
|
+
get(len,flags,&more)
|
607
|
+
end
|
608
|
+
# Random access store like in Array/String. Accepts the same enhancements
|
609
|
+
# for index, len, and flags as #[] does.
|
610
|
+
def []=(*args) # :args: (index=nil,len=nil,flags=After,value)
|
611
|
+
value = args.slice!(-1)
|
612
|
+
index,len,flags = *args
|
613
|
+
flags ||= After
|
614
|
+
len,flags = _index(index,len,flags)
|
615
|
+
if not len
|
616
|
+
ret = put(value,flags|Single)
|
617
|
+
else
|
618
|
+
flags &= ~Single
|
619
|
+
if len==0
|
620
|
+
flags &= ~Read
|
621
|
+
ret = put(value,flags|Insert)
|
622
|
+
elsif value.size==len
|
623
|
+
ret = put(value,flags&~Insert)
|
624
|
+
else
|
625
|
+
ret = get(len,flags^Ignore|Delete)
|
626
|
+
put(value,flags|Insert)
|
627
|
+
end
|
628
|
+
end
|
629
|
+
if flags&Read != 0
|
630
|
+
ret
|
631
|
+
else
|
632
|
+
value
|
633
|
+
end
|
634
|
+
end
|
635
|
+
alias_method :slice, :[]
|
636
|
+
# Random access slice! like in Array/String. Accepts the same enhancements
|
637
|
+
# for index, len, flags, and code block as #[] does (same except +Delete+
|
638
|
+
# bit is set).
|
639
|
+
def slice!(index=nil,len=nil,flags=After,&more) # :yield: len
|
640
|
+
len,flags = _index(index,len,flags)
|
641
|
+
return(data_class.new) if len==0
|
642
|
+
get(len,flags|Delete,&more)
|
643
|
+
end
|
644
|
+
# Performs each just to make this class Enumerable. Accepts the same enhancements
|
645
|
+
# for index, len, and flags as #[] does. +index+ defaults to nil which
|
646
|
+
# starts where the cursor (like IO) as opposed to the beginning (like Array).
|
647
|
+
# The cursor will be left at the end (or beginning if the +Reverse+ flags bit is use).
|
648
|
+
# nil is returned (or the break value if the code does a break).
|
649
|
+
def each(index=nil,len=nil,flags=Next,&code) # :yield: value
|
650
|
+
len,flags = _index(index,len,flags)
|
651
|
+
raise if len==0 or flags&Hold != 0
|
652
|
+
continue = true
|
653
|
+
loop do
|
654
|
+
value = get(len,flags) { continue = nil }
|
655
|
+
if not continue
|
656
|
+
code[value] if len and value
|
657
|
+
break
|
658
|
+
end
|
659
|
+
code[value]
|
660
|
+
end
|
661
|
+
end
|
662
|
+
# copies data from +index+ to the current location. Uses #[] and #[]=
|
663
|
+
# to accomplish the copy so that all of those options are available.
|
664
|
+
# +pflags are used for the put and +gflags+ for the get.
|
665
|
+
def copy_from(index,len=nil,pflags=After,gflags=After)
|
666
|
+
gflags &= ~Ignore
|
667
|
+
if len
|
668
|
+
pflags |= Single
|
669
|
+
else
|
670
|
+
pflags &= ~Single
|
671
|
+
end
|
672
|
+
self[nil,nil,pflags] = self[index,len,gflags]
|
673
|
+
end
|
674
|
+
# copies data from the current location to +index+. Uses #[] and #[]=
|
675
|
+
# to accomplish the copy so that all of those options are available.
|
676
|
+
# +pflags are used for the put and +gflags+ for the get.
|
677
|
+
def copy_to(index,len=nil,pflags=After,gflags=After)
|
678
|
+
gflags &= ~Ignore
|
679
|
+
if len
|
680
|
+
pflags |= Single
|
681
|
+
else
|
682
|
+
pflags &= ~Single
|
683
|
+
end
|
684
|
+
self[index,nil,pflags] = self[nil,len,gflags]
|
685
|
+
end
|
686
|
+
|
687
|
+
# Objects in this class are mainly used to simply mark/remember the location
|
688
|
+
# of a parent cursor. But, this class also has the fully functionality of the
|
689
|
+
# parent. When this child want to do an operation, it uses the parent to
|
690
|
+
# do it and returns the parent to where it was. Derived classes where the
|
691
|
+
# underlying data is random access may be able to implement this class to
|
692
|
+
# directly access the data rather than go through the parent.
|
693
|
+
class Position < Cursor
|
694
|
+
def initialize(parent,p)
|
695
|
+
@parent = parent
|
696
|
+
@pos = p
|
697
|
+
end
|
698
|
+
def data_class
|
699
|
+
@parent.data_class
|
700
|
+
end
|
701
|
+
def get(*args,&more) # :args: (len=nil, flags=Next) { |len| ... }
|
702
|
+
@parent.pos(@pos) {
|
703
|
+
ret = @parent.get(*args,&more)
|
704
|
+
@pos = @parent.pos
|
705
|
+
ret
|
706
|
+
}
|
707
|
+
end
|
708
|
+
def put(*args,&more) # :args: (value, flags=Next) { |len| ... }
|
709
|
+
@parent.pos(@pos) {
|
710
|
+
ret = @parent.put(*args,&more)
|
711
|
+
@pos = @parent.pos
|
712
|
+
ret
|
713
|
+
}
|
714
|
+
end
|
715
|
+
def position(p=nil,&code) # :yield:
|
716
|
+
if p or code
|
717
|
+
super(p,&code)
|
718
|
+
else
|
719
|
+
@parent.pos(@pos) { @parent.position }
|
720
|
+
end
|
721
|
+
end
|
722
|
+
def position?(p=nil,&code) # :yield:
|
723
|
+
if code
|
724
|
+
super(p,&code)
|
725
|
+
else
|
726
|
+
@parent.position?(p)
|
727
|
+
end
|
728
|
+
end
|
729
|
+
def position!(p=nil)
|
730
|
+
if p
|
731
|
+
@parent.position!(p)
|
732
|
+
else
|
733
|
+
nil
|
734
|
+
end
|
735
|
+
end
|
736
|
+
def close
|
737
|
+
parent = @parent
|
738
|
+
super
|
739
|
+
parent.position?(self)
|
740
|
+
end
|
741
|
+
end
|
742
|
+
|
743
|
+
# This class can be used to reverse the direction of operations on a given
|
744
|
+
# cursor. It operates on the given cursor directly moving it around.
|
745
|
+
class Reversed < Cursor
|
746
|
+
def initialize(cursor)
|
747
|
+
super()
|
748
|
+
@cursor = cursor
|
749
|
+
end
|
750
|
+
def data_class
|
751
|
+
@cursor.data_class
|
752
|
+
end
|
753
|
+
def get(len=nil,flags=Next,&more) # :yield: len
|
754
|
+
@cursor.get(len,flags^Reverse)
|
755
|
+
end
|
756
|
+
def put(value,flags=Next,&more) # :yield: len
|
757
|
+
@cursor.put(value,flags^Reverse,&more)
|
758
|
+
end
|
759
|
+
# Every +pos+ returned here is negative (-0.1: end, -N: beginning)
|
760
|
+
# unlike the base class.
|
761
|
+
def pos(p=nil,&code) # :yield:
|
762
|
+
if p or code
|
763
|
+
super(*args,&code)
|
764
|
+
else
|
765
|
+
p = @cursor.pos
|
766
|
+
p = -p
|
767
|
+
p = -0.1 if p==0
|
768
|
+
p
|
769
|
+
end
|
770
|
+
end
|
771
|
+
def pos=(p)
|
772
|
+
p = -p
|
773
|
+
p = -0.1 if p==0
|
774
|
+
p = (@cursor.pos = p)
|
775
|
+
p = -p
|
776
|
+
p = -0.1 if p==0
|
777
|
+
p
|
778
|
+
end
|
779
|
+
end
|
780
|
+
|
781
|
+
# This class is used to test the base class by overriding as few methods
|
782
|
+
# as possible.
|
783
|
+
class Test < Cursor
|
784
|
+
def initialize(data=[],index=0)
|
785
|
+
super()
|
786
|
+
@data = data
|
787
|
+
@pos = index
|
788
|
+
@data_class = (@data.respond_to?:data_class) ?
|
789
|
+
@data.data_class : @data.class
|
790
|
+
end
|
791
|
+
def data_class
|
792
|
+
@data_class
|
793
|
+
end
|
794
|
+
def _delete1(flags,&more) # :yield: len=1
|
795
|
+
if flags&Reverse != 0
|
796
|
+
@pos>0 && (@pos -= 1)
|
797
|
+
else
|
798
|
+
@pos<@data.size
|
799
|
+
end or return(
|
800
|
+
if ret = more&&more[1]
|
801
|
+
if flags&Ignore != 0
|
802
|
+
ret = true
|
803
|
+
else
|
804
|
+
ret,=ret
|
805
|
+
ret
|
806
|
+
end
|
807
|
+
end
|
808
|
+
)
|
809
|
+
if flags&Ignore != 0
|
810
|
+
@data[@pos,1] = @data_class.new
|
811
|
+
1
|
812
|
+
else
|
813
|
+
@data.slice!(@pos)
|
814
|
+
end
|
815
|
+
end
|
816
|
+
def _insert1(value,flags)
|
817
|
+
@data[@pos,0] = (@data_class.new << value)
|
818
|
+
@pos += 1 if flags&Reverse == 0
|
819
|
+
end
|
820
|
+
end
|
821
|
+
|
822
|
+
# This class puts a cursor on an Array or String.
|
823
|
+
class Indexed < Cursor
|
824
|
+
def initialize(data=[],index=0)
|
825
|
+
super()
|
826
|
+
@data = data
|
827
|
+
@pos = index
|
828
|
+
@data_class = (@data.respond_to?:data_class) ?
|
829
|
+
@data.data_class : @data.class
|
830
|
+
end
|
831
|
+
def data_class
|
832
|
+
@data_class
|
833
|
+
end
|
834
|
+
def _delete1(flags,&more) # :yield: len=1
|
835
|
+
if flags&Reverse != 0
|
836
|
+
@pos>0 && @pos -= 1
|
837
|
+
else
|
838
|
+
@pos<@data.size
|
839
|
+
end or return(
|
840
|
+
if ret = more&&more[1]
|
841
|
+
if flags&Ignore != 0
|
842
|
+
ret = true
|
843
|
+
else
|
844
|
+
ret,=ret
|
845
|
+
ret
|
846
|
+
end
|
847
|
+
end
|
848
|
+
)
|
849
|
+
if flags&Ignore != 0
|
850
|
+
@data[@pos,1] = @data_class.new
|
851
|
+
true
|
852
|
+
else
|
853
|
+
@data.slice!(@pos)
|
854
|
+
end
|
855
|
+
end
|
856
|
+
def _insert1(value,flags)
|
857
|
+
@data[@pos,0] = (@data_class.new << value)
|
858
|
+
@pos += 1 if flags&Reverse == 0
|
859
|
+
end
|
860
|
+
end
|
861
|
+
|
862
|
+
# This class treats an IO (or StringIO) as a Cursor. An IO is already
|
863
|
+
# like a Cursor, but doesn't have as robust an interface. Deleting and
|
864
|
+
# inserting is a slow/painful process.
|
865
|
+
class IO < Cursor
|
866
|
+
def initialize(io=StringIO.new)
|
867
|
+
super()
|
868
|
+
@io = io
|
869
|
+
end
|
870
|
+
def data_class
|
871
|
+
String
|
872
|
+
end
|
873
|
+
def _delete1(flags,&more) # :yield: len=1
|
874
|
+
if flags&Reverse != 0
|
875
|
+
begin
|
876
|
+
@io.seek(-1,::IO::SEEK_CUR)
|
877
|
+
ret = @io.getc
|
878
|
+
rescue
|
879
|
+
ret = nil
|
880
|
+
end
|
881
|
+
else
|
882
|
+
ret = @io.getc
|
883
|
+
end or return(
|
884
|
+
if ret = more&&more[1]
|
885
|
+
if flags&Ignore != 0
|
886
|
+
ret = true
|
887
|
+
else
|
888
|
+
ret,=ret
|
889
|
+
ret
|
890
|
+
end
|
891
|
+
end
|
892
|
+
)
|
893
|
+
ret = true if flags&Ignore != 0
|
894
|
+
after = 0
|
895
|
+
while c = @io.getc
|
896
|
+
after += 1
|
897
|
+
@io.seek(-2,::IO::SEEK_CUR)
|
898
|
+
@io.putc(c)
|
899
|
+
@io.seek(+1,::IO::SEEK_CUR)
|
900
|
+
end
|
901
|
+
@io.seek(-1,::IO::SEEK_CUR)
|
902
|
+
@io.truncate(@io.pos)
|
903
|
+
@io.seek(-after,::IO::SEEK_CUR) if after>0
|
904
|
+
ret
|
905
|
+
end
|
906
|
+
def _insert1(value,flags)
|
907
|
+
after = 0
|
908
|
+
while c = @io.getc
|
909
|
+
after += 1
|
910
|
+
@io.seek(-1,::IO::SEEK_CUR)
|
911
|
+
@io.putc(value)
|
912
|
+
value = c
|
913
|
+
end
|
914
|
+
@io.putc(value)
|
915
|
+
after += 1 if flags&Reverse != 0
|
916
|
+
@io.seek(-after,::IO::SEEK_CUR) if after>0
|
917
|
+
nil
|
918
|
+
end
|
919
|
+
def _get1(flags,&more) # :yield: len=1
|
920
|
+
if flags&Reverse != 0
|
921
|
+
begin
|
922
|
+
@io.seek(-1,::IO::SEEK_CUR)
|
923
|
+
rescue
|
924
|
+
if ret = more&&more[1]
|
925
|
+
ret,=ret
|
926
|
+
if flags&Hold != 0
|
927
|
+
flags ^= Hold
|
928
|
+
flags ^= Reverse
|
929
|
+
end
|
930
|
+
_insert(flags,ret)
|
931
|
+
ret = true if flags&Ignore != 0
|
932
|
+
end
|
933
|
+
return(ret)
|
934
|
+
end
|
935
|
+
if flags&Ignore != 0
|
936
|
+
@io.seek(+1,::IO::SEEK_CUR) if flags&Hold != 0
|
937
|
+
ret = true
|
938
|
+
else
|
939
|
+
ret = @io.getc
|
940
|
+
@io.seek(-1,::IO::SEEK_CUR) if flags&Hold == 0
|
941
|
+
end
|
942
|
+
else
|
943
|
+
if not ret = @io.getc
|
944
|
+
if ret = more&&more[1]
|
945
|
+
ret,=ret
|
946
|
+
@io.putc(ret)
|
947
|
+
@io.seek(-1,::IO::SEEK_CUR) if flags&Hold != 0
|
948
|
+
ret = true if flags&Ignore != 0
|
949
|
+
end
|
950
|
+
return(ret)
|
951
|
+
end
|
952
|
+
@io.ungetc(ret) if flags&Hold != 0
|
953
|
+
ret = true if flags&Ignore != 0
|
954
|
+
end
|
955
|
+
ret
|
956
|
+
end
|
957
|
+
def _put1(value,flags,&more) # :yield: len=1
|
958
|
+
if flags&Reverse != 0
|
959
|
+
begin
|
960
|
+
@io.seek(-1,::IO::SEEK_CUR)
|
961
|
+
rescue
|
962
|
+
if ret = more&&more[1]
|
963
|
+
if flags&Read == 0
|
964
|
+
ret = true
|
965
|
+
else
|
966
|
+
ret,=ret
|
967
|
+
end
|
968
|
+
end
|
969
|
+
if flags&Hold != 0
|
970
|
+
flags ^= Hold
|
971
|
+
flags ^= Reverse
|
972
|
+
end
|
973
|
+
_insert1(value,flags)
|
974
|
+
return(ret)
|
975
|
+
end
|
976
|
+
if flags&Read != 0
|
977
|
+
ret = @io.getc
|
978
|
+
@io.ungetc(ret)
|
979
|
+
else
|
980
|
+
ret = true
|
981
|
+
end
|
982
|
+
@io.putc(value)
|
983
|
+
if flags&Hold == 0
|
984
|
+
@io.seek(-1,::IO::SEEK_CUR)
|
985
|
+
end
|
986
|
+
else
|
987
|
+
if flags&Read != 0
|
988
|
+
if not ret = @io.getc
|
989
|
+
if ret = more&&more[1]
|
990
|
+
ret, = ret
|
991
|
+
end
|
992
|
+
else
|
993
|
+
@io.ungetc(ret)
|
994
|
+
end
|
995
|
+
elsif @io.eof?
|
996
|
+
ret = more&&more[1]&&true
|
997
|
+
else
|
998
|
+
ret = true
|
999
|
+
end
|
1000
|
+
@io.putc(value)
|
1001
|
+
@io.seek(-1,::IO::SEEK_CUR) if flags&Hold != 0
|
1002
|
+
end
|
1003
|
+
ret
|
1004
|
+
end
|
1005
|
+
def close
|
1006
|
+
@io.close
|
1007
|
+
super
|
1008
|
+
end
|
1009
|
+
end
|
1010
|
+
|
1011
|
+
# This Cursor class uses an Array/String for data before the cursor
|
1012
|
+
# and another one for data after the cursor. The result of this is that
|
1013
|
+
# data is only deleted/inserted at the end of these 2. Because of that
|
1014
|
+
# most operations have similar expense and are linear with respect to the
|
1015
|
+
# number of elements being moved, inserted, deleted, replaced, etc.
|
1016
|
+
class Buffer < Cursor
|
1017
|
+
def initialize(before=[],after=before.class.new)
|
1018
|
+
super()
|
1019
|
+
@before = before
|
1020
|
+
@after = after
|
1021
|
+
@data_class = (@before.respond_to?:data_class) ?
|
1022
|
+
@before.data_class : @before.class
|
1023
|
+
end
|
1024
|
+
def data_class
|
1025
|
+
@data_class
|
1026
|
+
end
|
1027
|
+
def _delete1(flags,&more) # :yield: len=1
|
1028
|
+
data = (flags&Reverse != 0) ? @before : @after
|
1029
|
+
data.size>0 or return(
|
1030
|
+
if ret = more&&more[1]
|
1031
|
+
if flags&Ignore != 0
|
1032
|
+
ret = true
|
1033
|
+
else
|
1034
|
+
ret,=ret
|
1035
|
+
ret
|
1036
|
+
end
|
1037
|
+
end
|
1038
|
+
)
|
1039
|
+
if flags&Ignore != 0
|
1040
|
+
data[-1,1] = @data_class.new
|
1041
|
+
true
|
1042
|
+
else
|
1043
|
+
data.slice!(-1)
|
1044
|
+
end
|
1045
|
+
end
|
1046
|
+
def _insert1(value,flags)
|
1047
|
+
data = (flags&Reverse != 0) ? @after : @before
|
1048
|
+
data << value
|
1049
|
+
end
|
1050
|
+
end
|
1051
|
+
|
1052
|
+
# This class gives unidirectional cursors (i.e. IO pipes) some
|
1053
|
+
# bidirectional capabilities. An input cursor and/or an output cursor
|
1054
|
+
# can be specified. The #position, #position?, and #position! methods are
|
1055
|
+
# used to control buffering. Full cursor capability (limited by the buffer
|
1056
|
+
# cursor) is accessible starting from the first #position. When the end of
|
1057
|
+
# the buffer is reached more data is read from the input cursor (if not nil) using #get . When no
|
1058
|
+
# #position is outstanding, everything before the buffer cursor is written
|
1059
|
+
# to the output cursor (if not nil) using #put. If the cursor is attempted
|
1060
|
+
# to be moved before the buffer, the output cursor is read in reverse.
|
1061
|
+
class Buffered < Cursor
|
1062
|
+
def initialize(input,output=nil,buffer=Buffer.new((input||output).data_class.new))
|
1063
|
+
@input = input
|
1064
|
+
@output = output
|
1065
|
+
@buffer = buffer
|
1066
|
+
@offset = 0
|
1067
|
+
super()
|
1068
|
+
end
|
1069
|
+
def data_class
|
1070
|
+
@buffer.data_class
|
1071
|
+
end
|
1072
|
+
def get(len=nil,flags=Next,&more) # :yield: len
|
1073
|
+
value = @buffer.get(len,flags) { |len|
|
1074
|
+
if flags&Reverse != 0
|
1075
|
+
if @offset==0
|
1076
|
+
more&&more[len]
|
1077
|
+
else
|
1078
|
+
v = @output.get(len,Prev,&more)
|
1079
|
+
@offset -= v.size if v
|
1080
|
+
v
|
1081
|
+
end
|
1082
|
+
elsif @input
|
1083
|
+
@input.get(len,Next,&more)
|
1084
|
+
else
|
1085
|
+
more&&more[len]
|
1086
|
+
end
|
1087
|
+
}
|
1088
|
+
if not position?
|
1089
|
+
if output_value = @buffer.get(0,Prev|Delete)
|
1090
|
+
@offset += output_value.size
|
1091
|
+
@output.put(output_value,Next) if @output
|
1092
|
+
end
|
1093
|
+
end
|
1094
|
+
value
|
1095
|
+
end
|
1096
|
+
def put(value,flags=Next,&more) # :yield: len
|
1097
|
+
value0 = @buffer.put(value,flags) { |len|
|
1098
|
+
if flags&Reverse != 0
|
1099
|
+
if @offset==0
|
1100
|
+
more&&more[len]
|
1101
|
+
else
|
1102
|
+
v = @output.get(len,Prev,&more)
|
1103
|
+
@offset -= v.size if v
|
1104
|
+
v
|
1105
|
+
end
|
1106
|
+
elsif @input
|
1107
|
+
@input.get(len,Next,&more)
|
1108
|
+
else
|
1109
|
+
more&&more[len]
|
1110
|
+
end
|
1111
|
+
}
|
1112
|
+
if not position?
|
1113
|
+
if output_value = @buffer.get(0,Prev|Delete)
|
1114
|
+
@offset += output_value.size
|
1115
|
+
@output.put(output_value,Next) if @output
|
1116
|
+
end
|
1117
|
+
end
|
1118
|
+
value0
|
1119
|
+
end
|
1120
|
+
def pos(p=nil,&code) # :yield:
|
1121
|
+
if p or code
|
1122
|
+
super(*args,&code)
|
1123
|
+
else
|
1124
|
+
@buffer.pos+@offset
|
1125
|
+
end
|
1126
|
+
end
|
1127
|
+
def pos=(p)
|
1128
|
+
if p<0
|
1129
|
+
@buffer.pos = p
|
1130
|
+
else
|
1131
|
+
@buffer.pos = p-@offset
|
1132
|
+
end
|
1133
|
+
end
|
1134
|
+
end
|
1135
|
+
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
|
metadata
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.10
|
3
|
+
specification_version: 1
|
4
|
+
name: cursor
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: "0.5"
|
7
|
+
date: 2005-05-19
|
8
|
+
summary: external iterator API
|
9
|
+
require_paths:
|
10
|
+
- "."
|
11
|
+
email:
|
12
|
+
homepage:
|
13
|
+
rubyforge_project: cursor
|
14
|
+
description:
|
15
|
+
autorequire: cursor
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
-
|
22
|
+
- ">"
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.0.0
|
25
|
+
version:
|
26
|
+
platform: ruby
|
27
|
+
authors:
|
28
|
+
- Eric Mahurin
|
29
|
+
files:
|
30
|
+
- cursor.rb
|
31
|
+
test_files: []
|
32
|
+
rdoc_options: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
executables: []
|
35
|
+
extensions: []
|
36
|
+
requirements: []
|
37
|
+
dependencies: []
|