io-like 0.3.1 → 0.4.0.pre1

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.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +1 -1
  3. data/NEWS.md +14 -1
  4. data/README.md +75 -94
  5. data/lib/io/like.rb +1916 -1314
  6. data/lib/io/like_helpers/abstract_io.rb +512 -0
  7. data/lib/io/like_helpers/blocking_io.rb +86 -0
  8. data/lib/io/like_helpers/buffered_io.rb +555 -0
  9. data/lib/io/like_helpers/character_io/basic_reader.rb +122 -0
  10. data/lib/io/like_helpers/character_io/converter_reader.rb +252 -0
  11. data/lib/io/like_helpers/character_io.rb +529 -0
  12. data/lib/io/like_helpers/delegated_io.rb +250 -0
  13. data/lib/io/like_helpers/duplexed_io.rb +259 -0
  14. data/lib/io/like_helpers/io.rb +21 -0
  15. data/lib/io/like_helpers/io_wrapper.rb +290 -0
  16. data/lib/io/like_helpers/pipeline.rb +77 -0
  17. data/lib/io/like_helpers/ruby_facts.rb +33 -0
  18. data/lib/io/like_helpers.rb +14 -0
  19. metadata +107 -224
  20. data/.yardopts +0 -1
  21. data/Rakefile +0 -228
  22. data/ruby.1.8.mspec +0 -7
  23. data/spec/binmode_spec.rb +0 -29
  24. data/spec/close_read_spec.rb +0 -64
  25. data/spec/close_spec.rb +0 -36
  26. data/spec/close_write_spec.rb +0 -61
  27. data/spec/closed_spec.rb +0 -16
  28. data/spec/each_byte_spec.rb +0 -38
  29. data/spec/each_line_spec.rb +0 -11
  30. data/spec/each_spec.rb +0 -11
  31. data/spec/eof_spec.rb +0 -11
  32. data/spec/fixtures/classes.rb +0 -96
  33. data/spec/fixtures/gets.txt +0 -9
  34. data/spec/fixtures/numbered_lines.txt +0 -5
  35. data/spec/fixtures/one_byte.txt +0 -1
  36. data/spec/fixtures/paragraphs.txt +0 -7
  37. data/spec/fixtures/readlines.txt +0 -6
  38. data/spec/flush_spec.rb +0 -8
  39. data/spec/getc_spec.rb +0 -44
  40. data/spec/gets_spec.rb +0 -212
  41. data/spec/isatty_spec.rb +0 -6
  42. data/spec/lineno_spec.rb +0 -84
  43. data/spec/output_spec.rb +0 -47
  44. data/spec/pos_spec.rb +0 -53
  45. data/spec/print_spec.rb +0 -97
  46. data/spec/printf_spec.rb +0 -24
  47. data/spec/putc_spec.rb +0 -57
  48. data/spec/puts_spec.rb +0 -99
  49. data/spec/read_spec.rb +0 -162
  50. data/spec/readchar_spec.rb +0 -49
  51. data/spec/readline_spec.rb +0 -60
  52. data/spec/readlines_spec.rb +0 -140
  53. data/spec/readpartial_spec.rb +0 -92
  54. data/spec/rewind_spec.rb +0 -56
  55. data/spec/seek_spec.rb +0 -72
  56. data/spec/shared/each.rb +0 -204
  57. data/spec/shared/eof.rb +0 -116
  58. data/spec/shared/pos.rb +0 -39
  59. data/spec/shared/tty.rb +0 -12
  60. data/spec/shared/write.rb +0 -53
  61. data/spec/sync_spec.rb +0 -56
  62. data/spec/sysread_spec.rb +0 -87
  63. data/spec/sysseek_spec.rb +0 -68
  64. data/spec/syswrite_spec.rb +0 -60
  65. data/spec/tell_spec.rb +0 -7
  66. data/spec/to_io_spec.rb +0 -19
  67. data/spec/tty_spec.rb +0 -6
  68. data/spec/ungetc_spec.rb +0 -118
  69. data/spec/write_spec.rb +0 -61
  70. data/spec_helper.rb +0 -49
  71. /data/{LICENSE-rubyspec → rubyspec/LICENSE} +0 -0
data/lib/io/like.rb CHANGED
@@ -1,1455 +1,2057 @@
1
- class IO # :nodoc:
2
- # IO::Like is a module which provides most of the basic input and output
3
- # functions of IO objects using methods named _unbuffered_read_,
4
- # _unbuffered_write_, and _unbuffered_seek_.
5
- #
6
- # == Readers
7
- #
8
- # In order to use this module to provide input methods, a class which
9
- # includes it must provide the _unbuffered_read_ method which takes one
10
- # argument, a length, as follows:
11
- #
12
- # def unbuffered_read(length)
13
- # ...
14
- # end
15
- #
16
- # This method must return at most _length_ bytes as a String, raise EOFError
17
- # if reading begins at the end of data, and raise SystemCallError on error.
18
- # Errno::EAGAIN should be raised if there is no data to return immediately and
19
- # the read operation should not block. Errno::EINTR should be raised if the
20
- # read operation is interrupted before any data is read.
21
- #
22
- # == Writers
23
- #
24
- # In order to use this module to provide output methods, a class which
25
- # includes it must provide the _unbuffered_write_ method which takes a single
26
- # string argument as follows:
27
- #
28
- # def unbuffered_write(string)
29
- # ...
30
- # end
31
- #
32
- # This method must either return the number of bytes written to the stream,
33
- # which may be less than the length of _string_ in bytes, OR must raise an
34
- # instance of SystemCallError. Errno::EAGAIN should be raised if no data can
35
- # be written immediately and the write operation should not block.
36
- # Errno::EINTR should be raised if the write operation is interrupted before
37
- # any data is written.
38
- #
39
- # == Seekers
40
- #
41
- # In order to use this module to provide seeking methods, a class which
42
- # includes it must provide the _unbuffered_seek_ method which takes two
43
- # required arguments, an offset and a start position, as follows:
44
- #
45
- # def unbuffered_seek(offset, whence)
46
- # ...
47
- # end
48
- #
49
- # This method must return the new position within the data stream relative to
50
- # the beginning of the stream and should raise SystemCallError on error.
51
- # _offset_ can be any integer and _whence_ can be any of IO::SEEK_SET,
52
- # IO::SEEK_CUR, or IO::SEEK_END. They are interpreted together as follows:
53
- #
54
- # whence | resulting position
55
- # -------------+------------------------------------------------------------
56
- # IO::SEEK_SET | Add offset to the position of the beginning of the stream.
57
- # -------------+------------------------------------------------------------
58
- # IO::SEEK_CUR | Add offset to the current position of the stream.
59
- # -------------+------------------------------------------------------------
60
- # IO::SEEK_END | Add offset to the position of the end of the stream.
61
- #
62
- # == Duplexed Streams
63
- #
64
- # In order to create a duplexed stream where writing and reading happen
65
- # independently of each other, override the #duplexed? method to return
66
- # +true+ and then provide the _unbuffered_read_ and _unbuffered_write_
67
- # methods. Do *NOT* provide an _unbuffered_seek_ method or the contents of
68
- # the internal read and write buffers may be lost unexpectedly.
69
- # ---
70
- # <b>NOTE:</b> Due to limitations of Ruby's finalizer, IO::Like#close is not
71
- # automatically called when the object is garbage collected, so it must be
72
- # explicitly called when the object is no longer needed or risk losing
73
- # whatever data remains in the internal write buffer.
74
- module Like
75
- include Enumerable
76
-
77
- # call-seq:
78
- # ios << obj -> ios
79
- #
80
- # Writes _obj_ to the stream using #write and returns _ios_. _obj_ is
81
- # converted to a String using _to_s_.
82
- def <<(obj)
83
- write(obj)
84
- self
85
- end
86
-
87
- # call-seq:
88
- # ios.binmode -> ios
89
- #
90
- # Returns +self+. Just for compatibility with IO.
91
- def binmode
92
- self
1
+ # frozen_string_literal: true
2
+
3
+ require 'io/like_helpers/duplexed_io'
4
+ require 'io/like_helpers/io'
5
+ require 'io/like_helpers/io_wrapper'
6
+ require 'io/like_helpers/pipeline'
7
+ require 'io/like_helpers/ruby_facts'
8
+
9
+ ##
10
+ # All the goodies for this library go in this namespace. It's probably a bad
11
+ # idea.
12
+ class IO
13
+
14
+ ##
15
+ # This is a wrapper class that provides the same instance methods as the IO
16
+ # class for simpler delegates given to it.
17
+ class Like < LikeHelpers::DuplexedIO
18
+ include LikeHelpers::RubyFacts
19
+ include Enumerable
20
+
21
+ ##
22
+ # This is used by #puts as the separator between all arguments.
23
+ ORS = "\n"
24
+
25
+ ##
26
+ # Creates a new instance of this class.
27
+ #
28
+ # @param delegate_r [LikeHelpers::AbstractIO] delegate for read operations
29
+ # @param delegate_w [LikeHelpers::AbstractIO] delegate for write operations
30
+ # @param autoclose [Boolean] when `true` close the delegate(s) when this
31
+ # stream is closed
32
+ # @param binmode [Boolean] when `true` suppresses EOL <-> CRLF conversion on
33
+ # Windows and sets external encoding to ASCII-8BIT unless explicitly
34
+ # specified
35
+ # @param encoding [Encoding, String] the external encoding or both the
36
+ # external and internal encoding if specified as `"ext_enc:int_enc"`
37
+ # @param encoding_opts [Hash] options to be passed to String#encode
38
+ # @param external_encoding [Encoding, String] the external encoding
39
+ # @param internal_encoding [Encoding, String] the internal encoding
40
+ # @param sync [Boolean] when `true` causes write operations to bypass internal
41
+ # buffering
42
+ # @param pid [Integer] the return value for {#pid}
43
+ def initialize(
44
+ delegate_r,
45
+ delegate_w = delegate_r,
46
+ autoclose: true,
47
+ binmode: false,
48
+ encoding: nil,
49
+ encoding_opts: {},
50
+ external_encoding: nil,
51
+ internal_encoding: nil,
52
+ sync: false,
53
+ pid: nil,
54
+ pipeline_class: LikeHelpers::Pipeline
55
+ )
56
+ if encoding
57
+ if external_encoding
58
+ warn("Ignoring encoding parameter '#{encoding}': external_encoding is used")
59
+ encoding = nil
60
+ elsif internal_encoding
61
+ warn("Ignoring encoding parameter '#{encoding}': internal_encoding is used")
62
+ encoding = nil
63
+ end
93
64
  end
94
65
 
95
- # call-seq:
96
- # ios.close -> nil
97
- #
98
- # Arranges for #closed? to return +true+. Raises IOError if #closed?
99
- # already returns +true+. For duplexed objects, calls #close_read and
100
- # #close_write. For non-duplexed objects, calls #flush if #writable?
101
- # returns +true+ and then sets a flag so that #closed? will return +true+.
102
- def close
103
- raise IOError, 'closed stream' if closed?
104
- __io_like__close_read
105
- flush if writable?
106
- __io_like__close_write
107
- nil
66
+ if external_encoding
67
+ external_encoding = Encoding.find(external_encoding)
68
+ elsif internal_encoding
69
+ external_encoding = Encoding.default_external
108
70
  end
109
71
 
110
- # call-seq:
111
- # ios.close_read -> nil
112
- #
113
- # Closes the read end of a duplexed object or the whole object if the object
114
- # is read-only.
115
- #
116
- # Raises IOError if #closed? returns +true+. Raises IOError for duplexed
117
- # objects if called more than once. Raises IOError for non-duplexed objects
118
- # if #writable? returns +true+.
119
- def close_read
120
- raise IOError, 'closed stream' if closed?
121
- if __io_like__closed_read? || ! duplexed? && writable? then
122
- raise IOError, 'closing non-duplex IO for reading'
123
- end
124
- if duplexed? then
125
- __io_like__close_read
72
+ @pipeline_class = pipeline_class
73
+ pipeline_r = @pipeline_class.new(delegate_r, autoclose: autoclose)
74
+ pipeline_w = delegate_r == delegate_w ?
75
+ pipeline_r :
76
+ @pipeline_class.new(delegate_w, autoclose: autoclose)
77
+
78
+ super(pipeline_r, pipeline_w)
79
+
80
+ # NOTE:
81
+ # Binary mode must be set before the encoding in order to allow any
82
+ # explicitly set external encoding to override the implicit ASCII-8BIT
83
+ # encoding when binmode is set.
84
+ @binmode = false
85
+ self.binmode if binmode
86
+ if ! binmode || encoding || external_encoding || internal_encoding
87
+ if encoding && ! (Encoding === encoding) && encoding =~ /^bom\|/i
88
+ if ! set_encoding_by_bom
89
+ set_encoding(encoding.to_s[4..-1], **encoding_opts)
90
+ end
126
91
  else
127
- close
92
+ set_encoding(
93
+ encoding || external_encoding, internal_encoding, **encoding_opts
94
+ )
128
95
  end
129
- nil
130
96
  end
131
97
 
132
- # call-seq:
133
- # ios.close_write -> nil
134
- #
135
- # Closes the write end of a duplexed object or the whole object if the
136
- # object is write-only.
137
- #
138
- # Raises IOError if #closed? returns +true+. Raises IOError for duplexed
139
- # objects if called more than once. Raises IOError for non-duplexed objects
140
- # if #readable? returns +true+.
141
- def close_write
142
- raise IOError, 'closed stream' if closed?
143
- if __io_like__closed_write? || ! duplexed? && readable? then
144
- raise IOError, 'closing non-duplex IO for reading'
145
- end
146
- if duplexed? then
147
- flush
148
- __io_like__close_write
149
- else
150
- close
151
- end
152
- nil
153
- end
98
+ @pid = nil == pid ? pid : ensure_integer(pid)
154
99
 
155
- # call-seq:
156
- # ios.closed? -> true or false
157
- #
158
- # Returns +true+ if this object is closed or otherwise unusable for read and
159
- # write operations.
160
- def closed?
161
- (__io_like__closed_read? || ! readable?) &&
162
- (__io_like__closed_write? || ! writable?)
163
- end
100
+ self.sync = sync
164
101
 
165
- # call-seq:
166
- # ios.duplexed? -> true or false
167
- #
168
- # Returns +false+. Override this to return +true+ when creating duplexed
169
- # IO objects.
170
- def duplexed?
171
- false
172
- end
102
+ @skip_duplexed_check = false
103
+ @readable = @writable = nil
104
+ end
173
105
 
174
- # call-seq:
175
- # ios.each_byte { |byte| block } -> ios
176
- #
177
- # Reads each byte (0..255) from the stream using #getc and calls the given
178
- # block once for each byte, passing the byte as an argument.
179
- #
180
- # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
181
- # #unbuffered_read. Therefore, this method always blocks. Aside from that
182
- # exception and the conversion of EOFError results into +nil+ results, this
183
- # method will also raise the same errors and block at the same times as
184
- # #unbuffered_read.
185
- def each_byte
186
- while (byte = getc) do
187
- yield(byte)
188
- end
189
- self
106
+ ##
107
+ # Writes `obj` to the stream using {#write}.
108
+ #
109
+ # @param obj converted to a String using its #to_s method
110
+ #
111
+ # @return [self]
112
+ def <<(obj)
113
+ write(obj)
114
+ self
115
+ end
116
+
117
+ ##
118
+ # Puts the stream into binary mode.
119
+ #
120
+ # Once a stream is in binary mode, it cannot be reset to nonbinary mode.
121
+ # * Newline conversion disabled
122
+ # * Encoding conversion disabled
123
+ # * Content is treated as ASCII-8BIT
124
+ #
125
+ # @return [self]
126
+ #
127
+ # @raise [IOError] if the stream is closed
128
+ def binmode
129
+ assert_open
130
+ @binmode = true
131
+ set_encoding(Encoding::BINARY)
132
+ self
133
+ end
134
+
135
+ ##
136
+ # Returns `true` if the stream is in binary mode and `false` otherwise.
137
+ #
138
+ # @return [Boolean]
139
+ #
140
+ # @raise [IOError] if the stream is closed
141
+ def binmode?
142
+ assert_open
143
+ @binmode
144
+ end
145
+
146
+ if RBVER_LT_3_0
147
+ ##
148
+ # @deprecated Use {#each_byte} instead.
149
+ #
150
+ # @version < Ruby 3.0
151
+ def bytes(&block)
152
+ warn('warning: IO#bytes is deprecated; use #each_byte instead')
153
+ each_byte(&block)
154
+ end
155
+
156
+ ##
157
+ # @deprecated Use {#each_char} instead.
158
+ #
159
+ # @version < Ruby 3.0
160
+ def chars(&block)
161
+ warn('warning: IO#chars is deprecated; use #each_char instead')
162
+ each_char(&block)
163
+ end
164
+ end
165
+
166
+ ##
167
+ # Closes the stream, flushing any buffered data first.
168
+ #
169
+ # This always blocks when buffered data needs to be written, even when the
170
+ # stream is in nonblocking mode.
171
+ #
172
+ # @return [nil]
173
+ def close
174
+ @skip_duplexed_check = true
175
+ super
176
+
177
+ nil
178
+ ensure
179
+ @skip_duplexed_check = false
180
+ end
181
+
182
+ ##
183
+ # Closes the read side of duplexed streams and closes the entire stream for
184
+ # read-only, non-duplexed streams.
185
+ #
186
+ # @return [nil]
187
+ #
188
+ # @raise [IOError] if the stream is non-duplexed and writable
189
+ def close_read
190
+ return if closed_read?
191
+
192
+ if ! @skip_duplexed_check && ! duplexed? && writable?
193
+ raise IOError, 'closing non-duplex IO for reading'
190
194
  end
