cursor 0.6 → 0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+
@@ -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
+