cursor 0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|