191
195
 
192
- # call-seq:
193
- # ios.each_line(sep_string = $/) { |line| block } -> ios
194
- # ios.each(sep_string = $/) { |line| block } -> ios
195
- #
196
- # Reads each line from the stream using #gets and calls the given block once
197
- # for each line, passing the line as an argument.
198
- #
199
- # <b>NOTE:</b> When _sep_string_ is not +nil+, this method ignores
200
- # Errno::EAGAIN and Errno::EINTR raised by #unbuffered_read. Therefore,
201
- # this method always blocks. Aside from that exception and the conversion
202
- # of EOFError results into +nil+ results, this method will also raise the
203
- # same errors and block at the same times as #unbuffered_read.
204
- def each_line(sep_string = $/)
205
- while (line = gets(sep_string)) do
206
- yield(line)
207
- end
208
- self
196
+ super
197
+
198
+ nil
199
+ end
200
+
201
+ ##
202
+ # Closes the write side of duplexed streams and closes the entire stream for
203
+ # write-only, non-duplexed streams.
204
+ #
205
+ # @return [nil]
206
+ #
207
+ # @raise [IOError] if the stream is non-duplexed and readable
208
+ def close_write
209
+ return if closed_write?
210
+
211
+ if ! @skip_duplexed_check && ! duplexed? && readable?
212
+ raise IOError, 'closing non-duplex IO for writing'
209
213
  end
210
- alias :each :each_line
211
-
212
- # call-seq:
213
- # ios.eof? -> true or false
214
- # ios.eof -> true or false
215
- #
216
- # Returns +true+ if there is no more data to read.
217
- #
218
- # This works by using #getc to fetch the next character and using #ungetc to
219
- # put the character back if one was fetched. It may be a good idea to
220
- # replace this implementation in derivative classes.
221
- #
222
- # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
223
- # #unbuffered_read. Therefore, this method always blocks. Aside from that
224
- # exception and the conversion of EOFError results into +nil+ results, this
225
- # method will also raise the same errors and block at the same times as
226
- # #unbuffered_read.
227
- def eof?
228
- if (char = getc) then
229
- ungetc(char)
230
- return false
231
- end
232
- true
214
+
215
+ flush if writable?
216
+ super
217
+
218
+ nil
219
+ end
220
+
221
+ if RBVER_LT_3_0
222
+ ##
223
+ # @deprecated Use {#each_codepoint} instead.
224
+ #
225
+ # @version < Ruby 3.0
226
+ def codepoints(&block)
227
+ warn('warning: IO#codepoints is deprecated; use #each_codepoint instead')
228
+ each_codepoint(&block)
229
+ end
230
+ end
231
+
232
+ ##
233
+ # @overload each_byte
234
+ # @return [Enumerator] an enumerator that iterates over each byte in the
235
+ # stream
236
+ #
237
+ # @overload each_byte
238
+ # Iterates over each byte in the stream, yielding each byte to the given
239
+ # block.
240
+ #
241
+ # @yieldparam byte [Integer] the next byte from the stream
242
+ #
243
+ # @return [self]
244
+ #
245
+ # @raise [IOError] if the stream is not open for reading
246
+ def each_byte
247
+ return to_enum(:each_byte) unless block_given?
248
+
249
+ while (byte = getbyte) do
250
+ yield(byte)
233
251
  end
234
- alias :eof :eof?
235
-
236
- # call-seq:
237
- # ios.fcntl
238
- #
239
- # Raises NotImplementedError.
240
- def fcntl(*args)
241
- raise NotImplementedError, 'not implemented'
252
+ self
253
+ end
254
+
255
+ ##
256
+ # @overload each_char
257
+ # @return [Enumerator] an enumerator that iterates over each character in
258
+ # the stream
259
+ #
260
+ # @overload each_char
261
+ # Iterates over each character in the stream, yielding each character to the
262
+ # given block.
263
+ #
264
+ # @yieldparam char [String] the next character from the stream
265
+ #
266
+ # @return [self]
267
+ #
268
+ # @raise [IOError] if the stream is not open for reading
269
+ def each_char
270
+ return to_enum(:each_char) unless block_given?
271
+
272
+ while char = getc do
273
+ yield(char)
242
274
  end
275
+ self
276
+ end
277
+
278
+ ##
279
+ # @overload each_codepoint
280
+ # @return [Enumerator] an enumerator that iterates over each Integer ordinal
281
+ # of each character in the stream
282
+ #
283
+ # @overload each_codepoint
284
+ # Iterates over each Integer ordinal of each character in the stream,
285
+ # yielding each ordinal to the given block.
286
+ #
287
+ # @yieldparam codepoint [Integer] the Integer ordinal of the next character
288
+ # from the stream
289
+ #
290
+ # @return [self]
291
+ #
292
+ # @raise [IOError] if the stream is not open for reading
293
+ def each_codepoint
294
+ return to_enum(:each_codepoint) unless block_given?
295
+
296
+ each_char { |c| yield(c.codepoints[0]) }
297
+ self
298
+ end
243
299
 
244
- # call-seq:
245
- # ios.fileno -> nil
246
- #
247
- # Returns +nil+. Just for compatibility with IO.
248
- def fileno
249
- nil
300
+ ##
301
+ # @overload each_line(separator = $/, limit = nil, chomp: false)
302
+ # @param separator [String, nil] a non-empty String that separates each
303
+ # line, an empty String that equates to 2 or more successive newlines as
304
+ # the separator, or `nil` to indicate reading all remaining data
305
+ # @param limit [Integer, nil] an Integer limiting the number of bytes
306
+ # returned in each line or `nil` to indicate no limit
307
+ # @param chomp [Boolean] when `true` trailing newlines and carriage returns
308
+ # will be removed from each line
309
+ #
310
+ # @return [Enumerator] an enumerator that iterates over each line in the
311
+ # stream
312
+ #
313
+ # @overload each_line(limit, chomp: false)
314
+ # @param limit [Integer] an Integer limiting the number of bytes returned in
315
+ # each line or `nil` to indicate no limit
316
+ # @param chomp [Boolean] when `true` trailing newlines and carriage returns
317
+ # will be removed from each line
318
+ #
319
+ # @return [Enumerator] an enumerator that iterates over each line in the
320
+ # stream where each line is separated by `$/`
321
+ #
322
+ # @overload each_line(separator = $/, limit = nil, chomp: false)
323
+ # Iterates over each line in the stream, yielding each line to the given
324
+ # block.
325
+ #
326
+ # @param separator [String, nil] a non-empty String that separates each
327
+ # line, an empty String that equates to 2 or more successive newlines as
328
+ # the separator, or `nil` to indicate reading all remaining data
329
+ # @param limit [Integer, nil] an Integer limiting the number of bytes
330
+ # returned in each line or `nil` to indicate no limit
331
+ # @param chomp [Boolean] when `true` trailing newlines and carriage returns
332
+ # will be removed from each line
333
+ #
334
+ # @yieldparam line [String] a line from the stream
335
+ #
336
+ # @return [self]
337
+ #
338
+ # @overload each_line(limit, chomp: false)
339
+ # Iterates over each line in the stream, where each line is separated by
340
+ # `$/`, yielding each line to the given block.
341
+ #
342
+ # @param limit [Integer] an Integer limiting the number of bytes returned in
343
+ # each line or `nil` to indicate no limit
344
+ # @param chomp [Boolean] when `true` trailing newlines and carriage returns
345
+ # will be removed from each line
346
+ #
347
+ # @yieldparam line [String] a line from the stream
348
+ #
349
+ # @return [self]
350
+ #
351
+ # @raise [IOError] if the stream is not open for reading
352
+ def each_line(*args, **opts)
353
+ unless block_given?
354
+ return to_enum(:each_line, *args, **opts)
250
355
  end
251
356
 
252
- # call-seq:
253
- # ios.fill_size -> integer
254
- #
255
- # Returns the number of bytes to read as a block whenever the internal
256
- # buffer needs to be refilled. Unless set explicitly via #fill_size=, this
257
- # defaults to 4096.
258
- #
259
- # Raises IOError if #closed? returns +true+. Raises IOError if the
260
- # stream is not opened for reading.
261
- def fill_size
262
- raise IOError, 'closed stream' if closed?
263
- raise IOError, 'not opened for reading' unless readable?
264
-
265
- @__io_like__fill_size ||= 4096
357
+ sep_string, limit = parse_readline_args(*args)
358
+ raise ArgumentError, 'invalid limit: 0 for each_line' if limit == 0
359
+
360
+ while (line = gets(sep_string, limit, **opts)) do
361
+ yield(line)
266
362
  end
363
+ self
364
+ end
365
+ alias_method :each, :each_line
267
366
 
268
- # call-seq:
269
- # ios.fill_size = integer -> integer
270
- #
271
- # Sets the number of bytes to read as a block whenever the internal read
272
- # buffer needs to be refilled. The new value must be a number greater than
273
- # or equal to 0. Setting this to 0 effectively disables buffering.
274
- #
275
- # Raises IOError if #closed? returns +true+. Raises IOError if the
276
- # stream is not opened for reading.
277
- def fill_size=(fill_size)
278
- raise IOError, 'closed stream' if closed?
279
- raise IOError, 'not opened for reading' unless readable?
280
-
281
- unless fill_size >= 0 then
282
- raise ArgumentError, "non-positive fill_size #{fill_size} given"
283
- end
284
- @__io_like__fill_size = fill_size
367
+ ##
368
+ # Returns `true` if the end of the stream has been reached and `false`
369
+ # otherwise.
370
+ #
371
+ # @note This method will block if reading the stream blocks.
372
+ # @note This method relies on buffered operations, so using it in conjuction
373
+ # with {#sysread} will be complicated at best.
374
+ #
375
+ # @return [Boolean]
376
+ #
377
+ # @raise [IOError] if the stream is not open for reading
378
+ def eof?
379
+ if byte = getbyte
380
+ ungetbyte(byte)
381
+ return false
285
382
  end
383
+ true
384
+ end
385
+ alias_method :eof, :eof?
286
386
 
287
- # call-seq:
288
- # ios.flush -> ios
289
- #
290
- # Flushes the internal write buffer to the underlying data stream.
291
- #
292
- # Regardless of the blocking status of the data stream or interruptions
293
- # during writing, this method will block until either all the data is
294
- # flushed or until an error is raised.
295
- #
296
- # Raises IOError if #closed? returns +true+. Raises IOError unless
297
- # #writable? returns +true+.
298
- #
299
- # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
300
- # #unbuffered_write. Therefore, this method always blocks if unable to
301
- # flush the internal write buffer. Aside from that exception, this
302
- # method will also raise the same errors and block at the same times as
303
- # #unbuffered_write.
304
- def flush
305
- begin
306
- __io_like__buffered_flush
307
- rescue Errno::EAGAIN, Errno::EINTR
308
- retry if write_ready?
309
- end
310
- self
387
+ ##
388
+ # Returns the external encoding of the stream, if any.
389
+ #
390
+ # @return [Encoding] the Encoding object that represents the encoding of the
391
+ # stream
392
+ # @return [nil] if the stream is writable and no encoding is specified
393
+ #
394
+ # @raise [IOError] if the stream is closed and Ruby version is less than 3.1
395
+ def external_encoding
396
+ assert_open if RBVER_LT_3_1
397
+
398
+ encoding = delegate.character_io.external_encoding
399
+ return encoding if encoding || writable?
400
+
401
+ Encoding::default_external
402
+ end
403
+
404
+ ##
405
+ # Flushes the internal write buffer to the underlying stream.
406
+ #
407
+ # Regardless of the blocking status of the stream or interruptions during
408
+ # writing, this method will block until either all the data is flushed or
409
+ # until an error is raised.
410
+ #
411
+ # @return [self]
412
+ #
413
+ # @raise [IOError] if the stream is not open for writing
414
+ def flush
415
+ assert_open
416
+
417
+ delegate_w.buffered_io.flush
418
+
419
+ self
420
+ end
421
+
422
+ ##
423
+ # Returns the next byte from the stream.
424
+ #
425
+ # @return [Integer] the next byte from the stream
426
+ # @return [nil] if the end of the stream has been reached
427
+ #
428
+ # @raise [IOError] if the stream is not open for reading
429
+ def getbyte
430
+ readbyte
431
+ rescue EOFError
432
+ return nil
433
+ end
434
+
435
+ ##
436
+ # Returns the next character from the stream.
437
+ #
438
+ # @return [String] the next character from the stream
439
+ # @return [nil] if the end of the stream has been reached
440
+ #
441
+ # @raise [IOError] if the stream is not open for reading
442
+ def getc
443
+ readchar
444
+ rescue EOFError
445
+ nil
446
+ end
447
+
448
+ ##
449
+ # Returns the next line from the stream.
450
+ #
451
+ # @overload gets(separator = $/, limit = nil, chomp: false)
452
+ #
453
+ # @param separator [String, nil] a non-empty String that separates each
454
+ # line, an empty String that equates to 2 or more successive newlines as
455
+ # the separator, or `nil` to indicate reading all remaining data
456
+ # @param limit [Integer, nil] an Integer limiting the number of bytes
457
+ # returned in each line or `nil` to indicate no limit
458
+ # @param chomp [Boolean] when `true` trailing newlines and carriage returns
459
+ # will be removed from each line
460
+ #
461
+ # @return [String] the next line in the stream
462
+ # @return [nil] if the end of the stream has been reached
463
+ #
464
+ # @overload gets(limit, chomp: false)
465
+ #
466
+ # @param limit [Integer] an Integer limiting the number of bytes returned in
467
+ # each line or `nil` to indicate no limit
468
+ # @param chomp [Boolean] when `true` trailing newlines and carriage returns
469
+ # will be removed from each line
470
+ #
471
+ # @return [String] the next line in the stream where the separator is `$/`
472
+ # @return [nil] if the end of the stream has been reached
473
+ #
474
+ # @raise [IOError] if the stream is not open for reading
475
+ def gets(*args, **opts)
476
+ readline(*args, **opts)
477
+ rescue EOFError
478
+ # NOTE:
479
+ # Through Ruby 3.3, assigning to $_ has no effect outside of a method that
480
+ # does it. This assignment is kept in case that ever changes.
481
+ $_ = nil
482
+ nil
483
+ end
484
+
485
+ ##
486
+ # Returns the internal encoding of the stream, if any.
487
+ #
488
+ # @return [Encoding] the Encoding object that represents the encoding of the
489
+ # internal string conversion
490
+ # @return [nil] if no encoding is specified
491
+ #
492
+ # @raise [IOError] if the stream is closed and Ruby version is less than 3.1
493
+ def internal_encoding
494
+ assert_open if RBVER_LT_3_1
495
+ delegate.character_io.internal_encoding
496
+ end
497
+
498
+ ##
499
+ # For compatibility with `IO`.
500
+ alias_method :isatty, :tty?
501
+
502
+ ##
503
+ # Returns the current line number of the stream.
504
+ #
505
+ # More accurately the number of times {#gets} is called on the stream and
506
+ # returns a non-`nil` result, either explicitly or implicitly via methods such
507
+ # as {#each_line}, {#readline}, {#readlines}, etc. is returned. This may
508
+ # differ from the number of lines if `$/` is changed from the default or if
509
+ # {#gets} is called with a different separator.
510
+ #
511
+ # @return [Integer] the current line number of the stream
512
+ #
513
+ # @raise [IOError] if the stream is not open for reading
514
+ def lineno
515
+ assert_readable
516
+
517
+ @lineno ||= 0
518
+ end
519
+
520
+ ##
521
+ # Sets the current line number of the stream to the given value. `$.` is
522
+ # updated by the _next_ call to {#gets}. If the object given is not an
523
+ # Integer, it is converted to one using the `Integer` method.
524
+ #
525
+ # @return [Integer] the current line number of the stream
526
+ #
527
+ # @raise [IOError] if the stream is not open for reading
528
+ def lineno=(integer)
529
+ assert_readable
530
+
531
+ @lineno = ensure_integer(integer)
532
+ end
533
+
534
+ if RBVER_LT_3_0
535
+ ##
536
+ # @deprecated Use {#each_line} instead.
537
+ #
538
+ # @version < Ruby 3.0
539
+ def lines(*args, &block)
540
+ warn('warning: IO#lines is deprecated; use #each_line instead')
541
+ each_line(*args, &block)
542
+ end
543
+ end
544
+
545
+ ##
546
+ # @return [Integer] the number of bytes that can be read without blocking or
547
+ # `0` if unknown
548
+ #
549
+ # @raise [IOError] if the stream is not open for reading
550
+ def nread
551
+ assert_readable
552
+
553
+ unless delegate_r.character_io.buffer_empty?
554
+ raise IOError, 'byte oriented read for character buffered IO'
311
555
  end
