cursor 0.6 → 0.8
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 +626 -1143
- data/cursor/buffered.rb +89 -0
- data/cursor/circular.rb +245 -0
- data/cursor/circular/indexed.rb +51 -0
- data/cursor/circular/linked.rb +98 -0
- data/cursor/circular/position.rb +105 -0
- data/cursor/circular/shifting.rb +48 -0
- data/cursor/circular/split.rb +106 -0
- data/cursor/indexed.rb +52 -0
- data/cursor/io.rb +342 -0
- data/cursor/lined.rb +49 -0
- data/cursor/linked.rb +62 -0
- data/cursor/position.rb +145 -0
- data/cursor/reversed.rb +122 -0
- data/cursor/shifting.rb +40 -0
- data/cursor/split.rb +45 -0
- data/cursor/test.rb +519 -0
- data/cursor/test_circulars.rb +87 -0
- data/cursor/test_cursors.rb +112 -0
- data/cursor/usedeleteinsert.rb +150 -0
- data/cursor/usenext.rb +145 -0
- data/cursor/usenext/position.rb +71 -0
- data/cursor/useposition.rb +42 -0
- data/cursor/usereadwrite.rb +126 -0
- data/duck.rb +31 -0
- data/regexp_cursor.rb +50 -0
- data/weakrefset.rb +130 -0
- metadata +35 -7
data/cursor/buffered.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# $Id: buffered.rb,v 1.11 2005/07/21 15:15:38 eric_mahurin Exp $
|
2
|
+
|
3
|
+
require 'cursor'
|
4
|
+
require 'cursor/split'
|
5
|
+
require 'cursor/usedeleteinsert'
|
6
|
+
require 'cursor/useposition'
|
7
|
+
|
8
|
+
class Cursor
|
9
|
+
# This class gives unidirectional cursors (i.e. IO pipes) some
|
10
|
+
# bidirectional capabilities. An input cursor (or input IO) and/or an output
|
11
|
+
# cursor (or output IO)
|
12
|
+
# can be specified. The #position, #position?, and #position! methods are
|
13
|
+
# used to control buffering. Full cursor capability (limited by the buffer
|
14
|
+
# cursor) is accessible starting from the first #position. When the end of
|
15
|
+
# the buffer is reached more data is read from the input cursor (if not nil) . When no
|
16
|
+
# #position is outstanding, everything before the buffer cursor is written
|
17
|
+
# to the output cursor (if not nil). If the cursor is attempted
|
18
|
+
# to be moved before the buffer, the output cursor is read in reverse (which
|
19
|
+
# the output cursor may not like).
|
20
|
+
class Buffered < Cursor
|
21
|
+
include UseDeleteInsert
|
22
|
+
include UsePosition
|
23
|
+
def initialize(input,output=nil,output_pos=0,buffer=nil)
|
24
|
+
@input = input
|
25
|
+
@output = output
|
26
|
+
if buffer
|
27
|
+
@buffer = buffer
|
28
|
+
else
|
29
|
+
buffer = input||output
|
30
|
+
@buffer = Split.new(
|
31
|
+
buffer.respond_to?(:new_data) ? buffer.new_data : ""
|
32
|
+
)
|
33
|
+
end
|
34
|
+
@output_pos = output_pos
|
35
|
+
end
|
36
|
+
# :stopdoc:
|
37
|
+
def new_data
|
38
|
+
@buffer.new_data
|
39
|
+
end
|
40
|
+
protected
|
41
|
+
def _delete1after?
|
42
|
+
v0 = @buffer.delete1after?
|
43
|
+
v0.nil? && @input && (v0 = @input.read(1)) && (v0 = v0[0])
|
44
|
+
v0
|
45
|
+
end
|
46
|
+
def _delete1before?
|
47
|
+
v0 = @buffer.delete1before?
|
48
|
+
v0.nil? && @output_pos>0 && (@output_pos -= 1;v0 = @output.read(-1)) && (v0 = v0[0])
|
49
|
+
v0
|
50
|
+
end
|
51
|
+
def _insert1before(v)
|
52
|
+
if not position?
|
53
|
+
len = @buffer.skip!(true)
|
54
|
+
if @output
|
55
|
+
value = len ? @buffer.read(len,nil) : @buffer.new_data
|
56
|
+
value << v
|
57
|
+
@output.write(value)
|
58
|
+
else
|
59
|
+
@buffer.read(len,nil) if len
|
60
|
+
end
|
61
|
+
@output_pos += (len||0)+1
|
62
|
+
else
|
63
|
+
@buffer.insert1before(v)
|
64
|
+
end
|
65
|
+
true
|
66
|
+
end
|
67
|
+
def _insert1after(v)
|
68
|
+
@buffer.insert1after(v)
|
69
|
+
end
|
70
|
+
public
|
71
|
+
def pos(reverse=false) # :yield:
|
72
|
+
if reverse
|
73
|
+
super
|
74
|
+
else
|
75
|
+
@buffer.pos+@output_pos
|
76
|
+
end
|
77
|
+
end
|
78
|
+
def close
|
79
|
+
if @output
|
80
|
+
@buffer.skip!(true)
|
81
|
+
value = @buffer.read!(false) and @output.write(value)
|
82
|
+
end
|
83
|
+
super
|
84
|
+
end
|
85
|
+
# :startdoc:
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
|
data/cursor/circular.rb
ADDED
@@ -0,0 +1,245 @@
|
|
1
|
+
# $Id: circular.rb,v 1.19 2005/07/21 15:15:38 eric_mahurin Exp $
|
2
|
+
|
3
|
+
require 'cursor'
|
4
|
+
|
5
|
+
class Cursor
|
6
|
+
# This Cursor class is used to represent a circular buffer. You can think of
|
7
|
+
# this as having no beginning/end or the current location is always both at the
|
8
|
+
# beginning/end. Because of the circular nature, the methods
|
9
|
+
# #scan_until, #modify,
|
10
|
+
# #each, #collect!, and #map!
|
11
|
+
# are not defined.
|
12
|
+
class Circular < Cursor
|
13
|
+
# Create a circular cursor from a normal finite one.
|
14
|
+
def initialize(cursor)
|
15
|
+
@cursor = cursor
|
16
|
+
end
|
17
|
+
protected
|
18
|
+
def _adjust_delete(len=1,reverse=false)
|
19
|
+
pos = _pos(false)
|
20
|
+
ret = nil
|
21
|
+
@positions.each { |p| ret = p.__send__(:_deletion,pos,len,reverse,ret) }
|
22
|
+
end
|
23
|
+
def _adjust_insert(len=1)
|
24
|
+
pos = _pos(false)
|
25
|
+
ret = nil
|
26
|
+
@positions.each { |p| ret = p.__send__(:_insertion,pos,len,ret) }
|
27
|
+
end
|
28
|
+
def _pos(reverse=false)
|
29
|
+
@cursor.pos(reverse)
|
30
|
+
end
|
31
|
+
def _pos=(p)
|
32
|
+
len = p-_pos((p.nonzero?||1.0/p)<0)
|
33
|
+
len.to_i.abs==skip(len) or raise(IndexError,"invalid pos=#{p}")
|
34
|
+
end
|
35
|
+
public
|
36
|
+
|
37
|
+
# :stopdoc:
|
38
|
+
def new_data
|
39
|
+
@cursor.new_data
|
40
|
+
end
|
41
|
+
def read1next
|
42
|
+
v0 = @cursor.read1next
|
43
|
+
v0.nil? && @cursor.skip!(true) && (v0 = @cursor.read1next)
|
44
|
+
v0
|
45
|
+
end
|
46
|
+
def read1prev
|
47
|
+
v0 = @cursor.read1prev
|
48
|
+
v0.nil? && @cursor.skip!(false) && (v0 = @cursor.read1prev)
|
49
|
+
v0
|
50
|
+
end
|
51
|
+
def read1after
|
52
|
+
v0 = @cursor.read1after
|
53
|
+
v0.nil? && @cursor.skip!(true) && (v0 = @cursor.read1after)
|
54
|
+
v0
|
55
|
+
end
|
56
|
+
def read1before
|
57
|
+
v0 = @cursor.read1before
|
58
|
+
v0.nil? && @cursor.skip!(false) && (v0 = @cursor.read1before)
|
59
|
+
v0
|
60
|
+
end
|
61
|
+
def skip1next
|
62
|
+
@cursor.skip1next || @cursor.skip!(true) && @cursor.skip1next
|
63
|
+
end
|
64
|
+
def skip1prev
|
65
|
+
@cursor.skip1prev || @cursor.skip!(false) && @cursor.skip1prev
|
66
|
+
end
|
67
|
+
def skip1after
|
68
|
+
@cursor.skip1after || @cursor.skip!(true) && @cursor.skip1after
|
69
|
+
end
|
70
|
+
def skip1before
|
71
|
+
@cursor.skip1before || @cursor.skip!(false) && @cursor.skip1before
|
72
|
+
end
|
73
|
+
def delete1after
|
74
|
+
v0 = @cursor.delete1after || @cursor.skip!(true) && @cursor.delete1after
|
75
|
+
v0.nil? || @positions && _adjust_delete
|
76
|
+
v0
|
77
|
+
end
|
78
|
+
def delete1before
|
79
|
+
v0 = @cursor.delete1before || @cursor.skip!(false) && @cursor.delete1before
|
80
|
+
v0.nil? || @positions && _adjust_delete
|
81
|
+
v0
|
82
|
+
end
|
83
|
+
def delete1after?
|
84
|
+
v0 = @cursor.delete1after?
|
85
|
+
v0.nil? && @cursor.skip!(true) && (v0 = @cursor.delete1after?)
|
86
|
+
v0.nil? || @positions && _adjust_delete
|
87
|
+
v0
|
88
|
+
end
|
89
|
+
def delete1before?
|
90
|
+
v0 = @cursor.delete1before?
|
91
|
+
v0.nil? && @cursor.skip!(false) && (v0 = @cursor.delete1before?)
|
92
|
+
v0.nil? || @positions && _adjust_delete
|
93
|
+
v0
|
94
|
+
end
|
95
|
+
def write1next(v)
|
96
|
+
@cursor.write1next(v) || @cursor.skip!(true) && @cursor.write1next(v)
|
97
|
+
end
|
98
|
+
def write1prev(v)
|
99
|
+
@cursor.write1prev(v) || @cursor.skip!(false) && @cursor.write1prev(v)
|
100
|
+
end
|
101
|
+
def write1after(v)
|
102
|
+
@cursor.write1after(v) || @cursor.skip!(true) && @cursor.write1after(v)
|
103
|
+
end
|
104
|
+
def write1before(v)
|
105
|
+
@cursor.write1before(v) || @cursor.skip!(false) && @cursor.write1before(v)
|
106
|
+
end
|
107
|
+
def write1next?(v)
|
108
|
+
v0 = @cursor.write1next?(v)
|
109
|
+
v0.nil? && @cursor.skip!(true) && (v0 = @cursor.write1next?(v))
|
110
|
+
v0
|
111
|
+
end
|
112
|
+
def write1prev?(v)
|
113
|
+
v0 = @cursor.write1prev?(v)
|
114
|
+
v0.nil? && @cursor.skip!(false) && (v0 = @cursor.write1prev?(v))
|
115
|
+
v0
|
116
|
+
end
|
117
|
+
def write1after?(v)
|
118
|
+
v0 = @cursor.write1after?(v)
|
119
|
+
v0.nil? && @cursor.skip!(true) && (v0 = @cursor.write1after?(v))
|
120
|
+
v0
|
121
|
+
end
|
122
|
+
def write1before?(v)
|
123
|
+
v0 = @cursor.write1before?(v)
|
124
|
+
v0.nil? && @cursor.skip!(false) && (v0 = @cursor.write1before?(v))
|
125
|
+
v0
|
126
|
+
end
|
127
|
+
def insert1before(v)
|
128
|
+
@positions && _adjust_insert
|
129
|
+
@cursor.insert1before(v)
|
130
|
+
end
|
131
|
+
def insert1after(v)
|
132
|
+
@positions && _adjust_insert
|
133
|
+
@cursor.insert1after(v)
|
134
|
+
end
|
135
|
+
def scan1next(v)
|
136
|
+
v0 = read1next
|
137
|
+
(v0.nil? || v==v0) ? v0 : (skip1prev;nil)
|
138
|
+
end
|
139
|
+
def scan1prev(v)
|
140
|
+
v0 = read1prev
|
141
|
+
(v0.nil? || v==v0) ? v0 : (skip1next;nil)
|
142
|
+
end
|
143
|
+
def modify1next(r)
|
144
|
+
v0 = read1after
|
145
|
+
(v0.nil? || (v = r[v0]).nil?) ? nil : (write1next!(v);v0)
|
146
|
+
end
|
147
|
+
def modify1prev(r)
|
148
|
+
v0 = read1before
|
149
|
+
(v0.nil? || (v = r[v0]).nil?) ? nil : (write1prev!(v);v0)
|
150
|
+
end
|
151
|
+
# :startdoc:
|
152
|
+
# read over one pass of the data to return where you started
|
153
|
+
def read!(reverse=false,hold=false,value0=new_data)
|
154
|
+
read(reverse ? -size : size,hold,value0)
|
155
|
+
end
|
156
|
+
# skip over one pass of the data to return where you started
|
157
|
+
def skip!(reverse=false,hold=false)
|
158
|
+
hold.nil? ? skip(reverse ? -size : size,hold) : size
|
159
|
+
end
|
160
|
+
# treat the current pos as both the beginning (+0) and the end (-0.0)
|
161
|
+
def pos(reverse=false)
|
162
|
+
reverse ? -0.0 : 0
|
163
|
+
end
|
164
|
+
# any pos is treated as relative to the current pos (0)
|
165
|
+
def pos=(p)
|
166
|
+
skip(p)
|
167
|
+
p
|
168
|
+
end
|
169
|
+
# Compare to +other+. Return 0 for the same location and +1 for not.
|
170
|
+
def <=>(other)
|
171
|
+
position?(other) and begin
|
172
|
+
len = (_pos-other.__send__(:_pos)).to_i
|
173
|
+
(len.zero? || len.abs==size) ? 0 : +1
|
174
|
+
end
|
175
|
+
end
|
176
|
+
# positive distance between self and other
|
177
|
+
def -(other)
|
178
|
+
len = (_pos-other.__send__(:_pos)).to_i
|
179
|
+
len<0 ? size+len : len==size ? 0 : len
|
180
|
+
end
|
181
|
+
# insert an element before the cursor and return self
|
182
|
+
def << (value)
|
183
|
+
insert1before(value)
|
184
|
+
self
|
185
|
+
end
|
186
|
+
# insert an element after the cursor and return self
|
187
|
+
def >> (value)
|
188
|
+
insert1after(value)
|
189
|
+
self
|
190
|
+
end
|
191
|
+
|
192
|
+
# :stopdoc:
|
193
|
+
def size
|
194
|
+
_pos(false)-_pos(true).to_i
|
195
|
+
end
|
196
|
+
alias length size
|
197
|
+
def begin
|
198
|
+
position(false)
|
199
|
+
end
|
200
|
+
def end
|
201
|
+
position(true)
|
202
|
+
end
|
203
|
+
def empty?
|
204
|
+
skip1after.nil?
|
205
|
+
end
|
206
|
+
def clear
|
207
|
+
skip!(false,nil)||0
|
208
|
+
end
|
209
|
+
def data
|
210
|
+
read!(false)
|
211
|
+
end
|
212
|
+
def position(reverse=false,&code) # :yield:
|
213
|
+
p = Position.new(self,reverse)
|
214
|
+
(@positions||=WeakRefSet.new) << p
|
215
|
+
if code
|
216
|
+
begin
|
217
|
+
code[]
|
218
|
+
ensure
|
219
|
+
begin
|
220
|
+
self.position = p
|
221
|
+
ensure
|
222
|
+
p.close
|
223
|
+
end
|
224
|
+
end
|
225
|
+
else
|
226
|
+
p
|
227
|
+
end
|
228
|
+
end
|
229
|
+
def position=(p)
|
230
|
+
self._pos = p.__send__(:_pos,nil)
|
231
|
+
self.prop(nil,p.prop)
|
232
|
+
nil
|
233
|
+
end
|
234
|
+
# :startdoc:
|
235
|
+
undef_method(:scan_until)
|
236
|
+
undef_method(:modify)
|
237
|
+
undef_method(:each)
|
238
|
+
undef_method(:collect!)
|
239
|
+
undef_method(:map!)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
require 'cursor/circular/position'
|
244
|
+
|
245
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# $Id: indexed.rb,v 1.6 2005/07/18 14:46:28 eric_mahurin Exp $
|
2
|
+
|
3
|
+
require 'cursor/circular'
|
4
|
+
require 'cursor/usedeleteinsert'
|
5
|
+
|
6
|
+
class Cursor
|
7
|
+
class Circular
|
8
|
+
# This class implements a circular buffer using an Array or String.
|
9
|
+
class Indexed < Circular
|
10
|
+
include UseDeleteInsert
|
11
|
+
def initialize(data=[],pos=0)
|
12
|
+
@data = data
|
13
|
+
@pos = pos
|
14
|
+
end
|
15
|
+
# :stopdoc:
|
16
|
+
def new_data
|
17
|
+
@data.class.new
|
18
|
+
end
|
19
|
+
protected
|
20
|
+
def _delete1after?
|
21
|
+
@pos = 0 if @pos==@data.size
|
22
|
+
@data.slice!(@pos)
|
23
|
+
end
|
24
|
+
def _delete1before?
|
25
|
+
@pos = @data.size if @pos.zero?
|
26
|
+
@data.slice!(@pos -= 1)
|
27
|
+
ensure
|
28
|
+
@pos = 0 if @pos<0
|
29
|
+
end
|
30
|
+
def _insert1before(v)
|
31
|
+
@data[@pos,0] = (new_data << v)
|
32
|
+
@pos += 1
|
33
|
+
true
|
34
|
+
end
|
35
|
+
def _insert1after(v)
|
36
|
+
@data[@pos,0] = (new_data << v)
|
37
|
+
true
|
38
|
+
end
|
39
|
+
def _pos(reverse=false)
|
40
|
+
reverse ? (@pos-@data.size).nonzero?||-0.0 : @pos
|
41
|
+
end
|
42
|
+
def _pos=(p)
|
43
|
+
@pos = ((p.nonzero?||1.0/p)<0) ? (p+@data.size).to_i : p
|
44
|
+
p
|
45
|
+
end
|
46
|
+
# :startdoc:
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# $Id: linked.rb,v 1.4 2005/07/21 15:15:38 eric_mahurin Exp $
|
2
|
+
|
3
|
+
require 'cursor/circular'
|
4
|
+
require 'cursor/usedeleteinsert'
|
5
|
+
|
6
|
+
class Cursor
|
7
|
+
class Circular
|
8
|
+
# Double-linked list
|
9
|
+
class Linked < Circular
|
10
|
+
include UseDeleteInsert
|
11
|
+
def initialize(before=[],after=before.class.new)
|
12
|
+
@next = nil
|
13
|
+
@prev = nil
|
14
|
+
@data_class = before.class
|
15
|
+
@pos0 = 0
|
16
|
+
@size = 0
|
17
|
+
write(before,false,nil)
|
18
|
+
write(after,true,nil)
|
19
|
+
end
|
20
|
+
# :stopdoc:
|
21
|
+
def new_data
|
22
|
+
@data_class.new
|
23
|
+
end
|
24
|
+
protected
|
25
|
+
def _delete1after?
|
26
|
+
@pos0 = (@size.nonzero?||return) if @pos0.zero?
|
27
|
+
@pos0 -= 1
|
28
|
+
@size -= 1
|
29
|
+
begin
|
30
|
+
@next[1]
|
31
|
+
ensure
|
32
|
+
if @next.equal?(@next[2])
|
33
|
+
@next = nil
|
34
|
+
@prev = nil
|
35
|
+
else
|
36
|
+
@next = @next[2]
|
37
|
+
@next[0] = @prev
|
38
|
+
@prev[2] = @next
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
def _delete1before?
|
43
|
+
if @pos0==@size
|
44
|
+
return if @size.zero?
|
45
|
+
@pos0 = 0
|
46
|
+
end
|
47
|
+
@size -= 1
|
48
|
+
begin
|
49
|
+
@prev[1]
|
50
|
+
ensure
|
51
|
+
if @prev.equal?(@prev[0])
|
52
|
+
@next = nil
|
53
|
+
@prev = nil
|
54
|
+
else
|
55
|
+
@prev = @prev[0]
|
56
|
+
@next[0] = @prev
|
57
|
+
@prev[2] = @next
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
def _insert1before(v)
|
62
|
+
@size += 1
|
63
|
+
if !@next
|
64
|
+
@prev = @next = [nil,v,nil]
|
65
|
+
@prev[2] = @next
|
66
|
+
@next[0] = @prev
|
67
|
+
else
|
68
|
+
node = [@prev,v,@next]
|
69
|
+
@prev[2] = node
|
70
|
+
@next[0] = node
|
71
|
+
@prev = node
|
72
|
+
end
|
73
|
+
true
|
74
|
+
end
|
75
|
+
def _insert1after(v)
|
76
|
+
@pos0 += 1
|
77
|
+
@size += 1
|
78
|
+
if !@next
|
79
|
+
@prev = @next = [nil,v,nil]
|
80
|
+
@prev[2] = @next
|
81
|
+
@next[0] = @prev
|
82
|
+
else
|
83
|
+
node = [@prev,v,@next]
|
84
|
+
@prev[2] = node if @prev
|
85
|
+
@next[0] = node if @next
|
86
|
+
@next = node
|
87
|
+
end
|
88
|
+
true
|
89
|
+
end
|
90
|
+
def _pos(reverse=false)
|
91
|
+
reverse ? -(@pos0.nonzero?||0.0) : @size-@pos0
|
92
|
+
end
|
93
|
+
# :startdoc:
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
|