virtfs 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +154 -0
- data/Rakefile +5 -0
- data/lib/virtfs-nativefs-thick.rb +1 -0
- data/lib/virtfs-nativefs-thin.rb +1 -0
- data/lib/virtfs.rb +38 -0
- data/lib/virtfs/activation.rb +97 -0
- data/lib/virtfs/block_io.rb +140 -0
- data/lib/virtfs/byte_range.rb +71 -0
- data/lib/virtfs/context.rb +300 -0
- data/lib/virtfs/context_manager.rb +175 -0
- data/lib/virtfs/context_switch_class_methods.rb +96 -0
- data/lib/virtfs/delegate_module.rb +40 -0
- data/lib/virtfs/dir_instance_delegate.rb +3 -0
- data/lib/virtfs/exception.rb +13 -0
- data/lib/virtfs/file_instance_delegate.rb +3 -0
- data/lib/virtfs/file_modes_and_options.rb +293 -0
- data/lib/virtfs/find_class_methods.rb +106 -0
- data/lib/virtfs/io_buffer.rb +133 -0
- data/lib/virtfs/io_instance_delegate.rb +3 -0
- data/lib/virtfs/kernel.rb +146 -0
- data/lib/virtfs/nativefs/thick.rb +30 -0
- data/lib/virtfs/nativefs/thick/dir_class_methods.rb +38 -0
- data/lib/virtfs/nativefs/thick/file_class_methods.rb +178 -0
- data/lib/virtfs/nativefs/thin.rb +32 -0
- data/lib/virtfs/nativefs/thin/dir.rb +30 -0
- data/lib/virtfs/nativefs/thin/dir_class_methods.rb +41 -0
- data/lib/virtfs/nativefs/thin/file.rb +112 -0
- data/lib/virtfs/nativefs/thin/file_class_methods.rb +181 -0
- data/lib/virtfs/protofs/protofs.rb +7 -0
- data/lib/virtfs/protofs/protofs_base.rb +12 -0
- data/lib/virtfs/protofs/protofs_dir.rb +13 -0
- data/lib/virtfs/protofs/protofs_dir_class.rb +31 -0
- data/lib/virtfs/protofs/protofs_file.rb +27 -0
- data/lib/virtfs/protofs/protofs_file_class.rb +136 -0
- data/lib/virtfs/stat.rb +100 -0
- data/lib/virtfs/thin_dir_delegator.rb +79 -0
- data/lib/virtfs/thin_file_delegator.rb +77 -0
- data/lib/virtfs/thin_io_delegator_methods.rb +301 -0
- data/lib/virtfs/thin_io_delegator_methods_bufferio.rb +337 -0
- data/lib/virtfs/v_dir.rb +238 -0
- data/lib/virtfs/v_file.rb +480 -0
- data/lib/virtfs/v_io.rb +243 -0
- data/lib/virtfs/v_pathname.rb +128 -0
- data/lib/virtfs/version.rb +3 -0
- data/spec/activate_spec.rb +202 -0
- data/spec/chroot_spec.rb +120 -0
- data/spec/context_manager_class_spec.rb +246 -0
- data/spec/context_manager_instance_spec.rb +255 -0
- data/spec/context_spec.rb +335 -0
- data/spec/data/UTF-16LE-data.txt +0 -0
- data/spec/data/UTF-8-data.txt +212 -0
- data/spec/dir_class_spec.rb +506 -0
- data/spec/dir_instance_spec.rb +208 -0
- data/spec/file_class_spec.rb +2106 -0
- data/spec/file_instance_spec.rb +154 -0
- data/spec/file_modes_and_options_spec.rb +1556 -0
- data/spec/find_spec.rb +142 -0
- data/spec/io_bufferio_size_shared_examples.rb +371 -0
- data/spec/io_bufferio_size_spec.rb +861 -0
- data/spec/io_bufferio_spec.rb +801 -0
- data/spec/io_class_spec.rb +145 -0
- data/spec/io_instance_spec.rb +516 -0
- data/spec/kernel_spec.rb +285 -0
- data/spec/mount_spec.rb +186 -0
- data/spec/nativefs_local_root_spec.rb +132 -0
- data/spec/path_spec.rb +39 -0
- data/spec/spec_helper.rb +126 -0
- data/tasks/rspec.rake +3 -0
- data/tasks/yard.rake +7 -0
- data/test/UTF-8-demo.txt +212 -0
- data/test/bench.rb +18 -0
- data/test/bio_internal_test.rb +45 -0
- data/test/delegate_io.rb +31 -0
- data/test/delegate_module.rb +62 -0
- data/test/encode_test.rb +42 -0
- data/test/enoent_test.rb +30 -0
- data/test/namespace_test.rb +42 -0
- data/test/read_block_valid_encoding.rb +44 -0
- data/test/read_test.rb +78 -0
- data/test/stream_readers.rb +46 -0
- data/test/utf-16-demo.txt +0 -0
- data/test/utf8_to_utf16.rb +77 -0
- data/test/wrapper_test.rb +34 -0
- data/virtfs.gemspec +29 -0
- metadata +230 -0
@@ -0,0 +1,301 @@
|
|
1
|
+
require_relative 'byte_range'
|
2
|
+
require_relative 'io_buffer'
|
3
|
+
require_relative 'thin_io_delegator_methods_bufferio'
|
4
|
+
|
5
|
+
module VirtFS
|
6
|
+
# Dispatches File calls to/from VirtFS and the 'Thin' subsystem
|
7
|
+
#
|
8
|
+
# IO objects are not instantiated directly, because
|
9
|
+
# IO.new delegates to VfsRealIO. These instances methods
|
10
|
+
# are only called through File objects.
|
11
|
+
#
|
12
|
+
module ThinIODelegatorMethods
|
13
|
+
include ThinIODelegatorMethodsBufferio
|
14
|
+
|
15
|
+
# Instance methods
|
16
|
+
def initialize(fs_io_obj, parsed_args)
|
17
|
+
@fs_io_obj = fs_io_obj
|
18
|
+
@size = @fs_io_obj.size
|
19
|
+
@start_byte_addr = 0
|
20
|
+
@end_byte_addr = @size - 1
|
21
|
+
|
22
|
+
@parsed_args = parsed_args
|
23
|
+
@seek_pos = @parsed_args.append? ? @size : 0 # The current byte position within the file.
|
24
|
+
|
25
|
+
@binary_encoding = Encoding.find("ASCII-8BIT")
|
26
|
+
@autoclose = @parsed_args.autoclose?
|
27
|
+
|
28
|
+
bio_init
|
29
|
+
|
30
|
+
enable_finalizer if @autoclose
|
31
|
+
end
|
32
|
+
|
33
|
+
def re_initialize(io_obj)
|
34
|
+
close
|
35
|
+
io_obj.flush
|
36
|
+
|
37
|
+
@fs_io_obj = io_obj.instance_variable_get(:@fs_io_obj)
|
38
|
+
@parsed_args = io_obj.instance_variable_get(:@parsed_args)
|
39
|
+
@seek_pos = io_obj.instance_variable_get(:@seek_pos)
|
40
|
+
|
41
|
+
@size = @fs_io_obj.size
|
42
|
+
@start_byte_addr = 0
|
43
|
+
@end_byte_addr = @size - 1
|
44
|
+
@autoclose = @parsed_args.autoclose?
|
45
|
+
|
46
|
+
bio_reinit(io_obj)
|
47
|
+
|
48
|
+
enable_finalizer if @autoclose
|
49
|
+
end
|
50
|
+
|
51
|
+
def enable_finalizer
|
52
|
+
# XXX ObjectSpace.define_finalizer(self, self.class.finalize(fs_file_obj))
|
53
|
+
end
|
54
|
+
|
55
|
+
def disable_finalizer
|
56
|
+
# XXX
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.finalize(obj)
|
60
|
+
proc { obj.close }
|
61
|
+
end
|
62
|
+
|
63
|
+
def autoclose=(bool)
|
64
|
+
file_open
|
65
|
+
initial_val = @autoclose
|
66
|
+
if (@autoclose = bool ? true : false)
|
67
|
+
enable_finalizer if initial_val == false
|
68
|
+
else
|
69
|
+
disable_finalizer if initial_val == true
|
70
|
+
end
|
71
|
+
bool
|
72
|
+
end
|
73
|
+
|
74
|
+
def autoclose?
|
75
|
+
file_open
|
76
|
+
@autoclose
|
77
|
+
end
|
78
|
+
|
79
|
+
def binmode
|
80
|
+
file_open
|
81
|
+
@parsed_args.binmode
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
def binmode?
|
86
|
+
file_open
|
87
|
+
@parsed_args.binmode?
|
88
|
+
end
|
89
|
+
|
90
|
+
def close
|
91
|
+
file_open
|
92
|
+
@io_buffer.flush
|
93
|
+
@fs_io_obj.close
|
94
|
+
@parsed_args.close
|
95
|
+
@autoclose = false
|
96
|
+
nil
|
97
|
+
end
|
98
|
+
|
99
|
+
def close_on_exec?
|
100
|
+
file_open
|
101
|
+
@fs_io_obj.close_on_exec?
|
102
|
+
end
|
103
|
+
|
104
|
+
def close_on_exec=(bool)
|
105
|
+
@fs_io_obj.close_on_exec = bool
|
106
|
+
end
|
107
|
+
|
108
|
+
def close_read
|
109
|
+
file_open
|
110
|
+
raise IOError, "closing non-duplex IO for reading" unless @parsed_args.rdonly?
|
111
|
+
@parsed_args.close_read
|
112
|
+
@fs_io_obj.close_read
|
113
|
+
end
|
114
|
+
|
115
|
+
def close_write
|
116
|
+
file_open
|
117
|
+
raise IOError, "closing non-duplex IO for writing" unless @parsed_args.wronly?
|
118
|
+
@parsed_args.close_write
|
119
|
+
@fs_io_obj.close_write
|
120
|
+
end
|
121
|
+
|
122
|
+
def closed?
|
123
|
+
@parsed_args.closed?
|
124
|
+
end
|
125
|
+
|
126
|
+
def eof
|
127
|
+
file_open && for_reading
|
128
|
+
@seek_pos > @end_byte_addr
|
129
|
+
end
|
130
|
+
alias_method :eof?, :eof
|
131
|
+
|
132
|
+
def external_encoding
|
133
|
+
file_open
|
134
|
+
@parsed_args.external_encoding
|
135
|
+
end
|
136
|
+
|
137
|
+
def fcntl(cms, arg)
|
138
|
+
file_open
|
139
|
+
@fs_io_obj.fcntl(cms, arg)
|
140
|
+
end
|
141
|
+
|
142
|
+
def fdatasync
|
143
|
+
file_open
|
144
|
+
@fs_io_obj.fdatasync
|
145
|
+
end
|
146
|
+
|
147
|
+
def fileno
|
148
|
+
file_open
|
149
|
+
@fs_io_obj.fileno
|
150
|
+
end
|
151
|
+
alias_method :to_i, :fileno
|
152
|
+
|
153
|
+
def flush
|
154
|
+
file_open
|
155
|
+
@io_buffer.flush
|
156
|
+
@fs_io_obj.flush
|
157
|
+
self
|
158
|
+
end
|
159
|
+
|
160
|
+
def fsync
|
161
|
+
file_open
|
162
|
+
@fs_io_obj.fsync
|
163
|
+
end
|
164
|
+
|
165
|
+
def internal_encoding
|
166
|
+
file_open
|
167
|
+
@parsed_args.internal_encoding
|
168
|
+
end
|
169
|
+
|
170
|
+
def ioctl(cmd, arg)
|
171
|
+
file_open
|
172
|
+
@fs_io_obj.ioctl(cmd, arg)
|
173
|
+
end
|
174
|
+
|
175
|
+
def isatty
|
176
|
+
file_open
|
177
|
+
@fs_io_obj.isatty
|
178
|
+
end
|
179
|
+
alias_method :tty?, :isatty
|
180
|
+
|
181
|
+
def pid
|
182
|
+
file_open
|
183
|
+
@fs_io_obj.pid
|
184
|
+
end
|
185
|
+
|
186
|
+
def pos
|
187
|
+
file_open
|
188
|
+
@seek_pos
|
189
|
+
end
|
190
|
+
alias_method :tell, :pos
|
191
|
+
|
192
|
+
def pos=(p)
|
193
|
+
file_open
|
194
|
+
raise SystemCallError.new(p.to_s, Errno::EINVAL::Errno) if p < 0
|
195
|
+
@seek_pos = p
|
196
|
+
end
|
197
|
+
|
198
|
+
def readpartial(limit, result = "")
|
199
|
+
file_open && for_reading
|
200
|
+
@fs_io_obj.readpartial(limit, result)
|
201
|
+
end
|
202
|
+
|
203
|
+
def reopen(*args)
|
204
|
+
raise ArgumentError, "wrong number of arguments (#{args.length} for 1..2)" if args.empty? || args.length > 2
|
205
|
+
if args[0].respond_to?(:to_str)
|
206
|
+
VFile.new(*args).__getobj__
|
207
|
+
elsif args[0].respond_to?(:__getobj__)
|
208
|
+
args[0].__getobj__.dup
|
209
|
+
else
|
210
|
+
args[0]
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def rewind
|
215
|
+
file_open
|
216
|
+
@seek_pos = 0
|
217
|
+
end
|
218
|
+
|
219
|
+
def seek(offset, whence = IO::SEEK_SET)
|
220
|
+
file_open
|
221
|
+
sysseek(offset, whence)
|
222
|
+
0
|
223
|
+
end
|
224
|
+
|
225
|
+
def set_encoding(*args)
|
226
|
+
file_open
|
227
|
+
@parsed_args.set_encoding(*args)
|
228
|
+
self
|
229
|
+
end
|
230
|
+
|
231
|
+
def stat
|
232
|
+
file_open
|
233
|
+
@fs_io_obj.stat # XXX wrap in VirtFS::Stat
|
234
|
+
end
|
235
|
+
|
236
|
+
def sysread(len, buffer = nil)
|
237
|
+
file_open && for_reading && not_at_eof
|
238
|
+
rv = @fs_io_obj.raw_read(@seek_pos, len)
|
239
|
+
@seek_pos += rv.bytesize
|
240
|
+
buffer.replace(rv) unless buffer.nil?
|
241
|
+
rv
|
242
|
+
end
|
243
|
+
|
244
|
+
def sysseek(offset, whence = IO::SEEK_SET)
|
245
|
+
file_open
|
246
|
+
new_pos = case whence
|
247
|
+
when IO::SEEK_CUR then @seek_pos + offset
|
248
|
+
when IO::SEEK_END then @size + offset
|
249
|
+
when IO::SEEK_SET then @start_byte_addr + offset
|
250
|
+
end
|
251
|
+
|
252
|
+
raise SystemCallError.new(offset.to_s, Errno::EINVAL::Errno) if new_pos < 0
|
253
|
+
@seek_pos = new_pos
|
254
|
+
end
|
255
|
+
|
256
|
+
def syswrite(buf)
|
257
|
+
file_open && for_writing
|
258
|
+
rv = @fs_io_obj.raw_write(@seek_pos, buf)
|
259
|
+
update_write_pos(rv)
|
260
|
+
rv
|
261
|
+
end
|
262
|
+
|
263
|
+
def to_io
|
264
|
+
self
|
265
|
+
end
|
266
|
+
|
267
|
+
def write_nonblock(buf)
|
268
|
+
file_open && for_writing
|
269
|
+
@fs_io_obj.write_nonblock(buf)
|
270
|
+
end
|
271
|
+
|
272
|
+
private
|
273
|
+
|
274
|
+
def file_open
|
275
|
+
raise IOError, "closed stream" if closed?
|
276
|
+
true
|
277
|
+
end
|
278
|
+
|
279
|
+
def for_reading
|
280
|
+
raise IOError, "not opened for reading" unless @parsed_args.read?
|
281
|
+
true
|
282
|
+
end
|
283
|
+
|
284
|
+
def for_writing
|
285
|
+
raise IOError, "not opened for writing" unless @parsed_args.write?
|
286
|
+
true
|
287
|
+
end
|
288
|
+
|
289
|
+
def not_at_eof
|
290
|
+
raise EOFError, "end of file reached" if eof?
|
291
|
+
true
|
292
|
+
end
|
293
|
+
|
294
|
+
def update_write_pos(len)
|
295
|
+
@seek_pos += len
|
296
|
+
return if @seek_pos <= @size
|
297
|
+
@size = @seek_pos
|
298
|
+
@end_byte_addr = @seek_pos - 1
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
@@ -0,0 +1,337 @@
|
|
1
|
+
module VirtFS
|
2
|
+
# Dispatches BufferIO calls to/from VirtFS and the 'Thin' subsystem
|
3
|
+
#
|
4
|
+
module ThinIODelegatorMethodsBufferio
|
5
|
+
attr_reader :min_read_buf_sz, :read_buffer, :end_byte_addr, :fs_io_obj
|
6
|
+
|
7
|
+
MIN_READ_BUF_SZ = 1024 * 32
|
8
|
+
MAX_CHAR_LEN = 8
|
9
|
+
|
10
|
+
def min_read_buf_sz=(val)
|
11
|
+
@min_read_buf_sz = val
|
12
|
+
@io_buffer.min_buf_sz = val
|
13
|
+
end
|
14
|
+
private :min_read_buf_sz=
|
15
|
+
|
16
|
+
def <<(obj)
|
17
|
+
file_open && for_writing
|
18
|
+
write(obj.to_s)
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def bytes # deprecated
|
23
|
+
to_enum(:enumerate_til_eof, :getbyte, nil, nil)
|
24
|
+
end
|
25
|
+
|
26
|
+
def chars # deprecated
|
27
|
+
to_enum(:enumerate_til_eof, :getc, nil, nil)
|
28
|
+
end
|
29
|
+
|
30
|
+
def each(*args, &block)
|
31
|
+
sep, lim = separator_limit_args(args)
|
32
|
+
# block = block_given? ? Proc.new : nil
|
33
|
+
each_common(sep, lim, block)
|
34
|
+
end
|
35
|
+
alias_method :each_line, :each
|
36
|
+
|
37
|
+
def each_common(sep, lim, block)
|
38
|
+
return enumerate_return(:read, self, block) if sep.nil?
|
39
|
+
return enumerate_return(:read_paragraph, self, lim, block) if sep.empty?
|
40
|
+
return enumerate_return(:read_line_with_limit, self, sep, lim, block) if lim > 0
|
41
|
+
enumerate_return(:read_line, self, sep, block)
|
42
|
+
end
|
43
|
+
|
44
|
+
def each_byte
|
45
|
+
block = block_given? ? Proc.new : nil
|
46
|
+
enumerate_return(:getbyte, self, block)
|
47
|
+
end
|
48
|
+
|
49
|
+
def each_char
|
50
|
+
block = block_given? ? Proc.new : nil
|
51
|
+
enumerate_return(:getc, self, block)
|
52
|
+
end
|
53
|
+
|
54
|
+
def each_codepoint
|
55
|
+
block = block_given? ? Proc.new : nil
|
56
|
+
enumerate_return(:codepoint, self, block)
|
57
|
+
end
|
58
|
+
alias_method :codepoints, :each_codepoint
|
59
|
+
|
60
|
+
def codepoint
|
61
|
+
file_open && for_reading
|
62
|
+
return nil if eof?
|
63
|
+
rv = @io_buffer.get_char(@seek_pos)
|
64
|
+
@seek_pos += rv.bytesize
|
65
|
+
rv.ord
|
66
|
+
end
|
67
|
+
private :codepoint
|
68
|
+
|
69
|
+
def getbyte
|
70
|
+
file_open && for_reading
|
71
|
+
return nil if eof?
|
72
|
+
rv = @io_buffer.get_byte(@seek_pos)
|
73
|
+
@seek_pos += 1
|
74
|
+
rv
|
75
|
+
end
|
76
|
+
|
77
|
+
def getc
|
78
|
+
file_open && for_reading
|
79
|
+
return nil if eof?
|
80
|
+
rv = @io_buffer.get_char(@seek_pos)
|
81
|
+
@seek_pos += rv.bytesize
|
82
|
+
rv.encode!(internal_encoding) if internal_encoding
|
83
|
+
rv
|
84
|
+
end
|
85
|
+
|
86
|
+
def gets(*args)
|
87
|
+
file_open && for_reading
|
88
|
+
return nil if eof?
|
89
|
+
sep, lim = separator_limit_args(args)
|
90
|
+
return read if sep.nil?
|
91
|
+
return read_paragraph(lim) if sep.empty?
|
92
|
+
return read_line_with_limit(sep, lim) if lim > 0
|
93
|
+
read_line(sep)
|
94
|
+
end
|
95
|
+
|
96
|
+
def lineno
|
97
|
+
file_open && for_reading
|
98
|
+
@lineno
|
99
|
+
end
|
100
|
+
|
101
|
+
def lineno=(ln)
|
102
|
+
file_open && for_reading
|
103
|
+
raise TypeError, "no implicit conversion from #{ln.class.name} to integer" unless ln.respond_to?(:to_int)
|
104
|
+
@lineno = ln
|
105
|
+
end
|
106
|
+
|
107
|
+
def lines(*args) # deprecated
|
108
|
+
to_enum(:enumerate_til_eof, :gets, self, *args, nil)
|
109
|
+
end
|
110
|
+
|
111
|
+
def print(*args)
|
112
|
+
file_open && for_writing
|
113
|
+
write(objects_to_str(args, true, $_, $\, $,))
|
114
|
+
nil
|
115
|
+
end
|
116
|
+
|
117
|
+
def printf(format_str, *args)
|
118
|
+
file_open && for_writing
|
119
|
+
write(format(format_str, *args))
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
|
123
|
+
def putc(obj)
|
124
|
+
file_open && for_writing
|
125
|
+
c = obj.is_a?(Integer) ? obj.chr : obj.to_s[0]
|
126
|
+
write(c)
|
127
|
+
obj
|
128
|
+
end
|
129
|
+
|
130
|
+
def puts(*args)
|
131
|
+
file_open && for_writing
|
132
|
+
write(objects_to_str(args, false, $/, $/, $/))
|
133
|
+
nil
|
134
|
+
end
|
135
|
+
|
136
|
+
def read(len = nil, buffer = nil)
|
137
|
+
file_open && for_reading
|
138
|
+
if len.nil?
|
139
|
+
return "" if eof?
|
140
|
+
len = @size
|
141
|
+
encode = true
|
142
|
+
else
|
143
|
+
return nil if eof?
|
144
|
+
encode = false
|
145
|
+
end
|
146
|
+
|
147
|
+
rv = @io_buffer.get_str(@seek_pos, len)
|
148
|
+
@seek_pos += rv.bytesize
|
149
|
+
|
150
|
+
if encode
|
151
|
+
rv.force_encoding(external_encoding) if external_encoding
|
152
|
+
rv.encode!(internal_encoding) if internal_encoding
|
153
|
+
end
|
154
|
+
buffer.replace(rv) unless buffer.nil?
|
155
|
+
rv
|
156
|
+
end
|
157
|
+
|
158
|
+
def readbyte
|
159
|
+
file_open && for_reading && not_at_eof
|
160
|
+
getbyte
|
161
|
+
end
|
162
|
+
|
163
|
+
def readchar
|
164
|
+
file_open && for_reading && not_at_eof
|
165
|
+
getc
|
166
|
+
end
|
167
|
+
|
168
|
+
def readline(*args)
|
169
|
+
file_open && for_reading && not_at_eof
|
170
|
+
gets(*args)
|
171
|
+
end
|
172
|
+
|
173
|
+
def readlines(*args)
|
174
|
+
file_open && for_reading
|
175
|
+
each(*args).to_a
|
176
|
+
end
|
177
|
+
|
178
|
+
def sync
|
179
|
+
file_open
|
180
|
+
@io_buffer.sync
|
181
|
+
end
|
182
|
+
|
183
|
+
def sync=(bool)
|
184
|
+
file_open
|
185
|
+
@io_buffer.sync = bool
|
186
|
+
end
|
187
|
+
|
188
|
+
def ungetbyte(val)
|
189
|
+
file_open && for_reading
|
190
|
+
str = val.respond_to?(:to_int) ? val.to_int.chr : val.to_str
|
191
|
+
@io_buffer.cover_range(@seek_pos, 1)
|
192
|
+
@io_buffer.truncate_left(@seek_pos)
|
193
|
+
len = @io_buffer.prepend_bytes(str)
|
194
|
+
@seek_pos -= len
|
195
|
+
nil
|
196
|
+
end
|
197
|
+
|
198
|
+
def ungetc(string)
|
199
|
+
file_open && for_reading
|
200
|
+
@io_buffer.cover_range(@seek_pos, 1)
|
201
|
+
@io_buffer.truncate_left(@seek_pos)
|
202
|
+
len = @io_buffer.prepend_str(string)
|
203
|
+
@seek_pos -= len
|
204
|
+
nil
|
205
|
+
end
|
206
|
+
|
207
|
+
def write(buf)
|
208
|
+
file_open && for_writing
|
209
|
+
buf = buf.dup
|
210
|
+
buf.encode!(external_encoding) if external_encoding
|
211
|
+
buf.force_encoding(@binary_encoding)
|
212
|
+
rv = @io_buffer.write_to_buffer(@seek_pos, buf)
|
213
|
+
update_write_pos(rv)
|
214
|
+
rv
|
215
|
+
end
|
216
|
+
|
217
|
+
private
|
218
|
+
|
219
|
+
#
|
220
|
+
# Called from initialize()
|
221
|
+
#
|
222
|
+
def bio_init
|
223
|
+
@lineno = 0 # The current line number - based on gets calls.
|
224
|
+
@min_read_buf_sz = MIN_READ_BUF_SZ
|
225
|
+
@io_buffer = IOBuffer.new(self, @min_read_buf_sz)
|
226
|
+
end
|
227
|
+
|
228
|
+
def bio_reinit(io_obj)
|
229
|
+
@lineno = io_obj.instance_variable_get(:@lineno)
|
230
|
+
@io_buffer = IOBuffer.new(self, @min_read_buf_sz)
|
231
|
+
end
|
232
|
+
|
233
|
+
#
|
234
|
+
# Return at most lim bytes, or up to end of line.
|
235
|
+
#
|
236
|
+
def read_line_with_limit(sep, lim)
|
237
|
+
file_open
|
238
|
+
@io_buffer.cover_range(@seek_pos, lim)
|
239
|
+
offset = @io_buffer.buf_offset(@seek_pos)
|
240
|
+
if (eol_pos = @io_buffer.buffer.index(sep, offset))
|
241
|
+
eol_len = eol_pos - offset + sep.bytesize - 1
|
242
|
+
@lineno += 1
|
243
|
+
else
|
244
|
+
eol_len = lim + 1
|
245
|
+
@lineno += 1 if eof?
|
246
|
+
end
|
247
|
+
read_from_buf([lim, eol_len, @io_buffer.available_bytes(@seek_pos)].min)
|
248
|
+
end
|
249
|
+
|
250
|
+
#
|
251
|
+
# Return up to end of line, or end of file.
|
252
|
+
#
|
253
|
+
def read_line(sep)
|
254
|
+
file_open
|
255
|
+
@io_buffer.cover_range(@seek_pos, 1)
|
256
|
+
offset = @io_buffer.buf_offset(@seek_pos)
|
257
|
+
while (eol_pos = @io_buffer.buffer.index(sep, offset)).nil?
|
258
|
+
break if @io_buffer.at_eof?
|
259
|
+
@io_buffer.extend_right(@min_read_buf_sz)
|
260
|
+
end
|
261
|
+
|
262
|
+
@lineno += 1
|
263
|
+
return read_from_buf(@io_buffer.available_bytes(@seek_pos)) unless eol_pos
|
264
|
+
read_from_buf(eol_pos - offset + sep.bytesize) if eol_pos
|
265
|
+
end
|
266
|
+
|
267
|
+
#
|
268
|
+
# Return len bytes from the buffer, adjusting the current position.
|
269
|
+
#
|
270
|
+
def read_from_buf(len)
|
271
|
+
rv = @io_buffer.get_str(@seek_pos, len)
|
272
|
+
@seek_pos += len
|
273
|
+
rv.force_encoding(external_encoding) if external_encoding
|
274
|
+
rv.encode!(internal_encoding) if internal_encoding
|
275
|
+
rv
|
276
|
+
end
|
277
|
+
|
278
|
+
def enumerate_til_eof(meth, rv, *args, block)
|
279
|
+
if block
|
280
|
+
block.call(send(meth, *args)) until eof?
|
281
|
+
else
|
282
|
+
yield(send(meth, *args)) until eof?
|
283
|
+
end
|
284
|
+
rv
|
285
|
+
end
|
286
|
+
|
287
|
+
def enumerate_return(meth, ret, *args, block)
|
288
|
+
return to_enum(:enumerate_til_eof, meth, ret, *args, block) if block.nil?
|
289
|
+
enumerate_til_eof(meth, ret, *args, block)
|
290
|
+
end
|
291
|
+
|
292
|
+
#
|
293
|
+
# Parse separator and limit args.
|
294
|
+
#
|
295
|
+
def separator_limit_args(args)
|
296
|
+
case args.length
|
297
|
+
when 0
|
298
|
+
return encode_separator($/), 0
|
299
|
+
when 1
|
300
|
+
return encode_separator($/), args[0].to_int if args[0].respond_to?(:to_int)
|
301
|
+
return encode_separator(args[0]), 0
|
302
|
+
when 2
|
303
|
+
return encode_separator(args[0]), args[1]
|
304
|
+
else
|
305
|
+
raise ArgumentError, "wrong number of arguments (3 for 0..2)"
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
def encode_separator(sep)
|
310
|
+
return nil if sep.nil?
|
311
|
+
sep = sep.encode(external_encoding)
|
312
|
+
sep.force_encoding(@binary_encoding)
|
313
|
+
end
|
314
|
+
|
315
|
+
def objects_to_str(args, dup_sep_ok = true, default = nil, ors = nil, ofs = nil)
|
316
|
+
ret_str = ""
|
317
|
+
if args.empty?
|
318
|
+
ret_str << default if default
|
319
|
+
else
|
320
|
+
first_obj = true
|
321
|
+
args.each do |obj|
|
322
|
+
add_sep(ret_str, ofs, dup_sep_ok) unless first_obj
|
323
|
+
ret_str << obj.to_s
|
324
|
+
first_obj = false
|
325
|
+
end
|
326
|
+
end
|
327
|
+
add_sep(ret_str, ors, dup_sep_ok)
|
328
|
+
ret_str
|
329
|
+
end
|
330
|
+
|
331
|
+
def add_sep(str, sep, dup_sep_ok)
|
332
|
+
return unless sep
|
333
|
+
return if !dup_sep_ok && str.end_with?(sep)
|
334
|
+
str << sep
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|