556
+ delegate_r.nread
557
+ end
558
+
559
+ ##
560
+ # Returns the process ID of a child process associated with this stream, if
561
+ # any.
562
+ #
563
+ # @return [Integer] a process ID
564
+ # @return [nil] if there is no associated child process
565
+ #
566
+ # @raise [IOError] if the stream is closed
567
+ def pid
568
+ assert_open
569
+ return @pid unless nil == @pid
570
+ super
571
+ end
572
+
573
+ ##
574
+ # @note This method will block if writing the stream blocks and there is data
575
+ # in the write buffer.
576
+ #
577
+ # @return [Integer] the current byte offset of the stream
578
+ #
579
+ # @raise [IOError] if the stream is closed
580
+ # @raise [Errno::ESPIPE] if the stream is not seekable
581
+ def pos
582
+ assert_open
583
+
584
+ flush
585
+ delegate.seek(0, IO::SEEK_CUR)
586
+ end
587
+ alias_method :tell, :pos
588
+
589
+ ##
590
+ # Sets the position of the stream to the given byte offset.
591
+ #
592
+ # @param position [Integer] the byte offset to which the stream will be set
593
+ #
594
+ # @return [Integer] the given byte offset
595
+ #
596
+ # @raise [IOError] if the stream is closed
597
+ # @raise [Errno::ESPIPE] if the stream is not seekable
598
+ def pos=(position)
599
+ seek(position, IO::SEEK_SET)
600
+ position
601
+ end
602
+
603
+ ##
604
+ # Reads at most `maxlen` bytes from the stream starting at `offset` without
605
+ # modifying the read position in the stream.
606
+ #
607
+ # @param maxlen [Integer] the maximum number of bytes to read
608
+ # @param offset [Integer] the offset from the beginning of the stream at which
609
+ # to begin reading
610
+ # @param buffer [String] if provided, a buffer into which the bytes should be
611
+ # placed
612
+ #
613
+ # @return [String] a new String containing the bytes read if `buffer` is `nil`
614
+ # or `buffer` if provided
615
+ #
616
+ # @raise [EOFError] when reading at the end of the stream
617
+ # @raise [IOError] if the stream is not readable
618
+ def pread(maxlen, offset, buffer = nil)
619
+ maxlen = ensure_integer(maxlen)
620
+ raise ArgumentError, 'negative string size (or size too big)' if maxlen < 0
621
+ buffer = nil == buffer ? ''.b : ensure_string(buffer)
622
+
623
+ return buffer if maxlen == 0
312
624
 
313
- # call-seq:
314
- # ios.flush_size -> integer
315
- #
316
- # Returns the number of bytes at which the internal write buffer is flushed
317
- # automatically to the data stream. Unless set explicitly via #flush_size=,
318
- # this defaults to 4096.
319
- #
320
- # Raises IOError if #closed? returns +true+. Raises IOError unless
321
- # #writable? returns +true+.
322
- def flush_size
323
- raise IOError, 'closed stream' if closed?
324
- raise IOError, 'not opened for writing' unless writable?
325
-
326
- @__io_like__flush_size ||= 4096
625
+ offset = ensure_integer(offset)
626
+ raise Errno::EINVAL if offset < 0
627
+
628
+ assert_readable
629
+
630
+ if maxlen > buffer.bytesize
631
+ buffer << "\0" * (maxlen - buffer.bytesize)
327
632
  end
633
+ bytes_read = delegate_r.pread(maxlen, offset, buffer: buffer)
634
+ buffer.slice!(bytes_read..-1)
635
+
636
+ buffer
637
+ end
328
638
 
329
- # call-seq:
330
- # ios.flush_size = integer -> integer
331
- #
332
- # Sets the number of bytes at which the internal write buffer is flushed
333
- # automatically to the data stream. The new value must be a number greater
334
- # than or equal to 0. Setting this to 0 effectively disables buffering.
335
- #
336
- # Raises IOError if #closed? returns +true+. Raises IOError unless
337
- # #writable? returns +true+.
338
- def flush_size=(flush_size)
339
- raise IOError, 'closed stream' if closed?
340
- raise IOError, 'not opened for writing' unless writable?
341
-
342
- unless flush_size >= 0 then
343
- raise ArgumentError, "non-positive flush_size #{flush_size} given"
639
+ ##
640
+ # Writes the given object(s), if any, to the stream using {#write}.
641
+ #
642
+ # If no objects are given, `$_` is written. The field separator (`$,`) is
643
+ # written between successive objects if it is not `nil`. The output record
644
+ # separator (`$\`) is written after all other data if it is not `nil`.
645
+ #
646
+ # @param args [Array<Object>] zero or more objects to write to the stream
647
+ #
648
+ # @return [nil]
649
+ #
650
+ # @raise [IOError] if the stream is not open for writing
651
+ def print(*args)
652
+ # NOTE:
653
+ # Through Ruby 3.1, $_ is always nil on entry to a Ruby method. This
654
+ # assignment is kept in case that ever changes.
655
+ args << $_ if args.empty?
656
+ first_arg = true
657
+ args.each do |arg|
658
+ # Write a field separator before writing each argument after the first
659
+ # one unless no field separator is specified.
660
+ if first_arg
661
+ first_arg = false
662
+ else
663
+ write($,)
344
664
  end
345
- @__io_like__flush_size = flush_size
346
- end
347
665
 
348
- # call-seq:
349
- # ios.getc -> nil or integer
350
- #
351
- # Calls #readchar and either returns the result or +nil+ if #readchar raises
352
- # EOFError.
353
- #
354
- # Raises IOError if #closed? returns +true+. Raises IOError unless
355
- # #readable? returns +true+. Raises all errors raised by #unbuffered_read
356
- # except for EOFError.
357
- #
358
- # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
359
- # #unbuffered_read. Therefore, this method always blocks. Aside from that
360
- # exception and the conversion of EOFError results into +nil+ results, this
361
- # method will also raise the same errors and block at the same times as
362
- # #unbuffered_read.
363
- def getc
364
- readchar
365
- rescue EOFError
366
- nil
666
+ write(arg)
367
667
  end
368
668
 
369
- # call-seq:
370
- # ios.gets(sep_string = $/) -> nil or string
371
- #
372
- # Calls #readline with _sep_string_ as an argument and either returns the
373
- # result or +nil+ if #readline raises EOFError. If #readline returns some
374
- # data, <tt>$.</tt> is set to the value of #lineno.
375
- #
376
- # <b>NOTE:</b> Due to limitations of MRI up to version 1.9.x when running
377
- # managed (Ruby) code, this method fails to set <tt>$_</tt> to the returned
378
- # data; however, other implementations may allow it.
379
- #
380
- # Raises IOError if #closed? returns +true+. Raises IOError unless
381
- # #readable? returns +true+. Raises all errors raised by #unbuffered_read
382
- # except for EOFError.
383
- #
384
- # <b>NOTE:</b> When _sep_string_ is not +nil+, this method ignores
385
- # Errno::EAGAIN and Errno::EINTR raised by #unbuffered_read. Therefore,
386
- # this method will always block in that case. Aside from that exception,
387
- # this method will raise the same errors and block at the same times as
388
- # #unbuffered_read.
389
- def gets(sep_string = $/)
390
- # Set the last read line in the global.
391
- $_ = readline(sep_string)
392
- # Set the last line number in the global.
393
- $. = lineno
394
- # Return the last read line.
395
- $_
396
- rescue EOFError
397
- nil
669
+ # Write the output record separator if one is specified.
670
+ write($\) if $\
671
+ nil
672
+ end
673
+
674
+ ##
675
+ # Writes the String returned by calling `Kernel.sprintf` using the given
676
+ # arguments.
677
+ #
678
+ # @param args [Array] arguments to pass to `Kernel.sprintf`
679
+ #
680
+ # @return [nil]
681
+ #
682
+ # @raise [IOError] if the stream is not open for writing
683
+ def printf(*args)
684
+ write(sprintf(*args))
685
+ nil
686
+ end
687
+
688
+ ##
689
+ # If `obj` is a String, write the first character; otherwise, convert `obj` to
690
+ # an Integer using the `Integer` method and write the low order byte.
691
+ #
692
+ # @param obj [String, Numeric] the character to be written
693
+ #
694
+ # @return [obj] the given parameter
695
+ #
696
+ # @raise [TypeError] if `obj` is not a String nor convertable to a Numeric
697
+ # type
698
+ # @raise [IOError] if the stream is not open for writing
699
+ def putc(obj)
700
+ char = case obj
701
+ when String
702
+ obj[0]
703
+ else
704
+ [ensure_integer(obj)].pack('V')[0]
705
+ end
706
+ write(char)
707
+ obj
708
+ end
709
+
710
+ ##
711
+ # Writes the given object(s), if any, to the stream.
712
+ #
713
+ # Uses {#write} after converting objects to strings using their `to_s`
714
+ # methods. Unlike {#print}, Array instances are recursively processed. The
715
+ # record separator character (`$\`) is written after each object which does
716
+ # not end with the record separator already.
717
+ #
718
+ # If no objects are given, a single record separator is written.
719
+ #
720
+ # @param args [Array<Object>] zero or more objects to write to the stream
721
+ #
722
+ # @return [nil]
723
+ #
724
+ # @raise [IOError] if the stream is not open for writing
725
+ def puts(*args)
726
+ # Write only the record separator if no arguments are given.
727
+ if args.length == 0
728
+ write(ORS)
729
+ return
398
730
  end
399
731
 
400
- # call-seq:
401
- # ios.isatty -> false
402
- #
403
- # Returns +false+. Just for compatibility with IO.
404
- #
405
- # Raises IOError if #closed? returns +true+.
406
- def isatty
407
- raise IOError, 'closed stream' if closed?
408
- false
732
+ flatten_puts(args)
733
+ nil
734
+ end
735
+
736
+ ##
737
+ # Writes at most `string.to_s.length` bytes to the stream starting at `offset`
738
+ # without modifying the write position in the stream.
739
+ #
740
+ # @param string [String] the bytes to write (encoding assumed to be binary)
741
+ # @param offset [Integer] the offset from the beginning of the stream at which
742
+ # to begin writing
743
+ #
744
+ # @return [Integer] the number of bytes written
745
+ #
746
+ # @raise [IOError] if the stream is not writable
747
+ def pwrite(string, offset)
748
+ string = string.to_s
749
+
750
+ offset = ensure_integer(offset)
751
+ raise Errno::EINVAL if offset < 0
752
+
753
+ assert_writable
754
+
755
+ delegate_w.pwrite(string, offset)
756
+ end
757
+
758
+ ##
759
+ # Reads data from the stream.
760
+ #
761
+ # If `length` is specified as a positive integer, at most `length` bytes are
762
+ # returned. Truncated data will occur if there is insufficient data left to
763
+ # fulfill the request. If the read starts at the end of data, `nil` is
764
+ # returned.
765
+ #
766
+ # If `length` is unspecified or `nil`, an attempt to return all remaining data
767
+ # is made. Partial data will be returned if a low-level error is raised after
768
+ # some data is retrieved. If no data would be returned at all, an empty
769
+ # String is returned.
770
+ #
771
+ # If `buffer` is specified, it will be converted to a String using its
772
+ # `to_str` method if necessary and will be filled with the returned data if
773
+ # any.
774
+ #
775
+ # @param length [Integer] the number of bytes to read
776
+ # @param buffer [String] the location into which data will be stored
777
+ #
778
+ # @return [String] the data read from the stream
779
+ # @return [nil] if `length` is non-zero but no data is left in the stream
780
+ #
781
+ # @raise [ArgumentError] if `length` is less than 0
782
+ # @raise [IOError] if the stream is not open for reading
783
+ def read(length = nil, buffer = nil)
784
+ length = ensure_integer(length) if nil != length
785
+ if nil != length && length < 0
786
+ raise ArgumentError, "negative length #{length} given"
409
787
  end
410
- alias :tty? :isatty
411
-
412
- # call-seq:
413
- # ios.lineno -> integer
414
- #
415
- # Returns the number of times #gets was called and returned non-+nil+ data.
416
- # By default this is the number of lines read, but calling #gets or any of
417
- # the other line-based reading methods with a non-default value for
418
- # _sep_string_ or after changing <tt>$/</tt> will affect this.
419
- #
420
- # Raises IOError if #closed? returns +true+. Raises IOError unless
421
- # #readable? returns +true+.
422
- def lineno
423
- raise IOError, 'closed stream' if closed?
424
- raise IOError, 'not opened for reading' unless readable?
425
- @__io_like__lineno ||= 0
788
+ buffer = ensure_string(buffer) unless nil == buffer
789
+
790
+ assert_readable
791
+
792
+ unless length
793
+ content = begin
794
+ delegate_r.character_io.read_all
795
+ rescue EOFError
796
+ ''.encode(
797
+ internal_encoding ||
798
+ external_encoding ||
799
+ Encoding.default_external
800
+ )
801
+ end
802
+ return content unless buffer
803
+ return buffer.replace(content)
426
804
  end
427
805
 
428
- # call-seq:
429
- # ios.lineno = lineno -> lineno
430
- #
431
- # Sets the current line number to the given value. <tt>$.</tt> is updated
432
- # by the _next_ call to #gets. If the object given is not an integer, it is
433
- # converted to one using its to_int method.
434
- #
435
- # Raises IOError if #closed? returns +true+. Raises IOError unless
436
- # #readable? returns +true+.
437
- def lineno=(integer)
438
- raise IOError, 'closed stream' if closed?
439
- raise IOError, 'not opened for reading' unless readable?
440
- if integer.nil? then
441
- raise TypeError, 'no implicit conversion from nil to integer'
442
- elsif ! integer.respond_to?(:to_int) then
443
- raise TypeError, "can't convert #{integer.class} into Integer"
444
- end
445
- @__io_like__lineno = integer.to_int
806
+ unless delegate_r.character_io.buffer_empty?
807
+ raise IOError, 'byte oriented read for character buffered IO'
446
808
  end
447
809
 
448
- # call-seq:
449
- # ios.path -> nil
450
- #
451
- # Returns +nil+. Just for compatibility with IO.
452
- def path
453
- nil
810
+ content = read_bytes(length)
811
+ if buffer
812
+ orig_encoding = buffer.encoding
813
+ buffer.replace(content)
814
+ buffer.force_encoding(orig_encoding)
815
+ content = buffer
454
816
  end
455
817
 
456
- # call-seq:
457
- # ios.pos = position -> position
458
- #
459
- # Sets the data position to _position_ by calling #seek.
460
- #
461
- # As a side effect, the internal read and write buffers are flushed.
462
- #
463
- # Raises IOError if #closed? returns +true+. Raises Errno::ESPIPE unless
464
- # #seekable? returns +true+.
465
- #
466
- # <b>NOTE:</b> Because this method relies on #unbuffered_seek and
467
- # #unbuffered_write (when the internal write buffer is not empty), it will
468
- # also raise the same errors and block at the same times as those functions.
469
- def pos=(position)
470
- seek(position, IO::SEEK_SET)
471
- position
818
+ return nil if content.empty? && length > 0
819
+ return content
820
+ end
821
+
822
+ ##
823
+ # Reads and returns at most `length` bytes from the stream.
824
+ #
825
+ # If the internal read buffer is **not** empty, only the buffer is used, even
826
+ # if less than `length` bytes are available. If the internal buffer **is**
827
+ # empty, sets non-blocking mode via {#nonblock=} and then reads from the
828
+ # underlying stream.
829
+ #
830
+ # @param length [Integer] the number of bytes to read
831
+ # @param buffer [String] the location in which to store the data
832
+ # @param exception [Boolean] when `true` causes this method to raise
833
+ # exceptions when no data is available; otherwise, symbols are returned
834
+ #
835
+ # @return [String] the data read from the stream
836
+ # @return [:wait_readable, :wait_writable] if `exception` is `false` and no
837
+ # data is available
838
+ # @return [nil] if `exception` is `false` and reading begins at the end of the
839
+ # stream
840
+ #
841
+ # @raise [EOFError] if reading begins at the end of the stream
842
+ # @raise [IOError] if the stream is not open for reading
843
+ # @raise [IO::EWOULDBLOCKWaitReadable, IO::EWOULDBLOCKWaitWritable] if
844
+ # `exception` is `true` and no data is available
845
+ # @raise [Errno::EBADF] if non-blocking mode is not supported
846
+ # @raise [SystemCallError] if there are low level errors
847
+ def read_nonblock(length, buffer = nil, exception: true)
848
+ length = ensure_integer(length)
849
+ raise ArgumentError, 'length must be at least 0' if length < 0
850
+ buffer = ensure_string(buffer) if nil != buffer
851
+
852
+ assert_readable
853
+
854
+ if RBVER_LT_3_0_4 && length == 0
855
+ return (buffer || ''.b)
472
856
  end
473
857
 
