sequence 0.1.0

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.
@@ -0,0 +1,272 @@
1
+ # $Id$
2
+ # Copyright (C) 2006 Caleb Clausen
3
+ # Distributed under the terms of Ruby's license.
4
+
5
+ require 'sequence'
6
+
7
+ class Sequence
8
+ # This Sequence class is used to represent a circular buffer. You can think of
9
+ # this as having no beginning/end or the current location is always both at the
10
+ # beginning/end. Because of the circular nature, the methods
11
+ # #scan_until, #modify,
12
+ # #each, #collect!, and #map!
13
+ # are not defined.
14
+ class Circular < Sequence
15
+ # Create a circular sequence from a normal finite one.
16
+ def initialize(sequence,pos=sequence.pos)
17
+ @seq = sequence
18
+ @pos=pos
19
+ @size=sequence.size
20
+ extend sequence.like
21
+
22
+ @seq.on_change_notify self
23
+ end
24
+
25
+ #the default _parse_slice_args isn't forgiving enough about large
26
+ #(positive or negative) indexes
27
+ def _normalize_pos pos, size=nil
28
+ pos
29
+ end
30
+
31
+ def change_notification(cu,first,oldsize,newsize)
32
+ assert(cu.equal?( @seq ))
33
+ @pos =_adjust_pos_on_change(@pos, first,oldsize,newsize)
34
+ @size+=newsize-oldsize
35
+ assert @size==@seq.size
36
+ notify_change(self,first,oldsize,newsize)
37
+ end
38
+
39
+ =begin
40
+ def _adjust_delete(len=1,reverse=false)
41
+ pos = _pos(false)
42
+ ret = nil
43
+ @positions.each { |p| ret = p.__send__(:_deletion,pos,len,reverse,ret) }
44
+ end
45
+ def _adjust_insert(len=1)
46
+ pos = _pos(false)
47
+ ret = nil
48
+ @positions.each { |p| ret = p.__send__(:_insertion,pos,len,ret) }
49
+ end
50
+ =end
51
+ attr_reader :pos,:size
52
+ def _pos=(pos)
53
+ #pos=0 if pos>=size
54
+ @pos=pos
55
+ end
56
+ public
57
+
58
+ # :stopdoc:
59
+ def new_data
60
+ @seq.new_data
61
+ end
62
+ =begin **
63
+ def read1next
64
+ v0 = @seq.read1next
65
+ v0.nil? && @seq.move!(true) && (v0 = @seq.read1next)
66
+ v0
67
+ end
68
+ def read1prev
69
+ v0 = @seq.read1prev
70
+ v0.nil? && @seq.move!(false) && (v0 = @seq.read1prev)
71
+ v0
72
+ end
73
+ def read1after
74
+ v0 = @seq.read1after
75
+ v0.nil? && begin! && (v0 = @seq.read1after)
76
+ v0
77
+ end
78
+ def read1before
79
+ v0 = @seq.read1before
80
+ v0.nil? && @seq.move!(false) && (v0 = @seq.read1before)
81
+ v0
82
+ end
83
+ def skip1next
84
+ @seq.skip1next || @seq.skip!(true) && @seq.skip1next
85
+ end
86
+ def skip1prev
87
+ @seq.skip1prev || @seq.skip!(false) && @seq.skip1prev
88
+ end
89
+ def skip1after
90
+ @seq.skip1after || @seq.skip!(true) && @seq.skip1after
91
+ end
92
+ def skip1before
93
+ @seq.skip1before || @seq.skip!(false) && @seq.skip1before
94
+ end
95
+ def delete1after
96
+ v0 = @seq.delete1after || @seq.skip!(true) && @seq.delete1after
97
+ v0.nil? || @positions && _adjust_delete
98
+ v0
99
+ end
100
+ def delete1before
101
+ v0 = @seq.delete1before || @seq.skip!(false) && @seq.delete1before
102
+ v0.nil? || @positions && _adjust_delete
103
+ v0
104
+ end
105
+ def delete1after?
106
+ v0 = @seq.delete1after?
107
+ v0.nil? && @seq.skip!(true) && (v0 = @seq.delete1after?)
108
+ v0.nil? || @positions && _adjust_delete
109
+ v0
110
+ end
111
+ def delete1before?
112
+ v0 = @seq.delete1before?
113
+ v0.nil? && @seq.skip!(false) && (v0 = @seq.delete1before?)
114
+ v0.nil? || @positions && _adjust_delete
115
+ v0
116
+ end
117
+ def write1next(v)
118
+ @seq.write1next(v) || @seq.skip!(true) && @seq.write1next(v)
119
+ end
120
+ def write1prev(v)
121
+ @seq.write1prev(v) || @seq.skip!(false) && @seq.write1prev(v)
122
+ end
123
+ def write1after(v)
124
+ @seq.write1after(v) || @seq.skip!(true) && @seq.write1after(v)
125
+ end
126
+ def write1before(v)
127
+ @seq.write1before(v) || @seq.skip!(false) && @seq.write1before(v)
128
+ end
129
+ def write1next?(v)
130
+ v0 = @seq.write1next?(v)
131
+ v0.nil? && @seq.skip!(true) && (v0 = @seq.write1next?(v))
132
+ v0
133
+ end
134
+ def write1prev?(v)
135
+ v0 = @seq.write1prev?(v)
136
+ v0.nil? && @seq.skip!(false) && (v0 = @seq.write1prev?(v))
137
+ v0
138
+ end
139
+ def write1after?(v)
140
+ v0 = @seq.write1after?(v)
141
+ v0.nil? && @seq.skip!(true) && (v0 = @seq.write1after?(v))
142
+ v0
143
+ end
144
+ def write1before?(v)
145
+ v0 = @seq.write1before?(v)
146
+ v0.nil? && @seq.skip!(false) && (v0 = @seq.write1before?(v))
147
+ v0
148
+ end
149
+ def insert1before(v)
150
+ @positions && _adjust_insert
151
+ @seq.insert1before(v)
152
+ end
153
+ def insert1after(v)
154
+ @positions && _adjust_insert
155
+ @seq.insert1after(v)
156
+ end
157
+ def scan1next(v)
158
+ v0 = read1next
159
+ (v0.nil? || v==v0) ? v0 : (skip1prev;nil)
160
+ end
161
+ def scan1prev(v)
162
+ v0 = read1prev
163
+ (v0.nil? || v==v0) ? v0 : (skip1next;nil)
164
+ end
165
+ def modify1next(r)
166
+ v0 = read1after
167
+ (v0.nil? || (v = r[v0]).nil?) ? nil : (write1next!(v);v0)
168
+ end
169
+ def modify1prev(r)
170
+ v0 = read1before
171
+ (v0.nil? || (v = r[v0]).nil?) ? nil : (write1prev!(v);v0)
172
+ end
173
+ =end
174
+ # :startdoc:
175
+ # read over one pass of the data to return where you started
176
+ def read!(reverse=false)
177
+ unless reverse
178
+ read(size)
179
+ else
180
+ readback(-size)
181
+ end
182
+ end
183
+ # skip over one pass of the data to return where you started
184
+ def move!(reverse=false)
185
+ size
186
+ end
187
+ alias end! begin!
188
+ alias end begin
189
+ # Compare to +other+.
190
+ def <=>(other)
191
+ position?(other) and pos<=>other.pos
192
+ end
193
+
194
+ =begin ***
195
+ # insert an element before the position and return self
196
+ def << (value)
197
+ insert1before(value)
198
+ self
199
+ end
200
+ # insert an element after the position and return self
201
+ def >> (value)
202
+ insert1after(value)
203
+ self
204
+ end
205
+ =end
206
+ # :stopdoc:
207
+
208
+ def data
209
+ @seq
210
+ end
211
+
212
+ def each
213
+ po=position
214
+ yield read1 until self==position
215
+ po.close
216
+ end
217
+
218
+ def eof?; false end
219
+
220
+ def readahead(len)
221
+ result=@seq[@pos%size,len]
222
+ len-=result.size
223
+ len.zero? and return result
224
+ loops=len/size
225
+
226
+ result+=@seq[0...size]*loops if loops.nonzero?
227
+
228
+ len%=size
229
+
230
+ len.zero? and return result
231
+
232
+ result+=@seq[0,len]
233
+ end
234
+
235
+ def read len
236
+ result=readahead len
237
+ move len
238
+ result
239
+ end
240
+
241
+ def modify(*args)
242
+ data=args.last
243
+ first,len,only1=_parse_slice_args(*args[0...-1])
244
+ first %= size
245
+
246
+ len>size and raise( ArgumentError, "dst len too long")
247
+ first+len>size and raise( ArgumentError, "wraparound modify in circular")
248
+
249
+ @seq.modify(*args)
250
+ end
251
+
252
+ #when reversed and circular, always put the Circular outermost.
253
+ def reverse
254
+ Circular.new @seq.reverse
255
+ end
256
+
257
+ def nearbegin(len,at=pos)
258
+ false
259
+ end
260
+
261
+ def nearend(len,at=pos)
262
+ false
263
+ end
264
+
265
+ def closed?
266
+ super or @seq.closed?
267
+ end
268
+ end
269
+ end
270
+
271
+
272
+
@@ -0,0 +1,260 @@
1
+ # Copyright (C) 2006 Caleb Clausen
2
+ # Distributed under the terms of Ruby's license.
3
+ require 'thread'
4
+ require 'sequence/arraylike'
5
+
6
+ =begin discussion
7
+
8
+ Sequence::Enum is pretty much the same thing as Generator from the
9
+ ruby standard library, but faster.
10
+ (Newer versions of the standard library include an improved
11
+ Generator, which should be about as fast.)
12
+
13
+ The key insight is to realize that Continuations were used in the
14
+ original because an independant call stack is needed (to run #each
15
+ in), separate from the call stack of the user of Generator. However,
16
+ Continuations are only one way to get another call stack; you could
17
+ also use a Thread (plus a Queue, to communicate back the results),
18
+ which doesn't have the same performance problems in ruby.
19
+
20
+ In order to implement #readahead1, I had to invent Queue#peek,
21
+ which tells you what the next element is without taking it from the
22
+ Queue.
23
+
24
+ Actually, I'm using a SizedQueue, not a plain Queue. Otherwise, the
25
+ memory used by the queue could grow without bounds.
26
+
27
+ #begin! was also a small challenge, until I realized that you could
28
+ just restart the Thread. (Hopefully, the enum or block will return
29
+ the same results the second time through.)
30
+
31
+ It's not allowed in Generator, but Sequence::Enum permits you to
32
+ pass in both an enum and a block. The results of the
33
+ block are passed up once the enum is exhausted.
34
+
35
+ #<< allows you to
36
+ add more items to the Sequence::Enum once it has been created. At first,
37
+ this was also aliased to #yield, because I didn't quite realize that
38
+ #yield should only be used inside the constructor's block. Once I
39
+ straightened out #yield, I decided that adding items after
40
+ construction was a cool feature, so I left the capability in.
41
+
42
+ It's clear that this version is faster than the original callcc-
43
+ based Generator, but I'm not sure how much. I was unable to run
44
+ the relevant benchmark to completion on my machine. Even after reducing
45
+ the number of loops in the test by 10x, the callcc version was
46
+ still taking more than 2 hours. (I don't know how much more because
47
+ at that point I grew impatient and gave it the ^C.) My own version
48
+ finishes in about a second or less.
49
+
50
+ I also found that bumping the queue size up to 400 from the original
51
+ 32 made about a 4x difference in running time. This implies to me
52
+ that context switches in ruby are rather more expensive than I
53
+ expected.
54
+ =end
55
+
56
+ if true
57
+
58
+ require 'sequence/generator'
59
+
60
+ class Sequence
61
+ class Enum < Sequence
62
+ include ArrayLike
63
+
64
+ def initialize(enum=nil,&block)
65
+ @gen=Generator.new(enum,&block)
66
+ end
67
+
68
+ def eof?; @gen.end? end
69
+ def pos; @gen.index end
70
+ def begin!; @gen.rewind; 0 end
71
+
72
+ def each &block
73
+ @gen.dup.each &block
74
+ end
75
+
76
+ def read len
77
+ return [] if len.zero? or eof?
78
+ result=[]
79
+ begin
80
+ len.times{ result<<@gen.next }
81
+ rescue EOFError:
82
+ end
83
+
84
+ return result
85
+ end
86
+
87
+ def readahead1
88
+ @gen.current
89
+ rescue EOFError:
90
+ return nil
91
+ end
92
+
93
+ def read1
94
+ @gen.next
95
+ rescue EOFError:
96
+ return nil
97
+ end
98
+
99
+ alias size pos
100
+
101
+
102
+
103
+
104
+ %w[pos= _pos= scan scan_until write [] []=
105
+ holding holding? holding! position
106
+ ].each{|mname| undef_method mname}
107
+ end
108
+ end
109
+
110
+
111
+ else
112
+
113
+
114
+ class Queue
115
+ # Retrieves next data from the queue, without pulling it off the queue.
116
+ # If the queue is empty, the calling thread is
117
+ # suspended until data is pushed onto the queue.
118
+ # If +non_block+ is true, the
119
+ # thread isn't suspended, and an exception is raised.
120
+ def peek(non_block=false)
121
+ raise ThreadError, "queue empty" if non_block and empty?
122
+ Thread.pass while (empty?)
123
+ Thread.critical=true
124
+ result=@que.first
125
+ Thread.critical=false
126
+ result
127
+ end
128
+
129
+ def read(len)
130
+ Thread.critical=true
131
+ result=@que.slice![0...len]
132
+ Thread.critical=false
133
+ result
134
+ end
135
+ end
136
+
137
+
138
+ class Sequence
139
+ class Enum < Sequence
140
+ include ArrayLike
141
+
142
+ def initialize(enum=[],qsize=400,&block)
143
+ @extras=[]
144
+ @extrasmutex=Mutex.new
145
+ init(enum,qsize,&block)
146
+ end
147
+
148
+ def init(enum,qsize,&block)
149
+ @block=block
150
+ @pos=0
151
+ @enum=enum
152
+ @q=q=SizedQueue.new(qsize)
153
+ @thread=Thread.new{
154
+ enum.each{|item| q<<item }
155
+ block[self] if block
156
+ i=0
157
+ while i<@extras.size
158
+ q.push @extrasmutex.synchronize { @extras[i] }
159
+ i+=1
160
+ end
161
+ }
162
+ end
163
+
164
+ #should only be called from inside constructor's block
165
+ def yield(item)
166
+ @q.push item
167
+ end
168
+
169
+ def <<(item)
170
+ @extrasmutex.synchronize { @extras<<item }
171
+ end
172
+
173
+ def begin!
174
+ @thread.kill
175
+ init(@enum,@q.max,&@block)
176
+ 0
177
+ end
178
+
179
+ def readahead1
180
+ current
181
+ rescue EOFError:
182
+ return nil
183
+ end
184
+
185
+ def read1
186
+ self.next
187
+ rescue EOFError:
188
+ return nil
189
+ end
190
+
191
+ def current
192
+ raise EOFError if eof?
193
+ @q.peek
194
+ rescue ThreadError:
195
+ raise EOFError
196
+ end
197
+
198
+ def next
199
+ result=@q.pop
200
+ raise EOFError if !result && eof?
201
+ @pos+=1
202
+ result
203
+ rescue ThreadError:
204
+ raise EOFError
205
+ end
206
+
207
+ def read(len)
208
+ len.zero? and return []
209
+ raise ThreadError if @thread.status=="sleep" and @q.empty?
210
+ result=[]
211
+ begin #loop
212
+ len.times{ result<<@q.pop(true) }
213
+ #feh, should fetch more at a time... Queue needs a #read
214
+ rescue ThreadError:
215
+ len-=result.length
216
+ end until @q.empty? and result.length.zero?
217
+
218
+ ensure
219
+ @pos+=result.length
220
+ return result
221
+ end
222
+
223
+ def size
224
+ @pos+@q.size
225
+ end
226
+
227
+ def eof?
228
+ Thread.pass while @q.empty? and @thread.alive?
229
+ @q.empty? and !@thread.alive?
230
+ end
231
+
232
+ def each(&block)
233
+ copy=dup
234
+ copy.begin!
235
+ until(copy.eof?)
236
+ block.call copy.read1
237
+ end
238
+ end
239
+
240
+ attr :pos
241
+
242
+ #methods for Generator compatibility:
243
+ def rewind; begin!; self end
244
+ alias end? eof?
245
+ alias index pos
246
+ def next?; !end? end
247
+
248
+ %w[pos= _pos= scan scan_until write [] []=
249
+ holding holding? holding! position
250
+ ].each{|mname| undef_method mname}
251
+ end
252
+ end
253
+
254
+ end
255
+
256
+ module Enumerable
257
+ def to_sequence
258
+ Sequence::Enum.new(self)
259
+ end
260
+ end
@@ -0,0 +1,172 @@
1
+ # Copyright (C) 2006 Caleb Clausen
2
+ # Distributed under the terms of Ruby's license.
3
+ # $Id$
4
+
5
+ require 'sequence'
6
+ require 'sequence/stringlike'
7
+
8
+ class Sequence
9
+ # This class treats an IO (or StringIO) as an Sequence. An IO is already
10
+ # like an Sequence, but with a differing interface.
11
+ # Actually, we assume that the IO is capable of seeking, so it most likely
12
+ # must be a File.
13
+ # delete/insert at arbitrary location is not supported.
14
+ class File < Sequence
15
+ # include UseNext
16
+ include StringLike
17
+ def initialize(file,mode="r")
18
+
19
+ case file
20
+ when Integer: file=IO.new(file,mode)
21
+ when String: file=File.new(file,mode)
22
+ else #do nothing, file is of a right type (we hope) already
23
+ end
24
+
25
+ @io = file
26
+ end
27
+ # :stopdoc:
28
+ def new_data
29
+ ''
30
+ end
31
+ def data; @io end
32
+ def read1
33
+ @io.getc
34
+ end
35
+ =begin ***
36
+ def write1next(v)
37
+ @io.eof? ? nil : (@io.putc(v);true)
38
+ end
39
+ def write1next!(v)
40
+ @io.putc(v)
41
+ true
42
+ end
43
+ def skip1prev
44
+ @io.pos.nonzero? && (@io.seek(-1,::IO::SEEK_CUR);true)
45
+ end
46
+ =end
47
+ def readahead1
48
+ v0 = @io.getc
49
+ v0 && @io.ungetc(v0)
50
+ v0
51
+ end
52
+ =begin ***
53
+ def write1after!(v)
54
+ @io.putc(v)
55
+ @io.seek(-1,::IO::SEEK_CUR)
56
+ true
57
+ end
58
+ def skip1next
59
+ @io.getc ? true : nil
60
+ end
61
+ def skip1after
62
+ @io.eof? ? nil : true
63
+ end
64
+ def skip1before
65
+ @io.pos.zero? ? nil : true
66
+ end
67
+ def scan1next(v)
68
+ v0 = @io.getc
69
+ (v0.nil? || v==v0) ? v0 : (@io.ungetc(v0);nil)
70
+ end
71
+ =end
72
+ def size
73
+ @io.stat.size
74
+ end
75
+ def read(len)
76
+ @io.read(len) or ""
77
+ end
78
+ def readahead(len)
79
+ buffer1 = read(len)
80
+ @io.seek(-buffer1.size,::IO::SEEK_CUR)
81
+ buffer1
82
+ end
83
+ def readback(len)
84
+ result=readbehind(len)
85
+ @io.seek(-result.size,::IO::SEEK_CUR)
86
+ result
87
+ end
88
+ def readbehind(len)
89
+ p = @io.pos
90
+ len>p and len=p
91
+ @io.seek(-len,::IO::SEEK_CUR)
92
+ @io.read(len)
93
+ end
94
+ def read!(reverse=false)
95
+ if reverse
96
+ len = @io.pos.nonzero? or return ""
97
+ @io.seek(0, ::IO::SEEK_SET)
98
+ #@io.pos = 0 # BUGGY in v1.8.2
99
+ buffer1 = @io.read(len) || ""
100
+ @io.seek(0, ::IO::SEEK_SET)
101
+ else
102
+ buffer1 = @io.read(nil)
103
+ end
104
+ buffer1
105
+ end
106
+
107
+ def eof?; @io.eof? end
108
+
109
+ def _pos=(p)
110
+ @io.seek(p,::IO::SEEK_SET)
111
+ p
112
+ end
113
+
114
+
115
+ def modify(*args)
116
+ data=args.pop
117
+ first,len,only1=_parse_slice_args(*args)
118
+ if first+len==size #working at end of data?
119
+ holding{
120
+ @io.truncate first if len.nonzero?
121
+ goto first
122
+ @io.write data
123
+ }
124
+ elsif len==data.size #inserted data is same size?
125
+ holding{
126
+ goto first
127
+ @io.write data
128
+ }
129
+ else
130
+ raise ArgumentError,"replace data must be same size or modification must be at very end"
131
+ end
132
+ notify_change(self,first,len,data.size)
133
+ data
134
+ end
135
+
136
+
137
+
138
+ def append(str)
139
+ Integer===str and str=str.chr
140
+ first=nil
141
+ holding{
142
+ end!
143
+ first=pos
144
+ @io.write str
145
+ }
146
+ notify_change(self,first,0,str.size)
147
+ self
148
+ end
149
+
150
+ def close
151
+ @io.close
152
+ super
153
+ end
154
+
155
+ def pos; @io.tell end
156
+ # :startdoc:
157
+ end
158
+ end
159
+
160
+ class File
161
+ # convert a File to a seq
162
+ def to_sequence
163
+ Sequence::File.new(self)
164
+ end
165
+ end
166
+
167
+ class StringIO
168
+ # convert an StringIO to a seq
169
+ def to_sequence
170
+ Sequence::File.new(self)
171
+ end
172
+ end