474
- # call-seq:
475
- # ios.pos -> integer
476
- #
477
- # Returns the current offest of ios.
478
- #
479
- # Raises IOError if #closed? returns +true+. Raises Errno::ESPIPE unless
480
- # #seekable? returns +true+.
481
- #
482
- # As a side effect, the internal write buffer is flushed unless this is
483
- # a writable, non-duplexed object. This is for compatibility with the
484
- # behavior of IO#pos.
485
- #
486
- # <b>NOTE:</b> Because this method relies on #unbuffered_seek and
487
- # #unbuffered_write (when the internal write buffer is not empty), it will
488
- # also raise the same errors and block at the same times as those functions.
489
- def pos
490
- # Flush the internal write buffer for writable, non-duplexed objects.
491
- __io_like__buffered_flush if writable? && ! duplexed?
492
- __io_like__buffered_seek(0, IO::SEEK_CUR)
858
+ unless delegate_r.character_io.buffer_empty?
859
+ raise IOError, 'byte oriented read for character buffered IO'
493
860
  end
494
- alias :tell :pos
495
-
496
- # call-seq:
497
- # ios.print([obj, ...]) -> nil
498
- #
499
- # Writes the given object(s), if any, to the stream using #write after
500
- # converting them to strings by calling their _to_s_ methods. If no
501
- # objects are given, <tt>$_</tt> is used. The field separator (<tt>$,</tt>)
502
- # is written between successive objects if it is not +nil+. The output
503
- # record separator (<tt>$\\</tt>) is written after all other data if it is
504
- # not nil.
505
- #
506
- # Raises IOError if #closed? returns +true+. Raises IOError unless
507
- # #writable? returns +true+.
508
- #
509
- # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
510
- # #unbuffered_write. Therefore, this method always blocks if unable to
511
- # immediately write +[obj, ...]+ completely. Aside from that exception,
512
- # this method will also raise the same errors and block at the same times as
513
- # #unbuffered_write.
514
- def print(*args)
515
- args << $_ if args.empty?
516
- first_arg = true
517
- args.each do |arg|
518
- # Write a field separator before writing each argument after the first
519
- # one unless no field separator is specified.
520
- if first_arg then
521
- first_arg = false
522
- elsif ! $,.nil? then
523
- write($,)
524
- end
525
861
 
526
- # If the argument is nil, write 'nil'; otherwise, write the stringified
527
- # form of the argument.
528
- if arg.nil? then
529
- write('nil')
530
- else
531
- write(arg)
532
- end
862
+ result = ensure_buffer(length, buffer) do |binary_buffer|
863
+ unless delegate_r.buffered_io.read_buffer_empty?
864
+ next delegate_r.read(length, buffer: binary_buffer)
533
865
  end
534
866
 
535
- # Write the output record separator if one is specified.
536
- write($\) unless $\.nil?
537
- nil
867
+ self.nonblock = true
868
+ delegate_r.concrete_io.read(length, buffer: binary_buffer)
538
869
  end
539
870
 
540
- # call-seq:
541
- # ios.printf(format_string [, obj, ...]) -> nil
542
- #
543
- # Writes the String returned by calling Kernel.sprintf using the given
544
- # arguments.
545
- #
546
- # Raises IOError if #closed? returns +true+. Raises IOError unless
547
- # #writable? returns +true+.
548
- #
549
- # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
550
- # #unbuffered_write. Therefore, this method always blocks if unable to
551
- # immediately write its arguments completely. Aside from that exception,
552
- # this method will also raise the same errors and block at the same times as
553
- # #unbuffered_write.
554
- def printf(*args)
555
- write(sprintf(*args))
556
- nil
871
+ case result
872
+ when String
873
+ # This means that a buffer was not given and that the delegate returned a
874
+ # buffer with the content.
875
+ return result
876
+ when Integer
877
+ # This means that a buffer was given and that the content is in the
878
+ # buffer.
879
+ return buffer
880
+ else
881
+ return nonblock_response(result, exception)
557
882
  end
883
+ rescue EOFError
884
+ raise if exception
885
+ return nil
886
+ end
558
887
 
559
- # call-seq:
560
- # ios.putc(obj) -> obj
561
- #
562
- # If _obj_ is a String, write the first byte; otherwise, convert _obj_ to a
563
- # integer using its _to_int_ method and write the low order byte.
564
- #
565
- # Raises IOError if #closed? returns +true+. Raises IOError unless
566
- # #writable? returns +true+.
567
- #
568
- # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
569
- # #unbuffered_write. Therefore, this method always blocks if unable to
570
- # immediately write _obj_ completely. Aside from that exception, this
571
- # method will also raise the same errors and block at the same times as
572
- # #unbuffered_write.
573
- def putc(obj)
574
- char = case obj
575
- when String
576
- obj[0].chr
577
- else
578
- [obj.to_int].pack('V')[0].chr
579
- end
580
- write(char)
581
- obj
582
- end
888
+ ##
889
+ # @return [Integer] the next 8-bit byte (0..255) from the stream
890
+ #
891
+ # @raise [EOFError] if reading begins at the end of the stream
892
+ # @raise [IOError] if the stream is not open for reading
893
+ def readbyte
894
+ assert_readable
583
895
 
584
- # call-seq:
585
- # ios.puts([obj, ...]) -> nil
586
- #
587
- # Writes the given object(s), if any, to the stream using #write after
588
- # converting them to strings using their _to_s_ methods. Unlike #print,
589
- # Array instances are recursively processed. A record separator character
590
- # is written after each object which does not end with the record separator
591
- # already. If no objects are given, a single record separator is written.
592
- #
593
- # Raises IOError if #closed? returns +true+. Raises IOError unless
594
- # #writable? returns +true+.
595
- #
596
- # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
597
- # #unbuffered_write. Therefore, this method always blocks if unable to
598
- # immediately write +[obj, ...]+ completely. Aside from that exception,
599
- # this method will also raise the same errors and block at the same times as
600
- # #unbuffered_write.
601
- #
602
- # <b>NOTE:</b> In order to be compatible with IO#puts, the record separator
603
- # is currently hardcoded to be a single newline (<tt>"\n"</tt>) even though
604
- # the documentation implies that the output record separator (<tt>$\\</tt>)
605
- # should be used.
606
- def puts(*args)
607
- # Set the output record separator such that this method is compatible with
608
- # IO#puts.
609
- ors = "\n"
610
-
611
- # Write only the record separator if no arguments are given.
612
- if args.length == 0 then
613
- write(ors)
614
- return
615
- end
896
+ unless delegate_r.character_io.buffer_empty?
897
+ raise IOError, 'byte oriented read for character buffered IO'
898
+ end
899
+ byte = delegate_r.read(1)
900
+ byte[0].ord
901
+ end
616
902
 
617
- # Write each argument followed by the record separator. Recursively
618
- # process arguments which are Array instances.
619
- args.each do |arg|
620
- line = arg.nil? ?
621
- 'nil' :
622
- arg.kind_of?(Array) ?
623
- __io_like__array_join(arg, ors) :
624
- arg.to_s
625
- line += ors if line.index(ors, -ors.length).nil?
626
- write(line)
627
- end
903
+ ##
904
+ # @return [String] the next character from the stream
905
+ #
906
+ # @raise [EOFError] if reading begins at the end of the stream
907
+ # @raise [IOError] if the stream is not open for reading
908
+ def readchar
909
+ assert_readable
628
910
 
629
- nil
630
- end
911
+ delegate_r.character_io.read_char
912
+ end
631
913
 
632
- # call-seq:
633
- # ios.read([length[, buffer]]) -> nil, buffer, or string
634
- #
635
- # If _length_ is specified and is a positive integer, at most length bytes
636
- # are returned. Truncated data will occur if there is insufficient data
637
- # left to fulfill the request. If the read starts at the end of data, +nil+
638
- # is returned.
639
- #
640
- # If _length_ is unspecified or +nil+, an attempt to return all remaining
641
- # data is made. Partial data will be returned if a low-level error is
642
- # raised after some data is retrieved. If no data would be returned at all,
643
- # an empty String is returned.
644
- #
645
- # If _buffer_ is specified, it will be converted to a String using its
646
- # +to_str+ method if necessary and will be filled with the returned data if
647
- # any.
648
- #
649
- # Raises IOError if #closed? returns +true+. Raises IOError unless
650
- # #readable? returns +true+.
651
- #
652
- # <b>NOTE:</b> Because this method relies on #unbuffered_read, it will also
653
- # raise the same errors and block at the same times as that function.
654
- def read(length = nil, buffer = nil)
655
- # Check the validity of the method arguments.
656
- unless length.nil? || length >= 0 then
657
- raise ArgumentError, "negative length #{length} given"
658
- end
659
- buffer = buffer.nil? ? '' : buffer.to_str
660
- buffer.slice!(0..-1) unless buffer.empty?
661
-
662
- if length.nil? then
663
- # Read and return everything.
664
- begin
665
- loop do
666
- buffer << __io_like__buffered_read(4096)
667
- end
668
- rescue EOFError
669
- # Ignore this.
670
- rescue SystemCallError
671
- # Reraise the error if there is nothing to return.
672
- raise if buffer.empty?
673
- end
674
- else
675
- # Read and return up to length bytes.
676
- begin
677
- buffer << __io_like__buffered_read(length)
678
- rescue EOFError
679
- # Return nil to the caller at end of file when requesting a specific
680
- # amount of data.
681
- return nil
682
- end
914
+ ##
915
+ # Returns the next line from the stream.
916
+ #
917
+ # @overload readline(separator = $/, limit = nil, chomp: false)
918
+ #
919
+ # @param separator [String, nil] a non-empty String that separates each
920
+ # line, an empty String that equates to 2 or more successive newlines as
921
+ # the separator, or `nil` to indicate reading all remaining data
922
+ # @param limit [Integer, nil] an Integer limiting the number of bytes
923
+ # returned in each line or `nil` to indicate no limit
924
+ # @param chomp [Boolean] when `true` trailing newlines and carriage returns
925
+ # will be removed from each line
926
+ #
927
+ # @return [String] the next line in the stream
928
+ #
929
+ # @overload readline(limit, chomp: false)
930
+ #
931
+ # @param limit [Integer] an Integer limiting the number of bytes returned in
932
+ # each line or `nil` to indicate no limit
933
+ # @param chomp [Boolean] when `true` trailing newlines and carriage returns
934
+ # will be removed from each line
935
+ #
936
+ # @return [String] the next line in the stream where the separator is `$/`
937
+ #
938
+ # @raise [EOFError] if reading begins at the end of the stream
939
+ # @raise [IOError] if the stream is not open for reading
940
+ def readline(*args, **opts)
941
+ separator, limit = parse_readline_args(*args)
942
+ chomp = opts.fetch(:chomp, false)
943
+
944
+ assert_readable
945
+
946
+ discard_newlines = false
947
+ encoding =
948
+ internal_encoding || external_encoding || Encoding.default_external
949
+
950
+ if separator
951
+ # Reading with a record separator is performed using bytes rather than
952
+ # characters, so ensure that the separator is correctly encoded to make
953
+ # this possible.
954
+ if separator.empty?
955
+ # Read by paragraph is requested. The separator is 2 newline characters
956
+ # in the encoding of the character reader.
957
+ discard_newlines = true
958
+ separator = "\n\n"
959
+ separator = separator.b if RBVER_LT_3_4
960
+ elsif $/.equal?(separator)
961
+ # When using the default record separator character, convert it into the
962
+ # encoding of the character reader.
963
+ separator = separator.encode(encoding)
964
+ elsif ! (separator.encoding == encoding ||
965
+ (separator.ascii_only? && encoding.ascii_compatible?))
966
+ # Raise an error when the separator encoding doesn't match the reader
967
+ # encoding and ASCII compatibility is not available.
968
+ #
969
+ # NOTE:
970
+ # We should probably attempt to convert the encoding of the separator
971
+ # into the encoding of the reader before raising this error, but MRI
972
+ # doesn't appear to do that.
973
+ raise ArgumentError,
974
+ 'encoding mismatch: %s IO with %s RS' % [encoding, separator.encoding]
683
975
  end
684
- buffer
685
976
  end
686
977
 
687
- # call-seq:
688
- # ios.read_ready? -> true or false
689
- #
690
- # Returns +true+ when the stream may be read without error, +false+
691
- # otherwise. This method will block until one of the conditions is known.
692
- #
693
- # This default implementation of #read_ready? is a hack which should be able
694
- # to work for both real IO objects and IO-like objects; however, it is
695
- # inefficient since it merely sleeps for 1 second and then returns +true+ as
696
- # long as #closed? returns +false+. IO.select should be used for real IO
697
- # objects to wait for a readable condition on platforms with support for
698
- # IO.select. Other solutions should be found as necessary to improve this
699
- # implementation on a case by case basis.
700
- #
701
- # Basically, this method should be overridden in derivative classes.
702
- def read_ready?
703
- return false unless readable?
704
- sleep(1)
705
- true
978
+ buffer = delegate_r.character_io.read_line(
979
+ separator: separator,
980
+ limit: limit,
981
+ chomp: chomp,
982
+ discard_newlines: discard_newlines
983
+ )
984
+
985
+ # Increment the number of times this method has returned a "line".
986
+ self.lineno += 1
987
+ # Set the last line number in the global.
988
+ $. = lineno
989
+ # Set the last read line in the global and return it.
990
+ # NOTE:
991
+ # Through Ruby 3.3, assigning to $_ has no effect outside of a method that
992
+ # does it. This assignment is kept in case that ever changes.
993
+ $_ = buffer
994
+ end
995
+
996
+ ##
997
+ # @overload readlines(separator = $/, limit = nil, chomp: false)
998
+ #
999
+ # @param separator [String, nil] a non-empty String that separates each
1000
+ # line, an empty String that equates to 2 or more successive newlines as
1001
+ # the separator, or `nil` to indicate reading all remaining data
1002
+ # @param limit [Integer, nil] an Integer limiting the number of bytes
1003
+ # returned in each line or `nil` to indicate no limit
1004
+ # @param chomp [Boolean] when `true` trailing newlines and carriage returns
1005
+ # will be removed from each line
1006
+ #
1007
+ # @return [Array<String>] the remaining lines in the stream
1008
+ #
1009
+ # @overload readlines(limit, chomp: false)
1010
+ #
1011
+ # @param limit [Integer] an Integer limiting the number of bytes returned in
1012
+ # each line or `nil` to indicate no limit
1013
+ # @param chomp [Boolean] when `true` trailing newlines and carriage returns
1014
+ # will be removed from each line
1015
+ #
1016
+ # @return [Array<String>] the remaining lines in the stream where the
1017
+ # separator is `$/`
1018
+ #
1019
+ # @raise [IOError] if the stream is not open for reading
1020
+ def readlines(*args, **opts)
1021
+ each_line(*args, **opts).to_a
1022
+ end
1023
+
1024
+ ##
1025
+ # Reads and returns at most `length` bytes from the stream.
1026
+ #
1027
+ # If the internal read buffer is **not** empty, only the buffer is used, even
1028
+ # if less than `length` bytes are available. If the internal buffer **is**
1029
+ # empty, reads from the underlying stream.
1030
+ #
1031
+ # @param length [Integer] the number of bytes to read
1032
+ # @param buffer [String] the location in which to store the data
1033
+ #
1034
+ # @return [String] the data read from the stream
1035
+ #
1036
+ # @raise [EOFError] if reading begins at the end of the stream
1037
+ # @raise [IOError] if the stream is not open for reading
1038
+ def readpartial(length, buffer = nil)
1039
+ length = ensure_integer(length)
1040
+ raise ArgumentError, 'length must be at least 0' if length < 0
1041
+ buffer = ensure_string(buffer) if nil != buffer
1042
+
1043
+ assert_readable
1044
+
1045
+ if RBVER_LT_3_0_4 && length == 0
1046
+ return (buffer || ''.b)
706
1047
  end
707
1048
 
708
- # call-seq:
709
- # ios.readable? -> true or false
710
- #
711
- # Returns +true+ if the stream is both open and readable, +false+ otherwise.
712
- #
713
- # This implementation checks to see if #unbuffered_read is defined in order
714
- # to make its determination. Override this if the implementing class always
715
- # provides the #unbuffered_read method but may not always be open in a
716
- # readable mode.
717
- def readable?
718
- ! __io_like__closed_read? && respond_to?(:unbuffered_read, true)
1049
+ unless delegate_r.character_io.buffer_empty?
1050
+ raise IOError, 'byte oriented read for character buffered IO'
719
1051
  end
720
1052
 
721
- # call-seq:
722
- # ios.readbytes(length) -> string
723
- #
724
- # Reads and returns _length_ bytes from the data stream.
725
- #
726
- # Raises EOFError if reading begins at the end of the stream. Raises
727
- # IOError if #closed? returns +true+. Raises IOError unless #readable?
728
- # returns +true+. Raises TruncatedDataError if insufficient data is
729
- # immediately available to satisfy the request.
730
- #
731
- # In the case of TruncatedDataError being raised, the retrieved data can be
732
- # fetched from the _data_ attribute of the exception.
733
- #
734
- # This method is basically copied from IO#readbytes.
735
- #
736
- # <b>NOTE:</b> Because this method relies on #unbuffered_read, it will also
737
- # raise the same errors and block at the same times as that function.
738
- def readbytes(length)
739
- buffer = read(length)
740
- if buffer.nil? then
741
- raise EOFError, "end of file reached"
1053
+ result = ensure_buffer(length, buffer) do |binary_buffer|
1054
+ unless delegate_r.buffered_io.read_buffer_empty?
1055
+ next delegate_r.read(length, buffer: binary_buffer)
742
1056
  end
743
- if buffer.length < length then
744
- raise TruncatedDataError.new("data truncated", buffer)
745
- end
746
- buffer
747
- end
748
1057
 
749
- # call-seq:
750
- # ios.readchar -> integer
751
- #
752
- # Returns the next 8-bit byte (0..255) from the stream.
753
- #
754
- # Raises EOFError when there is no more data in the stream. Raises IOError
755
- # if #closed? returns +true+. Raises IOError unless #readable? returns
756
- # +true+.
757
- #
758
- # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
759
- # #unbuffered_read. Therefore, this method always blocks. Aside from that
760
- # exception, this method will also raise the same errors and block at the
761
- # same times as #unbuffered_read.
762
- def readchar
763
- __io_like__buffered_read(1)[0]
764
- rescue Errno::EAGAIN, Errno::EINTR
765
- retry if read_ready?
1058
+ delegate_r.blocking_io.read(length, buffer: binary_buffer)
766
1059
  end
767
1060
 
768
- # call-seq:
769
- # ios.readline(sep_string = $/) -> string
770
- #
771
- # Returns the next line from the stream, where lines are separated by
772
- # _sep_string_. Increments #lineno by <tt>1</tt> for each call regardless
773
- # of the value of _sep_string_.
774
- #
775
- # If _sep_string_ is not +nil+ and not a String, it is first converted to a
776
- # String using its +to_str+ method and processing continues as follows.
777
- #
778
- # If _sep_string_ is +nil+, a line is defined as the remaining contents of
779
- # the stream. Partial data will be returned if a low-level error of any
780
- # kind is raised after some data is retrieved. This is equivalent to
781
- # calling #read without any arguments except that this method will raise an
782
- # EOFError if called at the end of the stream.
783
- #
784
- # If _sep_string_ is an empty String, a paragraph is returned, where a
785
- # paragraph is defined as data followed by 2 or more successive newline
786
- # characters. A maximum of 2 newlines are returned at the end of the
787
- # returned data. Fewer may be returned if the stream ends before at least 2
788
- # successive newlines are seen.
789
- #
790
- # Any other value for _sep_string_ is used as a delimiter to mark the end of
791
- # a line. The returned data includes this delimiter unless the stream ends
792
- # before the delimiter is seen.
793
- #
794
- # In any case, the end of the stream terminates the current line.
795
- #
796
- # Raises EOFError when there is no more data in the stream. Raises IOError
797
- # if #closed? returns +true+. Raises IOError unless #readable? returns
798
- # +true+.
799
- #
800
- # <b>NOTE:</b> When _sep_string_ is not +nil+, this method ignores
801
- # Errno::EAGAIN and Errno::EINTR raised by #unbuffered_read. Therefore,
802
- # this method will always block in that case. Aside from that exception,
803
- # this method will raise the same errors and block at the same times as
804
- # #unbuffered_read.
805
- def readline(sep_string = $/)
806
- # Ensure that sep_string is either nil or a String.
807
- unless sep_string.nil? || sep_string.kind_of?(String) then
808
- sep_string = sep_string.to_str
809
- end
1061
+ # The delegate returns the read content unless a buffer is given.
1062
+ return nil == buffer ? result : buffer
1063
+ end
1064
+
1065
+ ##
1066
+ # @overload reopen(other)
1067
+ # @param other [Like] another IO::Like instance whose delegate(s) will be
1068
+ # dup'd and used as this stream's delegate(s)
1069
+ #
1070
+ # @raise [IOError] if this instance is closed
1071
+ #
1072
+ # @overload reopen(io)
1073
+ # @param io [IO, #to_io] an IO instance that will be dup'd and used as this
1074
+ # stream's delegate
1075
+ #
1076
+ # @raise [IOError] if this instance or `io` are closed
1077
+ #
1078
+ # @overload reopen(path, mode, **opt)
1079
+ # @param path [String] path to a file to open and use as a delegate for this
1080
+ # stream
1081
+ # @param mode [String] file open mode as used by `File.open`, defaults to a
1082
+ # mode equivalent to this stream's current read/writ-ability
1083
+ # @param opts [Hash] options hash as used by `File.open`
1084
+ #
1085
+ # @raise all errors raised by `File.open`
1086
+ #
1087
+ # Replaces the delegate(s) of this stream with another instance's delegate(s)
1088
+ # or an IO instance.
1089
+ #
1090
+ # @return [self]
1091
+ def reopen(*args, **opts)
1092
+ unless args.size == 1 || args.size == 2
1093
+ raise ArgumentError,
1094
+ "wrong number of arguments (given #{args.size}, expected 1..2)"
1095
+ end
810
1096
 
811
- buffer = ''
1097
+ if args.size == 1
812
1098
  begin
813
- if sep_string.nil? then
814
- # A nil line separator means that the user wants to capture all the
815
- # remaining input.
816
- loop do
817
- buffer << __io_like__buffered_read(4096)
818
- end
819
- else
820
- begin
821
- # Record if the user requested paragraphs rather than lines.
822
- paragraph_requested = sep_string.empty?
823
- # An empty line separator string indicates that the user wants to
824
- # return paragraphs. A pair of newlines in the stream is used to
825
- # mark this.
826
- sep_string = "\n\n" if paragraph_requested
827
-
828
- # Add each character from the input to the buffer until either the
829
- # buffer has the right ending or the end of the input is reached.
830
- while buffer.index(sep_string, -sep_string.length).nil? &&
831
- (char = __io_like__buffered_read(1)) do
832
- buffer << char
833
- end
834
-
835
- if paragraph_requested then
836
- # If the user requested paragraphs instead of lines, we need to
837
- # consume and discard all newlines remaining at the front of the
838
- # input.
839
- while char == "\n" && (char = __io_like__buffered_read(1)) do
840
- nil
841
- end
842
- # Put back the last character.
843
- ungetc(char[0])
844
- end
845
- rescue Errno::EAGAIN, Errno::EINTR
846
- retry if read_ready?
847
- end
1099
+ io = args[0]
1100
+ io = args[0].to_io unless IO::Like === io
1101
+
1102
+ if IO::Like === io
1103
+ assert_open
1104
+ new_delegate_r = io.delegate_r.concrete_io.dup
1105
+ new_delegate_w = io.duplexed? ? io.delegate_w.concrete_io.dup : new_delegate_r
1106
+ close
1107
+ initialize(
1108
+ new_delegate_r,
1109
+ new_delegate_w,
1110
+ binmode: io.binmode?,
1111
+ internal_encoding: delegate_r.character_io.internal_encoding,
1112
+ external_encoding: delegate_r.character_io.external_encoding,
1113
+ sync: io.sync,
1114
+ pid: io.pid,
1115
+ pipeline_class: @pipeline_class
1116
+ )
1117
+ return self
1118
+ end
1119
+
1120
+ unless IO === io
1121
+ raise TypeError,
1122
+ "can't convert #{args[0].class} to IO (#{args[0].class}#to_io gives #{io.class})"
848
1123
  end
849
- rescue EOFError, SystemCallError
850
- # Reraise the error if there is nothing to return.
851
- raise if buffer.empty?
1124
+
1125
+ assert_open
1126
+ io = io.dup
1127
+ rescue NoMethodError
1128
+ mode = (readable? ? 'r' : 'w').dup
1129
+ mode << '+' if readable? && writable?
1130
+ mode << 'b'
1131
+ io = File.open(args[0], mode)
852
1132
  end
853
- # Increment the number of times this method has returned a "line".
854
- self.lineno += 1
855
- buffer
1133
+ else
1134
+ io = File.open(*args, **opts)
856
1135
  end
857
1136
 
858
- # call-seq:
859
- # ios.readlines(sep_string = $/) -> array
860
- #
861
- # Returns an Array containing the lines in the stream using #each_line.
862
- #
863
- # If _sep_string_ is +nil+, a line is defined as the remaining contents of
864
- # the stream. If _sep_string_ is not a String, it is converted to one using
865
- # its +to_str+ method. If _sep_string_ is empty, a paragraph is returned,
866
- # where a paragraph is defined as data followed by 2 or more successive
867
- # newline characters (only 2 newlines are returned at the end of the
868
- # returned data).
869
- #
870
- # In any case, the end of the stream terminates the current line.
871
- #
872
- # Raises EOFError when there is no more data in the stream. Raises IOError
873
- # if #closed? returns +true+. Raises IOError unless #readable? returns
874
- # +true+.
875
- #
876
- # <b>NOTE:</b> When _sep_string_ is not +nil+, this method ignores
877
- # Errno::EAGAIN and Errno::EINTR raised by #unbuffered_read. Therefore,
878
- # this method always blocks. Aside from that exception, this method will
879
- # also raise the same errors and block at the same times as
880
- # #unbuffered_read.
881
- def readlines(sep_string = $/)
882
- lines = []
883
- each_line(sep_string) { |line| lines << line }
884
- lines
885
- end
1137
+ close
1138
+ initialize(
1139
+ IO::LikeHelpers::IOWrapper.new(io),
1140
+ binmode: io.binmode?,
1141
+ internal_encoding: delegate_r.character_io.internal_encoding,
1142
+ external_encoding: delegate_r.character_io.external_encoding,
1143
+ sync: io.sync,
1144
+ pid: io.pid,
1145
+ pipeline_class: @pipeline_class
1146
+ )
1147
+
1148
+ self
1149
+ end
886
1150
 
887
- # call-seq:
888
- # ios.readpartial(length[, buffer]) -> string or buffer
889
- #
890
- # Returns at most _length_ bytes from the data stream using only the
891
- # internal read buffer if the buffer is not empty. Falls back to reading
892
- # from the stream if the buffer is empty. Blocks if no data is available
893
- # from either the internal read buffer or the data stream regardless of
894
- # whether or not the data stream would block.
895
- #
896
- # Raises EOFError when there is no more data in the stream. Raises IOError
897
- # if #closed? returns +true+. Raises IOError unless #readable? returns
898
- # +true+.
899
- #
900
- # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
901
- # #unbuffered_read. Therefore, this method always blocks if unable to
902
- # immediately return _length_ bytes. Aside from that exception, this method
903
- # will also raise the same errors and block at the same times as
904
- # #unbuffered_read.
905
- def readpartial(length, buffer = nil)
906
- # Check the validity of the method arguments.
907
- unless length >= 0 then
908
- raise ArgumentError, "negative length #{length} given"
909
- end
910
- buffer = '' if buffer.nil?
911
- # Flush the buffer.
912
- buffer.slice!(0..-1)
913
-
914
- # Read and return up to length bytes.
915
- if __io_like__internal_read_buffer.empty? then
916
- begin
917
- buffer << __io_like__buffered_read(length)
918
- rescue Errno::EAGAIN, Errno::EINTR
919
- retry if read_ready?
920
- end
921
- else
922
- raise IOError, 'closed stream' if closed?
923
- raise IOError, 'not opened for reading' unless readable?
1151
+ ##
1152
+ # Sets the position of the file pointer to the beginning of the stream.
1153
+ #
1154
+ # The `lineno` attribute is reset to `0` if successful and the stream is
1155
+ # readable.
1156
+ #
1157
+ # @return [0]
1158
+ #
1159
+ # @raise [IOError] if the stream is closed
1160
+ # @raise [Errno::ESPIPE] if the stream is not seekable
1161
+ def rewind
1162
+ seek(0, IO::SEEK_SET)
1163
+ self.lineno = 0 if readable?
1164
+ 0
1165
+ end
924
1166
 
925
- buffer << __io_like__internal_read_buffer.slice!(0, length)
926
- end
927
- buffer
1167
+ ##
1168
+ # Sets the current stream position to `amount` based on the setting of
1169
+ # `whence`.
1170
+ #
1171
+ # | `whence` | `amount` Interpretation |
1172
+ # | -------- | ----------------------- |
1173
+ # | `:CUR` or `IO::SEEK_CUR` | `amount` added to current stream position |
1174
+ # | `:END` or `IO::SEEK_END` | `amount` added to end of stream position (`amount` will usually be negative here) |
1175
+ # | `:SET` or `IO::SEEK_SET` | `amount` used as absolute position |
1176
+ #
1177
+ # @param amount [Integer] the amount to move the position in bytes
1178
+ # @param whence [Integer, Symbol] the position alias from which to consider
1179
+ # `amount`
1180
+ #
1181
+ # @return [0]
1182
+ #
1183
+ # @raise [IOError] if the stream is closed
1184
+ # @raise [Errno::ESPIPE] if the stream is not seekable
1185
+ def seek(amount, whence = IO::SEEK_SET)
1186
+ super
1187
+ # This also clears the byte oriented read buffer, so calling
1188
+ # delegate_r.buffered_io.flush would be redundant.
1189
+ delegate_r.character_io.clear
1190
+ 0
1191
+ end
1192
+
1193
+ ##
1194
+ # @overload set_encoding(encoding, **opts)
1195
+ # @param encoding [Encoding, String, nil] the external encoding or both the
1196
+ # external and internal encoding if specified as `"ext_enc:int_enc"`
1197
+ # @param opts [Hash] encoding conversion options used character or newline
1198
+ # conversion is performed
1199
+ #
1200
+ # @overload set_encoding(external, internal, **opts)
1201
+ # @param external [Encoding, String, nil] the external encoding
1202
+ # @param internal [Encoding, String, nil] the internal encoding
1203
+ # @param opts [Hash] encoding conversion options used character or newline
1204
+ # conversion is performed
1205
+ #
1206
+ # Sets the external and internal encodings of the stream.
1207
+ #
1208
+ # When the given external encoding is not `nil` or `Encoding::BINARY` or an
1209
+ # equivalent and the internal encoding is either not given or `nil`, the
1210
+ # current value of `Encoding.default_internal` is used for the internal
1211
+ # encoding.
1212
+ #
1213
+ # When the given external encoding is `nil` and the internal encoding is either
1214
+ # not given or `nil`, the current values of `Encoding.default_external` and
1215
+ # `Encoding.default_internal` are used, respectively, unless
1216
+ # `Encoding.default_external` is `Encoding::BINARY` or an equivalent **or**
1217
+ # `Encoding.default_internal` is `nil`, in which case `nil` is used for both.
1218
+ #
1219
+ # Setting the given internal encoding to `"-"` indicates no character
1220
+ # conversion should be performed. The internal encoding of the stream will be
1221
+ # set to `nil`. This is needed in cases where `Encoding.default_internal` is
1222
+ # not `nil` but character conversion is not desired.
1223
+ #
1224
+ # @return [self]
1225
+ #
1226
+ # @raise [TypeError] if the given external encoding is `nil` and the internal
1227
+ # encoding is given and **not** `nil`
1228
+ # @raise [ArgumentError] if an encoding given as a string is invalid
1229
+ def set_encoding(ext_enc, int_enc = nil, **opts)
1230
+ assert_open
1231
+
1232
+ # Check that any given newline option is valid.
1233
+ if opts.key?(:newline) &&
1234
+ ! %i{cr lf crlf universal}.include?(opts[:newline])
1235
+ message = 'unexpected value for newline option'
1236
+ message += ": #{opts[:newline]}" if Symbol === opts[:newline]
1237
+ raise ArgumentError, message
928
1238
  end
929
1239
 
930
- # call-seq:
931
- # ios.rewind -> 0
932
- #
933
- # Sets the position of the file pointer to the beginning of the stream and
934
- # returns 0 when complete. The lineno attribute is reset to 0 if
935
- # successful and the stream is readable according to #readable?.
936
- #
937
- # As a side effect, the internal read and write buffers are flushed.
938
- #
939
- # Raises IOError if #closed? returns +true+. Raises Errno::ESPIPE unless
940
- # #seekable? returns +true+.
941
- #
942
- # <b>NOTE:</b> Because this method relies on #unbuffered_seek and
943
- # #unbuffered_write (when the internal write buffer is not empty), it will
944
- # also raise the same errors and block at the same times as those functions.
945
- def rewind
946
- seek(0, IO::SEEK_SET)
947
- self.lineno = 0 if readable?
948
- 0
1240
+ # Newline handling is not allowed in binary mode.
1241
+ if binmode? &&
1242
+ (opts.key?(:newline) ||
1243
+ opts[:cr_newline] || opts[:crlf_newline] || opts[:lf_newline] ||
1244
+ opts[:universal_newline])
1245
+ raise ArgumentError, 'newline decorator with binary mode'
949
1246
  end
950
1247
 
951
- # call-seq:
952
- # seek(offset[, whence]) -> 0
953
- #
954
- # Sets the current data position to _offset_ based on the setting of
955
- # _whence_. If _whence_ is unspecified or IO::SEEK_SET, _offset_ counts
956
- # from the beginning of the data. If _whence_ is IO::SEEK_END, _offset_
957
- # counts from the end of the data (_offset_ should be negative here). If
958
- # _whence_ is IO::SEEK_CUR, _offset_ is relative to the current position.
959
- #
960
- # As a side effect, the internal read and write buffers are flushed except
961
- # when seeking relative to the current position (whence is IO::SEEK_CUR) to
962
- # a location within the internal read buffer.
963
- #
964
- # Raises IOError if #closed? returns +true+. Raises Errno::ESPIPE unless
965
- # #seekable? returns +true+.
966
- #
967
- # <b>NOTE:</b> Because this method relies on #unbuffered_seek and
968
- # #unbuffered_write (when the internal write buffer is not empty), it will
969
- # also raise the same errors and block at the same times as those functions.
970
- def seek(offset, whence = IO::SEEK_SET)
971
- __io_like__buffered_seek(offset, whence)
972
- 0
1248
+ # Ruby 3.2 and below have a bug (#18899) handling the internal encoding
1249
+ # correctly when the external encoding is binary that only happens when they
1250
+ # are supplied individually to this method.
1251
+ bug_18899_compatibility = RBVER_LT_3_3
1252
+ # Convert the argument(s) into Encoding objects.
1253
+ if ! (nil == ext_enc || Encoding === ext_enc) && nil == int_enc
1254
+ string_arg = ensure_string(ext_enc)
1255
+ begin
1256
+ e, _, i = string_arg.rpartition(':')
1257
+ # Bug #18899 compatibility is unnecessary when the encodings are passed
1258
+ # together as a colon speparated string.
1259
+ ext_enc, int_enc, bug_18899_compatibility = e, i, false unless e.empty?
1260
+ rescue Encoding::CompatibilityError
1261
+ # This is caused by failure to split on colon when the string argument
1262
+ # is not ASCII compatible. Ignore it and use the argument as is.
1263
+ end
973
1264
  end
974
1265
 
975
- # call-seq:
976
- # ios.seekable? -> true or false
977
- #
978
- # Returns +true+ if the stream is seekable, +false+ otherwise.
979
- #
980
- # This implementation always returns +false+ for duplexed objects and
981
- # checks to see if #unbuffered_seek is defined in order to make its
982
- # determination otherwise. Override this if the implementing class always
983
- # provides the #unbuffered_seek method but may not always be seekable.
984
- def seekable?
985
- ! duplexed? && respond_to?(:unbuffered_seek, true)
1266
+ # Potential values:
1267
+ # ext_enc int_enc
1268
+ # ======================
1269
+ # nil Object => error
1270
+ # nil nil => maybe copy default encodings
1271
+ # Object Object => use given encodings
1272
+ # Object nil => maybe copy default internal encoding
1273
+ if nil == ext_enc && nil == int_enc
1274
+ unless Encoding::BINARY == Encoding.default_external ||
1275
+ nil == Encoding.default_internal
1276
+ ext_enc = Encoding.default_external
1277
+ int_enc = Encoding.default_internal
1278
+ end
1279
+ else
1280
+ ext_enc = Encoding.find(ext_enc)
1281
+ int_enc = case int_enc
1282
+ when nil
1283
+ RBVER_LT_3_3 ? nil : Encoding.default_internal
1284
+ when '-'
1285
+ # Allows explicit request of no conversion when
1286
+ # Encoding.default_internal is set.
1287
+ nil
1288
+ else
1289
+ Encoding.find(int_enc)
1290
+ end
986
1291
  end
987
1292
 
988
- # call-seq:
989
- # ios.sync -> true or false
990
- #
991
- # Returns true if the internal write buffer is currently being bypassed,
992
- # false otherwise.
993
- #
994
- # Raises IOError if #closed? returns +true+.
995
- def sync
996
- raise IOError, 'closed stream' if closed?
997
- @__io_like__sync ||= false
1293
+ # Ignore the chosen internal encoding when no conversion will be performed.
1294
+ if int_enc == ext_enc ||
1295
+ Encoding::BINARY == ext_enc && ! bug_18899_compatibility
1296
+ int_enc = nil
998
1297
  end
999
1298
 
1000
- # call-seq:
1001
- # ios.sync = boolean -> boolean
1002
- #
1003
- # When set to +true+ the internal write buffer will be bypassed. Any data
1004
- # currently in the buffer will be flushed prior to the next output
1005
- # operation. When set to +false+, the internal write buffer will be
1006
- # enabled.
1007
- #
1008
- # Raises IOError if #closed? returns +true+.
1009
- def sync=(sync)
1010
- raise IOError, 'closed stream' if closed?
1011
- @__io_like__sync = sync ? true : false
1299
+ # ASCII incompatible external encoding without conversion when reading
1300
+ # requires binmode.
1301
+ if ! binmode? && readable? && nil == int_enc &&
1302
+ ! (ext_enc || Encoding.default_external).ascii_compatible?
1303
+ raise ArgumentError, 'ASCII incompatible encoding needs binmode'
1012
1304
  end
1013
1305
 
1014
- # call-seq:
1015
- # ios.sysread(length) -> string
1016
- #
1017
- # Reads and returns up to _length_ bytes directly from the data stream,
1018
- # bypassing the internal read buffer.
1019
- #
1020
- # Returns <tt>""</tt> if _length_ is 0 regardless of the status of the data
1021
- # stream. This is for compatibility with IO#sysread.
1022
- #
1023
- # Raises EOFError if reading begins at the end of the stream. Raises
1024
- # IOError if the internal read buffer is not empty. Raises IOError if
1025
- # #closed? returns +true+.
1026
- #
1027
- # <b>NOTE:</b> Because this method relies on #unbuffered_read, it will also
1028
- # raise the same errors and block at the same times as that function.
1029
- def sysread(length, buffer = nil)
1030
- buffer = buffer.nil? ? '' : buffer.to_str
1031
- buffer.slice!(0..-1) unless buffer.empty?
1032
- return buffer if length == 0
1033
-
1034
- raise IOError, 'closed stream' if closed?
1035
- raise IOError, 'not opened for reading' unless readable?
1036
- unless __io_like__internal_read_buffer.empty? then
1037
- raise IOError, 'sysread on buffered IO'
1038
- end
1306
+ delegate_r.character_io.set_encoding(ext_enc, int_enc, **opts)
1307
+ if duplexed?
1308
+ delegate_w.character_io.set_encoding(ext_enc, int_enc, **opts)
1309
+ end
1039
1310
 
1040
- # Flush the internal write buffer for writable, non-duplexed objects.
1041
- __io_like__buffered_flush if writable? && ! duplexed?
1311
+ self
1312
+ end
1042
1313
 
1043
- buffer << unbuffered_read(length)
1314
+ ##
1315
+ # Sets the external encoding of the stream based on a byte order mark (BOM)
1316
+ # in the next bytes of the stream if found or to `nil` if not found.
1317
+ #
1318
+ # @return [nil] if no byte order mark is found
1319
+ # @return [Encoding] the encoding indicated by the byte order mark
1320
+ #
1321
+ # @raise [ArgumentError] if the stream is not in binary mode, an internal
1322
+ # encoding is set, or the external encoding is set to anything other than
1323
+ # `Encoding::BINARY`
1324
+ #
1325
+ # @version \>= Ruby 2.7
1326
+ def set_encoding_by_bom
1327
+ unless binmode?
1328
+ raise ArgumentError, 'ASCII incompatible encoding needs binmode'
1329
+ end
1330
+ if nil != internal_encoding
1331
+ raise ArgumentError, 'encoding conversion is set'
1332
+ elsif nil != external_encoding && Encoding::BINARY != external_encoding
1333
+ raise ArgumentError, "encoding is set to #{external_encoding} already"
1044
1334
  end
1045
1335
 
1046
- # call-seq:
1047
- # ios.sysseek(offset[, whence]) -> integer
1048
- #
1049
- # Sets the current data position to _offset_ based on the setting of
1050
- # _whence_. If _whence_ is unspecified or IO::SEEK_SET, _offset_ counts
1051
- # from the beginning of the data. If _whence_ is IO::SEEK_END, _offset_
1052
- # counts from the end of the data (_offset_ should be negative here). If
1053
- # _whence_ is IO::SEEK_CUR, _offset_ is relative to the current position.
1054
- #
1055
- # Raises IOError if the internal read buffer is not empty. Raises IOError
1056
- # if #closed? returns +true+. Raises Errno::ESPIPE unless #seekable?
1057
- # returns +true+.
1058
- #
1059
- # <b>NOTE:</b> Because this method relies on #unbuffered_seek, it will also
1060
- # raise the same errors and block at the same times as that function.
1061
- def sysseek(offset, whence = IO::SEEK_SET)
1062
- raise IOError, 'closed stream' if closed?
1063
- raise Errno::ESPIPE unless seekable?
1064
- unless __io_like__internal_read_buffer.empty? then
1065
- raise IOError, 'sysseek on buffered IO'
1336
+ return nil unless readable?
1337
+
1338
+ case b1 = getbyte
1339
+ when nil
1340
+ when 0xEF
1341
+ case b2 = getbyte
1342
+ when nil
1343
+ when 0xBB
1344
+ case b3 = getbyte
1345
+ when nil
1346
+ when 0xBF
1347
+ set_encoding(Encoding::UTF_8)
1348
+ return Encoding::UTF_8
1349
+ end
1350
+ ungetbyte(b3)
1066
1351
  end
1067
- unless __io_like__internal_write_buffer.empty? then
1068
- warn('warning: sysseek on buffered IO')
1352
+ ungetbyte(b2)
1353
+ when 0xFE
1354
+ case b2 = getbyte
1355
+ when nil
1356
+ when 0xFF
1357
+ set_encoding(Encoding::UTF_16BE)
1358
+ return Encoding::UTF_16BE
1069
1359
  end
1070
-
1071
- unbuffered_seek(offset, whence)
1360
+ ungetbyte(b2)
1361
+ when 0xFF
1362
+ case b2 = getbyte
1363
+ when nil
1364
+ when 0xFE
1365
+ case b3 = getbyte
1366
+ when nil
1367
+ when 0x00
1368
+ case b4 = getbyte
1369
+ when nil
1370
+ when 0x00
1371
+ set_encoding(Encoding::UTF_32LE)
1372
+ return Encoding::UTF_32LE
1373
+ end
1374
+ ungetbyte(b4)
1375
+ end
1376
+ ungetbyte(b3)
1377
+ set_encoding(Encoding::UTF_16LE)
1378
+ return Encoding::UTF_16LE
1379
+ end
1380
+ ungetbyte(b2)
1381
+ when 0x00
1382
+ case b2 = getbyte
1383
+ when nil
1384
+ when 0x00
1385
+ case b3 = getbyte
1386
+ when nil
1387
+ when 0xFE
1388
+ case b4 = getbyte
1389
+ when nil
1390
+ when 0xFF
1391
+ set_encoding(Encoding::UTF_32BE)
1392
+ return Encoding::UTF_32BE
1393
+ end
1394
+ ungetbyte(b4)
1395
+ end
1396
+ ungetbyte(b3)
1397
+ end
1398
+ ungetbyte(b2)
1072
1399
  end
1400
+ ungetbyte(b1)
1073
1401
 
1074
- # call-seq:
1075
- # ios.syswrite(string) -> integer
1076
- #
1077
- # Writes _string_ directly to the data stream, bypassing the internal write
1078
- # buffer and returns the number of bytes written.
1079
- #
1080
- # As a side effect for non-duplex objects, the internal read buffer is
1081
- # flushed.
1082
- #
1083
- # Raises IOError if #closed? returns +true+. Raises IOError unless
1084
- # #writable? returns +true+.
1085
- #
1086
- # <b>NOTE:</b> Because this method relies on #unbuffered_write, it will also
1087
- # raise the same errors and block at the same times as that function.
1088
- def syswrite(string)
1089
- raise IOError, 'closed stream' if closed?
1090
- raise IOError, 'not opened for writing' unless writable?
1091
- unless __io_like__internal_write_buffer.empty? then
1092
- warn('warning: syswrite on buffered IO')
1093
- end
1402
+ return nil
1403
+ end
1094
1404
 
1095
- # Flush the internal read buffer and set the unbuffered position to the
1096
- # buffered position when dealing with non-duplexed objects.
1097
- unless duplexed? || __io_like__internal_read_buffer.empty? then
1098
- unbuffered_seek(-__io_like__internal_read_buffer.length, IO::SEEK_CUR)
1099
- __io_like__internal_read_buffer.slice!(0..-1)
1100
- end
1405
+ ##
1406
+ # Returns `true` if the internal write buffer is being bypassed and `false`
1407
+ # otherwise.
1408
+ #
1409
+ # @return [Boolean]
1410
+ #
1411
+ # @raise [IOError] if the stream is closed
1412
+ def sync
1413
+ assert_open
1414
+ delegate_w.character_io.sync?
1415
+ end
1101
1416
 
1102
- unbuffered_write(string)
1103
- end
1417
+ ##
1418
+ # When set to `true` the internal write buffer will be bypassed. Any data
1419
+ # currently in the buffer will be flushed prior to the next output operation.
1420
+ # When set to `false`, the internal write buffer will be enabled.
1421
+ #
1422
+ # @param sync [Boolean] the sync mode
1423
+ #
1424
+ # @return [Boolean] the given value for `sync`
1425
+ #
1426
+ # @raise [IOError] if the stream is closed
1427
+ def sync=(sync)
1428
+ assert_open
1429
+ delegate_w.character_io.sync = sync
1430
+ end
1104
1431
 
1105
- # call-seq:
1106
- # ios.to_io -> ios
1107
- #
1108
- # Returns _ios_.
1109
- def to_io
1110
- self
1432
+ ##
1433
+ # Reads and returns up to `length` bytes directly from the data stream,
1434
+ # bypassing the internal read buffer.
1435
+ #
1436
+ # If `buffer` is given, it is used to store the bytes that are read;
1437
+ # otherwise, a new buffer is created.
1438
+ #
1439
+ # Returns an empty String if `length` is `0` regardless of the status of the
1440
+ # data stream. This is for compatibility with `IO#sysread`.
1441
+ #
1442
+ # @param length [Integer] the number of bytes to read
1443
+ # @param buffer [String] a buffer into which bytes will be read
1444
+ #
1445
+ # @return [String] the bytes that were read
1446
+ #
1447
+ # @raise [EOFError] if reading begins at the end of the stream
1448
+ # @raise [IOError] if the internal read buffer is not empty
1449
+ # @raise [IOError] if the stream is not open for reading
1450
+ def sysread(length, buffer = nil)
1451
+ length = ensure_integer(length)
1452
+ raise ArgumentError, "negative length #{length} given" if length < 0
1453
+ buffer = ensure_string(buffer) unless nil == buffer
1454
+
1455
+ return (buffer || ''.b) if length == 0
1456
+
1457
+ assert_readable
1458
+
1459
+ if ! delegate_r.buffered_io.read_buffer_empty?
1460
+ raise IOError, 'sysread for buffered IO'
1461
+ elsif ! delegate_r.character_io.buffer_empty?
1462
+ raise IOError, 'byte oriented read for character buffered IO'
1111
1463
  end
1112
1464
 
1113
- # call-seq:
1114
- # ios.ungetc(integer) -> nil
1115
- #
1116
- # Calls #unread with <tt>integer.chr</tt> as an argument.
1117
- #
1118
- # Raises IOError if #closed? returns +true+. Raises IOError unless
1119
- # #readable? returns +true+.
1120
- def ungetc(integer)
1121
- unread(integer.chr)
1465
+ result = ensure_buffer(length, buffer) do |binary_buffer|
1466
+ delegate_r.blocking_io.read(length, buffer: binary_buffer)
1122
1467
  end
1123
1468
 
1124
- # call-seq:
1125
- # ios.unread(string) -> nil
1126
- #
1127
- # Pushes the given string onto the front of the internal read buffer and
1128
- # returns +nil+. If _string_ is not a String, it is converted to one using
1129
- # its +to_s+ method.
1130
- #
1131
- # Raises IOError if #closed? returns +true+. Raises IOError unless
1132
- # #readable? returns +true+.
1133
- def unread(string)
1134
- raise IOError, 'closed stream' if closed?
1135
- raise IOError, 'not opened for reading' unless readable?
1136
- __io_like__internal_read_buffer.insert(0, string.to_s)
1137
- nil
1138
- end
1469
+ # The delegate returns the read content unless a buffer is given.
1470
+ return buffer ? buffer : result
1471
+ end
1139
1472
 
1140
- # call-seq:
1141
- # ios.write_ready? -> true or false
1142
- #
1143
- # Returns +true+ when the stream may be written without error, +false+
1144
- # otherwise. This method will block until one of the conditions is known.
1145
- #
1146
- # This default implementation of #write_ready? is a hack which should be
1147
- # able to work for both real IO objects and IO-like objects; however, it is
1148
- # inefficient since it merely sleeps for 1 second and then returns +true+ as
1149
- # long as #closed? returns +false+. IO.select should be used for real
1150
- # IO objects to wait for a writeable condition on platforms with support for
1151
- # IO.select. Other solutions should be found as necessary to improve this
1152
- # implementation on a case by case basis.
1153
- #
1154
- # Basically, this method should be overridden in derivative classes.
1155
- def write_ready?
1156
- return false unless writable?
1157
- sleep(1)
1158
- true
1473
+ ##
1474
+ # Sets the current, unbuffered stream position to `amount` based on the
1475
+ # setting of `whence`.
1476
+ #
1477
+ # | `whence` | `amount` Interpretation |
1478
+ # | -------- | ----------------------- |
1479
+ # | `:CUR` or `IO::SEEK_CUR` | `amount` added to current stream position |
1480
+ # | `:END` or `IO::SEEK_END` | `amount` added to end of stream position (`amount` will usually be negative here) |
1481
+ # | `:SET` or `IO::SEEK_SET` | `amount` used as absolute position |
1482
+ #
1483
+ # @param offset [Integer] the amount to move the position in bytes
1484
+ # @param whence [Integer, Symbol] the position alias from which to consider
1485
+ # `amount`
1486
+ #
1487
+ # @return [Integer] the new stream position
1488
+ #
1489
+ # @raise [IOError] if the internal read buffer is not empty
1490
+ # @raise [IOError] if the stream is closed
1491
+ # @raise [Errno::ESPIPE] if the stream is not seekable
1492
+ def sysseek(offset, whence = IO::SEEK_SET)
1493
+ assert_open
1494
+ if ! delegate_r.buffered_io.read_buffer_empty? ||
1495
+ ! delegate_r.character_io.buffer_empty?
1496
+ raise IOError, 'sysseek for buffered IO'
1497
+ end
1498
+ unless delegate_w.buffered_io.write_buffer_empty?
1499
+ warn('warning: sysseek for buffered IO')
1159
1500
  end
1160
1501
 
1161
- # call-seq:
1162
- # ios.writable? -> true or false
1163
- #
1164
- # Returns +true+ if the stream is both open and writable, +false+ otherwise.
1165
- #
1166
- # This implementation checks to see if #unbuffered_write is defined in order
1167
- # to make its determination. Override this if the implementing class always
1168
- # provides the #unbuffered_write method but may not always be open in a
1169
- # writable mode.
1170
- def writable?
1171
- ! __io_like__closed_write? && respond_to?(:unbuffered_write, true)
1502
+ delegate.blocking_io.seek(offset, whence)
1503
+ end
1504
+
1505
+ ##
1506
+ # Writes `string` directly to the data stream, bypassing the internal
1507
+ # write buffer.
1508
+ #
1509
+ # @param string [String] a string of bytes to be written
1510
+ #
1511
+ # @return [Integer] the number of bytes written
1512
+ #
1513
+ # @raise [IOError] if the stream is not open for writing
1514
+ def syswrite(string)
1515
+ assert_writable
1516
+ unless delegate_w.buffered_io.write_buffer_empty?
1517
+ warn('warning: syswrite for buffered IO')
1172
1518
  end
1173
1519
 
1174
- # call-seq:
1175
- # ios.write(string) -> integer
1176
- #
1177
- # Writes the given string to the stream and returns the number of bytes
1178
- # written. If _string_ is not a String, its +to_s+ method is used to
1179
- # convert it into one. The entire contents of _string_ are written,
1180
- # blocking as necessary even if the data stream does not block.
1181
- #
1182
- # Raises IOError if #closed? returns +true+. Raises IOError unless
1183
- # #writable? returns +true+.
1184
- #
1185
- # <b>NOTE:</b> This method ignores Errno::EAGAIN and Errno::EINTR raised by
1186
- # #unbuffered_write. Therefore, this method always blocks if unable to
1187
- # immediately write _string_ completely. Aside from that exception, this
1188
- # method will also raise the same errors and block at the same times as
1189
- # #unbuffered_write.
1190
- def write(string)
1191
- string = string.to_s
1192
- return 0 if string.empty?
1193
-
1194
- bytes_written = 0
1195
- while bytes_written < string.length do
1196
- begin
1197
- bytes_written +=
1198
- __io_like__buffered_write(string.to_s.slice(bytes_written..-1))
1199
- rescue Errno::EAGAIN, Errno::EINTR
1200
- retry if write_ready?
1520
+ delegate_w.buffered_io.flush || delegate_w.blocking_io.write(string.to_s.b)
1521
+ end
1522
+
1523
+ ##
1524
+ # This is for compatibility with IO.
1525
+ alias_method :to_i, :fileno
1526
+
1527
+ ##
1528
+ # @overload ungetbyte(string)
1529
+ # @param string [String] a string of bytes to push onto the internal read
1530
+ # buffer
1531
+ #
1532
+ # @overload ungetbyte(integer)
1533
+ # @param integer [Integer] a number whose low order byte will be pushed
1534
+ # onto the internal read buffer
1535
+ #
1536
+ # Pushes bytes onto the internal read buffer such that subsequent read
1537
+ # operations will return them first.
1538
+ #
1539
+ # @return [nil]
1540
+ #
1541
+ # @raise [IOError] if the stream is not open for reading
1542
+ # @raise [IOError] if the internal read buffer does not have enough space
1543
+ def ungetbyte(obj)
1544
+ assert_readable
1545
+
1546
+ return if nil == obj
1547
+
1548
+ string = case obj
1549
+ when String
1550
+ obj
1551
+ when Integer
1552
+ (obj & 255).chr
1553
+ else
1554
+ ensure_string(obj)
1555
+ end
1556
+
1557
+ delegate_r.character_io.unread(string.b)
1558
+
1559
+ nil
1560
+ end
1561
+
1562
+ ##
1563
+ # @overload ungetc(string)
1564
+ # @param string [String] a string of characters to push onto the internal
1565
+ # read buffer
1566
+ #
1567
+ # @overload ungetc(integer)
1568
+ # @param integer [Integer] a number that will be converted into a character
1569
+ # using the stream's external encoding and pushed onto the internal read
1570
+ # buffer
1571
+ #
1572
+ # Pushes characters onto the internal read buffer such that subsequent read
1573
+ # operations will return them first.
1574
+ #
1575
+ # @return [nil]
1576
+ #
1577
+ # @raise [IOError] if the stream is not open for reading
1578
+ # @raise [IOError] if the internal read buffer does not have enough space
1579
+ def ungetc(string)
1580
+ assert_readable
1581
+
1582
+ return if nil == string && RBVER_LT_3_0
1583
+
1584
+ string = case string
1585
+ when String
1586
+ string.dup
1587
+ when Integer
1588
+ encoding = internal_encoding || external_encoding
1589
+ nil == encoding ? string.chr : string.chr(encoding)
1590
+ else
1591
+ ensure_string(string)
1592
+ end
1593
+
1594
+ delegate_r.character_io.unread(string.b)
1595
+
1596
+ nil
1597
+ end
1598
+
1599
+ ##
1600
+ # @overload wait(events, timeout)
1601
+ # @param events [Integer] a bit mask of `IO::READABLE`, `IO::WRITABLE`, or
1602
+ # `IO::PRIORITY`
1603
+ # @param timeout [Numeric, nil] the timeout in seconds or no timeout if
1604
+ # `nil`
1605
+ #
1606
+ # @overload wait(timeout = nil, mode = :read)
1607
+ # @param timeout [Numeric]
1608
+ # @param mode [Symbol]
1609
+ #
1610
+ # @deprecated Included for compability with Ruby 2.7 and earlier
1611
+ #
1612
+ # @return [self] if the stream becomes ready for at least one of the given
1613
+ # events
1614
+ # @return [nil] if the IO does not become ready before the timeout
1615
+ #
1616
+ # @raise [IOError] if the stream is closed
1617
+ def wait(*args)
1618
+ events = 0
1619
+ timeout = nil
1620
+ if RBVER_LT_3_0
1621
+ # Ruby <=2.7 compatibility mode while running Ruby <=2.7.
1622
+ args.each do |arg|
1623
+ case arg
1624
+ when Symbol
1625
+ events |= wait_event_from_symbol(arg)
1626
+ else
1627
+ timeout = arg
1628
+ if nil != timeout && timeout < 0
1629
+ raise ArgumentError, 'time interval must not be negative'
1630
+ end
1201
1631
  end
1202
1632
  end
1203
- bytes_written
1633
+ else
1634
+ if args.size < 2 || args.size >= 2 && Symbol === args[1]
1635
+ # Ruby <=2.7 compatibility mode while running Ruby >=3.0.
1636
+ timeout = args[0] if args.size > 0
1637
+ if nil != timeout && timeout < 0
1638
+ raise ArgumentError, 'time interval must not be negative'
1639
+ end
1640
+ events = args[1..-1]
1641
+ .map { |mode| wait_event_from_symbol(mode) }
1642
+ .inject(0) { |memo, value| memo | value }
1643
+ elsif args.size == 2
1644
+ # Ruby >=3.0 mode.
1645
+ events = ensure_integer(args[0])
1646
+ timeout = args[1]
1647
+ if nil != timeout && timeout < 0
1648
+ raise ArgumentError, 'time interval must not be negative'
1649
+ end
1650
+ else
1651
+ # Arguments are invalid, but punt like Ruby 3.0 does.
1652
+ return nil
1653
+ end
1204
1654
  end
1655
+ events = IO::READABLE if events == 0
1205
1656
 
1206
- private
1207
-
1208
- # call-seq:
1209
- # ios.__io_like__buffered_flush -> 0
1210
- #
1211
- # Attempts to completely flush the internal write buffer to the data stream.
1212
- #
1213
- # Raises IOError unless #writable? returns +true+.
1214
- #
1215
- # <b>NOTE:</b> Because this method relies on #unbuffered_write, it raises
1216
- # all errors raised by #unbuffered_write and blocks when #unbuffered_write
1217
- # blocks.
1218
- def __io_like__buffered_flush
1219
- raise IOError, 'closed stream' if closed?
1220
- raise IOError, 'not opened for writing' unless writable?
1221
-
1222
- until __io_like__internal_write_buffer.empty? do
1223
- __io_like__internal_write_buffer.slice!(
1224
- 0, unbuffered_write(__io_like__internal_write_buffer)
1225
- )
1226
- end
1227
- 0
1657
+ assert_open
1658
+
1659
+ return self if super(events, timeout)
1660
+ return nil
1661
+ end
1662
+
1663
+ unless RBVER_LT_3_0
1664
+ ##
1665
+ # Waits until the stream is priority or until `timeout` is reached.
1666
+ #
1667
+ # Returns `true` immediately if buffered data is available to read.
1668
+ #
1669
+ # @return [self] when the stream is priority
1670
+ # @return [nil] when the call times out
1671
+ #
1672
+ # @raise [IOError] if the stream is not open for reading
1673
+ #
1674
+ # @version \>= Ruby 3.0
1675
+ def wait_priority(timeout = nil)
1676
+ assert_readable
1677
+
1678
+ return self if delegate.wait(IO::PRIORITY, timeout)
1679
+ return nil
1680
+ end
1681
+ end
1682
+
1683
+ ##
1684
+ # Waits until the stream is readable or until `timeout` is reached.
1685
+ #
1686
+ # Returns `true` immediately if buffered data is available to read.
1687
+ #
1688
+ # @return [self] when the stream is readable
1689
+ # @return [nil] when the call times out
1690
+ #
1691
+ # @raise [IOError] if the stream is not open for reading
1692
+ def wait_readable(timeout = nil)
1693
+ assert_readable
1694
+
1695
+ return self if delegate.wait(IO::READABLE, timeout)
1696
+ return nil
1697
+ end
1698
+
1699
+ ##
1700
+ # Waits until the stream is writable or until `timeout` is reached.
1701
+ #
1702
+ # @return [self] when the stream is writable
1703
+ # @return [nil] when the call times out
1704
+ #
1705
+ # @raise [IOError] if the stream is not open for writing
1706
+ def wait_writable(timeout = nil)
1707
+ assert_writable
1708
+
1709
+ return self if delegate.wait(IO::WRITABLE, timeout)
1710
+ return nil
1711
+ end
1712
+
1713
+ ##
1714
+ # Writes the given arguments to the stream via an internal buffer and returns
1715
+ # the number of bytes written.
1716
+ #
1717
+ # If an argument is not a `String`, its `to_s` method is used to convert it
1718
+ # into one. The entire contents of all arguments are written, blocking as
1719
+ # necessary even if the underlying stream does not block.
1720
+ #
1721
+ # @param strings [Array<Object>] bytes to write to the stream
1722
+ #
1723
+ # @return [Integer] the total number of bytes written
1724
+ #
1725
+ # @raise [IOError] if the stream is not open for writing
1726
+ def write(*strings)
1727
+ # This short circuit is for compatibility with old Ruby versions where this
1728
+ # method took a single argument and would return 0 when the argument
1729
+ # resulted in a 0 length string without first checking if the stream was
1730
+ # already closed.
1731
+ if strings.size == 1
1732
+ # This satisfies rubyspec by ensuring that the argument's #to_s method is
1733
+ # only called once in the case where it results in a non-empty string and
1734
+ # the short circuit is skipped.
1735
+ strings[0] = strings[0].to_s
1736
+ return 0 if strings[0].empty?
1228
1737
  end
1229
1738
 
1230
- # call-seq:
1231
- # ios.__io_like__buffered_read(length) -> string
1232
- #
1233
- # Reads at most _length_ bytes first from an internal read buffer followed
1234
- # by the underlying stream if necessary and returns the resulting buffer.
1235
- #
1236
- # Raises EOFError if the internal read buffer is empty and reading begins at
1237
- # the end of the stream. Raises IOError unless #readable? returns +true+.
1238
- #
1239
- # <b>NOTE:</b> Because this method relies on #unbuffered_read, it raises all
1240
- # errors raised by #unbuffered_read and blocks when #unbuffered_read blocks
1241
- # whenever the internal read buffer is unable to fulfill the request.
1242
- def __io_like__buffered_read(length)
1243
- # Check the validity of the method arguments.
1244
- raise ArgumentError, "non-positive length #{length} given" if length < 0
1245
-
1246
- raise IOError, 'closed stream' if closed?
1247
- raise IOError, 'not opened for reading' unless readable?
1248
-
1249
- # Flush the internal write buffer for writable, non-duplexed objects.
1250
- __io_like__buffered_flush if writable? && ! duplexed?
1251
-
1252
- # Ensure that the internal read buffer has at least enough data to satisfy
1253
- # the request.
1254
- if __io_like__internal_read_buffer.length < length then
1255
- unbuffered_length = length - __io_like__internal_read_buffer.length
1256
- unbuffered_length = fill_size if unbuffered_length < fill_size
1257
-
1258
- begin
1259
- __io_like__internal_read_buffer << unbuffered_read(unbuffered_length)
1260
- rescue EOFError, SystemCallError
1261
- # Reraise the error if there is no data to return.
1262
- raise if __io_like__internal_read_buffer.empty?
1263
- end
1264
- end
1739
+ assert_writable
1265
1740
 
1266
- # Read from the internal read buffer.
1267
- buffer = __io_like__internal_read_buffer.slice!(0, length)
1741
+ delegate_w.buffered_io.flush if sync
1268
1742
 
1269
- buffer
1743
+ bytes_written = 0
1744
+ strings.each do |string|
1745
+ bytes_written += delegate_w.character_io.write(string.to_s)
1270
1746
  end
1271
1747
 
1272
- # call-seq:
1273
- # ios.__io_like__buffered_seek(offset[, whence]) -> integer
1274
- #
1275
- # Sets the current data position to _offset_ based on the setting of
1276
- # _whence_. If _whence_ is unspecified or IO::SEEK_SET, _offset_ counts
1277
- # from the beginning of the data. If _whence_ is IO::SEEK_END, _offset_
1278
- # counts from the end of the data (_offset_ should be negative here). If
1279
- # _whence_ is IO::SEEK_CUR, _offset_ is relative to the current position.
1280
- #
1281
- # As a side effect, the internal read and write buffers are flushed except
1282
- # when seeking relative to the current position (whence is IO::SEEK_CUR) to
1283
- # a location within the internal read buffer.
1284
- #
1285
- # Raises Errno::ESPIPE unless #seekable? returns +true+.
1286
- #
1287
- # See #seek for the usage of _offset_ and _whence_.
1288
- #
1289
- # <b>NOTE:</b> Because this method relies on #unbuffered_seek and
1290
- # #unbuffered_write (when the internal write buffer is not empty), it will
1291
- # raise the same errors and block at the same times as those functions.
1292
- def __io_like__buffered_seek(offset, whence = IO::SEEK_SET)
1293
- raise IOError, 'closed stream' if closed?
1294
- raise Errno::ESPIPE unless seekable?
1295
-
1296
- if whence == IO::SEEK_CUR && offset == 0 then
1297
- # The seek is only determining the current position, so return the
1298
- # buffered position based on the read buffer if it's not empty and the
1299
- # write buffer otherwise.
1300
- __io_like__internal_read_buffer.empty? ?
1301
- unbuffered_seek(0, IO::SEEK_CUR) +
1302
- __io_like__internal_write_buffer.length :
1303
- unbuffered_seek(0, IO::SEEK_CUR) -
1304
- __io_like__internal_read_buffer.length
1305
- elsif whence == IO::SEEK_CUR && offset > 0 &&
1306
- __io_like__internal_write_buffer.empty? &&
1307
- offset <= __io_like__internal_read_buffer.length then
1308
- # The seek is within the read buffer, so just discard a sufficient
1309
- # amount of the buffer and report the new buffered position.
1310
- __io_like__internal_read_buffer.slice!(0, offset)
1311
- unbuffered_seek(0, IO::SEEK_CUR) -
1312
- __io_like__internal_read_buffer.length
1313
- else
1314
- # The seek target is outside of the buffers, so flush the buffers and
1315
- # jump to the new position.
1316
- if whence == IO::SEEK_CUR then
1317
- # Adjust relative offsets based on the current buffered offset.
1318
- offset += __io_like__internal_read_buffer.empty? ?
1319
- __io_like__internal_write_buffer.length :
1320
- -__io_like__internal_read_buffer.length
1321
- end
1748
+ bytes_written
1749
+ end
1322
1750
 
1323
- # Flush the internal buffers.
1324
- __io_like__internal_read_buffer.slice!(0..-1)
1325
- __io_like__buffered_flush if writable?
1751
+ ##
1752
+ # Enables blocking mode on the stream (via #nonblock=), flushes any buffered
1753
+ # data, and then directly writes `string`, bypassing the internal buffer.
1754
+ #
1755
+ # If `string` is not a `String`, its `to_s` method is used to convert it into
1756
+ # one. If any of `string` is written, this method returns the number of bytes
1757
+ # written, which may be less than all requested bytes (partial write).
1758
+ #
1759
+ # @param string [Object] bytes to write to the stream
1760
+ # @param exception [Boolean] when `true` causes this method to raise
1761
+ # exceptions when writing would block; otherwise, symbols are returned
1762
+ #
1763
+ # @return [Integer] the total number of bytes written
1764
+ # @return [:wait_readable, :wait_writable] if `exception` is `false` and
1765
+ # writing to the stream would block
1766
+ #
1767
+ # @raise [IOError] if the stream is not open for writing
1768
+ # @raise [IO::EWOULDBLOCKWaitReadable, IO::EWOULDBLOCKWaitWritable] if
1769
+ # `exception` is `true` and writing to the stream would block
1770
+ # @raise [Errno::EBADF] if non-blocking mode is not supported
1771
+ # @raise [SystemCallError] if there are low level errors
1772
+ def write_nonblock(string, exception: true)
1773
+ assert_writable
1774
+
1775
+ string = string.to_s
1776
+
1777
+ self.nonblock = true
1778
+ result = delegate_w.buffered_io.flush || delegate_w.concrete_io.write(string.b)
1779
+ case result
1780
+ when Integer
1781
+ return result
1782
+ else
1783
+ return nonblock_response(result, exception)
1784
+ end
1785
+ end
1326
1786
 
1327
- # Move the data stream's position as requested.
1328
- unbuffered_seek(offset, whence)
1329
- end
1787
+ # Expose these to other instances of this class for use with #reopen.
1788
+ protected :delegate_r, :delegate_w
1789
+
1790
+ # Hide these to preserve the interface of IO.
1791
+ private :readable?, :writable?
1792
+
1793
+ private
1794
+
1795
+ ##
1796
+ # This returns the class name as a string for all objects including those
1797
+ # where #class is not defined such as descendants of BasicObject.
1798
+ #
1799
+ # @param object [any] an object instance of any class
1800
+ #
1801
+ # @return [String] the class name of `object`
1802
+ def class_name_of(object)
1803
+ # This ensures that the standard #class method is used whether object
1804
+ # implements its own #inspect implementation or does not implement it at
1805
+ # all, such as for BasicObject descendants.
1806
+ Kernel.instance_method(:class).bind(object).call.to_s
1807
+ end
1808
+
1809
+ ##
1810
+ # Ensures that a buffer, if provided, is large enough to hold the requested
1811
+ # number of bytes and is then truncated to the returned number of bytes while
1812
+ # ensuring that the encoding is preserved.
1813
+ #
1814
+ # @param length [Integer] the minimum size of the buffer in bytes
1815
+ # @param buffer [String, nil] the buffer
1816
+ #
1817
+ # @yieldparam binary_buffer [String, nil] a binary encoded String based on
1818
+ # `buffer` (if non-nil) that is at least `length` bytes long
1819
+ # @yieldreturn [Integer, String, Symbol] the result of a low level read
1820
+ # operation. (See {IO::LikeHelpers::AbstractIO#read})
1821
+ #
1822
+ # @return the result of the given block
1823
+ def ensure_buffer(length, buffer)
1824
+ if nil != buffer
1825
+ orig_encoding = buffer.encoding
1826
+ buffer.force_encoding(Encoding::BINARY)
1827
+
1828
+ # Ensure the given buffer is large enough to hold the requested number of
1829
+ # bytes.
1830
+ buffer << "\0".b * (length - buffer.bytesize) if length > buffer.bytesize
1330
1831
  end
1331
1832
 
1332
- # call-seq:
1333
- # ios.__io_like__buffered_write(string) -> integer
1334
- #
1335
- # Writes _string_ to the internal write buffer and returns the number of
1336
- # bytes written. If the internal write buffer is overfilled by _string_, it
1337
- # is repeatedly flushed until that last of _string_ is consumed. A partial
1338
- # write will occur if part of _string_ fills the internal write buffer but
1339
- # the internal write buffer cannot be immediately flushed due to the
1340
- # underlying stream not blocking when unable to accept more data.
1341
- #
1342
- # <b>NOTE:</b> Because this method relies on #unbuffered_write, it raises
1343
- # all errors raised by #unbuffered_write and blocks when #unbuffered_write
1344
- # blocks whenever the internal write buffer is unable to fulfill the
1345
- # request.
1346
- def __io_like__buffered_write(string)
1347
- raise IOError, 'closed stream' if closed?
1348
- raise IOError, 'not opened for writing' unless writable?
1349
-
1350
- # Flush the internal read buffer and set the unbuffered position to the
1351
- # buffered position when dealing with non-duplexed objects.
1352
- unless duplexed? || __io_like__internal_read_buffer.empty? then
1353
- unbuffered_seek(-__io_like__internal_read_buffer.length, IO::SEEK_CUR)
1354
- __io_like__internal_read_buffer.slice!(0..-1)
1355
- end
1833
+ result = yield(buffer)
1834
+ ensure
1835
+ if nil != buffer
1836
+ # A buffer was given to fill, so the delegate returned the number of bytes
1837
+ # read. Truncate the buffer if necessary and restore its original
1838
+ # encoding.
1839
+ buffer.slice!((result || 0)..-1)
1840
+ buffer.force_encoding(orig_encoding)
1841
+ end
1842
+ end
1356
1843
 
1357
- bytes_written = 0
1358
- if sync then
1359
- # Flush the internal write buffer and then bypass it when in synchronous
1360
- # mode.
1361
- __io_like__buffered_flush
1362
- bytes_written = unbuffered_write(string)
1363
- else
1364
- if __io_like__internal_write_buffer.length + string.length >= flush_size then
1365
- # The tipping point for the write buffer would be surpassed by this
1366
- # request, so flush everything.
1367
- __io_like__buffered_flush
1368
- bytes_written = unbuffered_write(string)
1369
- else
1370
- # The buffer can absorb the entire request.
1371
- __io_like__internal_write_buffer << string
1372
- bytes_written = string.length
1373
- end
1374
- end
1375
- return bytes_written
1844
+ ##
1845
+ # Attempt to perform implicit conversion of `object` to an `Integer`.
1846
+ #
1847
+ # @param object [Object] an object to be converted via its `#to_int` method
1848
+ #
1849
+ # This method exists solely to make the message of any raised `TypeError`
1850
+ # exceptions exactly match what IO methods would produce in order to appease
1851
+ # overly prescriptive rubyspec tests; otherwise, the `Integer()` method would
1852
+ # suffice.
1853
+ #
1854
+ # @return [Integer] the converted value
1855
+ #
1856
+ # @raise [TypeError] when conversion fails
1857
+ def ensure_integer(object)
1858
+ object.to_int
1859
+ rescue NoMethodError
1860
+ if nil == object
1861
+ raise TypeError, 'no implicit conversion from nil to integer'
1376
1862
  end
1377
1863
 
1378
- # Returns a reference to the internal read buffer.
1379
- def __io_like__internal_read_buffer
1380
- @__io_like__read_buffer ||= ''
1864
+ name = case object
1865
+ when nil
1866
+ 'nil'
1867
+ when false
1868
+ 'false'
1869
+ when true
1870
+ 'true'
1871
+ else
1872
+ class_name_of(object)
1873
+ end
1874
+ raise TypeError,
1875
+ 'no implicit conversion of %s into Integer' % [name]
1876
+ end
1877
+
1878
+ ##
1879
+ # Attempt to perform implicit conversion of `object` to a `String`.
1880
+ #
1881
+ # @param object [Object] an object to be converted via its `#to_str` method
1882
+ #
1883
+ # This method exists because no other method is strict about implicit string
1884
+ # conversion while also raising `TypeError` for failures. For instance,
1885
+ # `String.new` first calls `#to_str` on its argument but then falls back to
1886
+ # `#to_s` if the former call fails. Some rubyspec tests require a `TypeError`
1887
+ # to be raised when implicit conversion fails, and directly calling `#to_str`
1888
+ # raises NoMethodError if the method does not exist.
1889
+ #
1890
+ # @return [String] the converted value
1891
+ #
1892
+ # @raise [TypeError] when conversion fails
1893
+ def ensure_string(object)
1894
+ begin
1895
+ result = object.to_str
1896
+ return result if String === result
1897
+ rescue NoMethodError
1898
+ name = case object
1899
+ when nil
1900
+ 'nil'
1901
+ when false
1902
+ 'false'
1903
+ when true
1904
+ 'true'
1905
+ else
1906
+ class_name_of(object)
1907
+ end
1908
+ raise TypeError,
1909
+ 'no implicit conversion of %s into String' % [name]
1381
1910
  end
1382
1911
 
1383
- # Returns a reference to the internal write buffer.
1384
- def __io_like__internal_write_buffer
1385
- @__io_like__write_buffer ||= ''
1912
+ object_class = class_name_of(object)
1913
+ result_class = class_name_of(result)
1914
+ raise TypeError,
1915
+ 'can\'t convert %s to String (%s#to_str gives %s)' %
1916
+ [object_class, object_class, result_class]
1917
+ end
1918
+
1919
+ ##
1920
+ # Write `item` followed by the record separator. Recursively process
1921
+ # elements of `item` if it responds to `#to_ary` with a non-`nil` result.
1922
+ #
1923
+ # @param item [Object] the item to be `String`-ified and written to the stream
1924
+ # @param seen [Array] a list of `Objects` that have already been printed
1925
+ #
1926
+ # @return [nil]
1927
+ def flatten_puts(item, seen = [])
1928
+ if seen.include?(item.__id__)
1929
+ write('[...]')
1930
+ write(ORS)
1931
+ return
1386
1932
  end
1387
1933
 
1388
- # Returns +true+ if this object has been closed for reading; otherwise,
1389
- # returns +false+.
1390
- def __io_like__closed_read?
1391
- @__io_like__closed_read ||= false
1934
+ seen.push(item.__id__)
1935
+
1936
+ array = item.to_ary rescue nil
1937
+ if nil == array
1938
+ string = item.to_s
1939
+ unless String === string
1940
+ # This ensures that #inspect-like output is generated even if item
1941
+ # implements its own #inspect implementation or does not implement it at
1942
+ # all, such as for BasicObject descendants.
1943
+ string =
1944
+ Kernel.instance_method(:inspect).bind(item).call.sub(%r{ .*}, '>')
1945
+ end
1946
+ write(string)
1947
+ write(ORS) unless string.end_with?(ORS)
1948
+ else
1949
+ array.each { |i| flatten_puts(i, seen) }
1392
1950
  end
1393
1951
 
1394
- # Arranges for #__io_like__closed_read? to return +true+.
1395
- def __io_like__close_read
1396
- @__io_like__closed_read = true
1397
- nil
1952
+ seen.pop
1953
+
1954
+ nil
1955
+ end
1956
+
1957
+ ##
1958
+ # Converts non-blocking responses into exceptions if requested.
1959
+ #
1960
+ # @param type [:wait_readable, :wait_writable] the type of non-blocking
1961
+ # response
1962
+ # @param exception [Boolean] if `true`, raise an exception for `type`
1963
+ #
1964
+ # @return [:wait_readable, :wait_writable] if `exception` is `false`
1965
+ def nonblock_response(type, exception)
1966
+ case type
1967
+ when :wait_readable
1968
+ return type unless exception
1969
+ raise IO::EWOULDBLOCKWaitReadable
1970
+ when :wait_writable
1971
+ return type unless exception
1972
+ raise IO::EWOULDBLOCKWaitWritable
1973
+ else
1974
+ raise ArgumentError, "Invalid type: #{type}"
1398
1975
  end
1976
+ end
1399
1977
 
1400
- # Returns +true+ if this object has been closed for writing; otherwise,
1401
- # returns +false+.
1402
- def __io_like__closed_write?
1403
- @__io_like__closed_write ||= false
1978
+ ##
1979
+ # Parses the positional arguments for #readline, #gets, and related methods.
1980
+ #
1981
+ # @return [[String, Integer]] an array containing the separator string and the
1982
+ # maximum length
1983
+ def parse_readline_args(*args)
1984
+ if args.size > 2
1985
+ raise ArgumentError,
1986
+ "wrong number of arguments (given #{args.size}, expected 0..2)"
1987
+ elsif args.size == 2
1988
+ sep_string = nil == args[0] ? nil : ensure_string(args[0])
1989
+ limit = nil == args[1] ? nil : ensure_integer(args[1])
1990
+ elsif args.size == 1
1991
+ begin
1992
+ sep_string = nil == args[0] ? nil : ensure_string(args[0])
1993
+ limit = nil
1994
+ rescue TypeError
1995
+ limit = ensure_integer(args[0])
1996
+ sep_string = $/
1997
+ end
1998
+ else
1999
+ sep_string = $/
2000
+ limit = nil
1404
2001
  end
1405
2002
 
1406
- # Arranges for #__io_like__closed_write? to return +true+.
1407
- def __io_like__close_write
1408
- @__io_like__closed_write = true
1409
- nil
2003
+ return [sep_string, limit]
2004
+ end
2005
+
2006
+ ##
2007
+ # Reads and returns up to `length` bytes from this stream.
2008
+ #
2009
+ # This method always blocks, even when the stream is in non-blocking mode. An
2010
+ # empty `String` is returned if reading begins at the end of the stream.
2011
+ #
2012
+ # @param length [Integer] the number of bytes to read
2013
+ #
2014
+ # @return [String] up to `length` bytes read from this stream
2015
+ def read_bytes(length)
2016
+ buffer = ''.b
2017
+
2018
+ if delegate_r.buffered_io.read_buffer_empty?
2019
+ # Flush any pending write data in the buffered IO in preparation for
2020
+ # reading directly from the delegate behind it.
2021
+ delegate_r.buffered_io.flush
2022
+ else
2023
+ buffer << delegate_r.buffered_io.read(length)
2024
+ length -= buffer.bytesize
1410
2025
  end
1411
2026
 
1412
- # This method joins the elements of _array_ together with _separator_
1413
- # between each element and returns the result. _seen_ is a list of object
1414
- # IDs representing arrays which have already started processing.
1415
- #
1416
- # This method exists only because Array#join apparently behaves in an
1417
- # implementation dependent manner when joining recursive arrays and so does
1418
- # not always produce the expected results. Specifically, MRI 1.8.6 and
1419
- # 1.8.7 behave as follows:
1420
- #
1421
- # x = []
1422
- # x << 1 << x << 2
1423
- # x.join(', ') => "1, 1, [...], 2, 2"
1424
- #
1425
- # The expected and necessary result for use with #puts is:
1426
- #
1427
- # "1, [...], 2"
1428
- #
1429
- # Things get progressively worse as the nesting and recursion become more
1430
- # convoluted.
1431
- def __io_like__array_join(array, separator, seen = [])
1432
- seen.push(array.object_id)
1433
- need_separator = false
1434
- result = array.inject('') do |memo, item|
1435
- memo << separator if need_separator
1436
- need_separator = true
1437
-
1438
- memo << if item.kind_of?(Array) then
1439
- if seen.include?(item.object_id) then
1440
- '[...]'
1441
- else
1442
- __io_like__array_join(item, separator, seen)
1443
- end
1444
- else
1445
- item.to_s
1446
- end
2027
+ begin
2028
+ while length > 0 do
2029
+ bytes = delegate_r.blocking_io.read(length)
2030
+ length -= bytes.bytesize
2031
+ buffer << bytes
1447
2032
  end
1448
- seen.pop
2033
+ rescue EOFError
2034
+ end
1449
2035
 
1450
- result
2036
+ buffer
2037
+ end
2038
+
2039
+ ##
2040
+ # @return [Symbol] a `Symbol` equivalent to the given `mode` for Ruby 2.7 and
2041
+ # lower for use with `#wait`
2042
+ def wait_event_from_symbol(mode)
2043
+ case mode
2044
+ when :r, :read, :readable
2045
+ IO::READABLE
2046
+ when :w, :write, :writable
2047
+ IO::WRITABLE
2048
+ when :rw, :read_write, :readable_writable
2049
+ IO::READABLE || IO::WRITABLE
2050
+ else
2051
+ raise ArgumentError, "unsupported mode: #{mode}"
1451
2052
  end
1452
2053
  end
1453
2054
  end
2055
+ end
1454
2056
 
1455
2057
  # vim: ts=2 sw=2 et