for i in [0 .. max[ do
var b = raw_read_byte
if b < 0 then break
- buf[i] = b.to_b
+ buf[i] = b
rd += 1
end
return rd
else
lookahead_length = 0
end
- return rd + raw_read_bytes(bytes, max - rd)
+ return rd + raw_read_bytes(bytes.fast_cstring(rd), max - rd)
end
# Read a string until the end of the line.
# Is there something to read.
# This function returns 'false' if there is something to read.
- fun eof: Bool is abstract
+ fun eof: Bool do
+ if lookahead_length > 0 then return false
+ lookahead_length = raw_read_bytes(lookahead, 1)
+ return lookahead_length <= 0
+ end
# Read the next sequence of non whitespace characters.
#
# Iterator returned by `Reader::each_line`.
# See the aforementioned method for details.
class LineIterator
- super Iterator[String]
+ super CachedIterator[String]
# The original stream
var stream: Reader
- redef fun is_ok
- do
- var res = not stream.eof
- if not res and close_on_finish then stream.close
- return res
- end
-
- redef fun item
+ redef fun next_item
do
- var line = self.line
- if line == null then
- line = stream.read_line
+ if stream.eof then
+ if close_on_finish then stream.close
+ return null
end
- self.line = line
- return line
- end
-
- # The last line read (cache)
- private var line: nullable String = null
-
- redef fun next
- do
- # force the read
- if line == null then item
- # drop the line
- line = null
+ return stream.read_line
end
# Close the stream when the stream is at the EOF.
fun write(s: Text) is abstract
# Write a single byte
- fun write_byte(value: Byte) is abstract
+ fun write_byte(value: Int) is abstract
# Write a single char
fun write_char(c: Char) do
# The specific logic it let to the concrete subclasses
fun write_to(stream: Writer) is abstract
- # Like `write_to` but return a new String (may be quite large)
+ # Like `write_to` but return a new String (may be quite large).
#
- # This funtionality is anectodical, since the point
- # of streamable object to to be efficienlty written to a
- # stream without having to allocate and concatenate strings
+ # This functionality is anecdotal, since the point
+ # of a streamable object is to be efficiently written to a
+ # stream without having to allocate and concatenate strings.
fun write_to_string: String
do
var stream = new StringWriter
write_to(stream)
return stream.to_s
end
+
+ # Like `write_to` but return a new Bytes (may be quite large)
+ #
+ # This functionality is anecdotal, since the point
+ # of a streamable object is to be efficiently written to a
+ # stream without having to allocate and concatenate buffers.
+ #
+ # Nevertheless, you might need this method if you want to know
+ # the byte size of a writable object.
+ fun write_to_bytes: Bytes
+ do
+ var stream = new BytesWriter
+ write_to(stream)
+ return stream.bytes
+ end
end
redef class Bytes
redef fun write_to(stream) do stream.write(self)
end
-# Input streams with a buffered input for efficiency purposes
-abstract class BufferedReader
- super Reader
-
- redef fun raw_read_byte
- do
- if last_error != null then return -1
- if eof then
- last_error = new IOError("Stream has reached eof")
- return -1
- end
- var c = _buffer[_buffer_pos]
- _buffer_pos += 1
- return c.to_i
- end
-
- # Resets the internal buffer
- fun buffer_reset do
- _buffer_length = 0
- _buffer_pos = 0
- end
-
- # Peeks up to `n` bytes in the buffer
- #
- # The operation does not consume the buffer
- #
- # ~~~nitish
- # var x = new FileReader.open("File.txt")
- # assert x.peek(5) == x.read(5)
- # ~~~
- fun peek(i: Int): Bytes do
- if eof then return new Bytes.empty
- var remsp = _buffer_length - _buffer_pos
- if i <= remsp then
- var bf = new Bytes.with_capacity(i)
- bf.append_ns_from(_buffer, i, _buffer_pos)
- return bf
- end
- var bf = new Bytes.with_capacity(i)
- bf.append_ns_from(_buffer, remsp, _buffer_pos)
- _buffer_pos = _buffer_length
- read_intern(i - bf.length, bf)
- remsp = _buffer_length - _buffer_pos
- var full_len = bf.length + remsp
- if full_len > _buffer_capacity then
- var c = _buffer_capacity
- while c < full_len do c = c * 2 + 2
- _buffer_capacity = c
- end
- var nns = new CString(_buffer_capacity)
- bf.items.copy_to(nns, bf.length, 0, 0)
- _buffer.copy_to(nns, remsp, _buffer_pos, bf.length)
- _buffer = nns
- _buffer_pos = 0
- _buffer_length = full_len
- return bf
- end
-
- redef fun read_bytes_to_cstring(buf, i)
- do
- if last_error != null then return 0
- var bbf = new Bytes(buf, 0, i)
- return read_intern(i, bbf)
- end
-
- # Fills `buf` with at most `i` bytes read from `self`
- private fun read_intern(i: Int, buf: Bytes): Int do
- if eof then return 0
- var p = _buffer_pos
- var bufsp = _buffer_length - p
- if bufsp >= i then
- _buffer_pos += i
- buf.append_ns_from(_buffer, i, p)
- return i
- end
- _buffer_pos = _buffer_length
- var readln = _buffer_length - p
- buf.append_ns_from(_buffer, readln, p)
- var rd = read_intern(i - readln, buf)
- return rd + readln
- end
-
- redef fun read_all_bytes
- do
- if last_error != null then return new Bytes.empty
- var s = new Bytes.with_capacity(10)
- var b = _buffer
- while not eof do
- var j = _buffer_pos
- var k = _buffer_length
- var rd_sz = k - j
- s.append_ns_from(b, rd_sz, j)
- _buffer_pos = k
- fill_buffer
- end
- return s
- end
-
- redef fun append_line_to(s)
- do
- var lb = new Bytes.with_capacity(10)
- loop
- # First phase: look for a '\n'
- var i = _buffer_pos
- while i < _buffer_length and _buffer[i] != 0xAu8 do
- i += 1
- end
-
- var eol
- if i < _buffer_length then
- assert _buffer[i] == 0xAu8
- i += 1
- eol = true
- else
- eol = false
- end
-
- # if there is something to append
- if i > _buffer_pos then
- # Copy from the buffer to the string
- var j = _buffer_pos
- while j < i do
- lb.add(_buffer[j])
- j += 1
- end
- _buffer_pos = i
- else
- assert end_reached
- s.append lb.to_s
- return
- end
-
- if eol then
- # so \n is found
- s.append lb.to_s
- return
- else
- # so \n is not found
- if end_reached then
- s.append lb.to_s
- return
- end
- fill_buffer
- end
- end
- end
-
- redef fun eof
- do
- if _buffer_pos < _buffer_length then return false
- if end_reached then return true
- fill_buffer
- return _buffer_pos >= _buffer_length and end_reached
- end
-
- # The buffer
- private var buffer: CString = new CString(0)
-
- # The current position in the buffer
- private var buffer_pos = 0
-
- # Length of the current buffer (i.e. nuber of bytes in the buffer)
- private var buffer_length = 0
-
- # Capacity of the buffer
- private var buffer_capacity = 0
-
- # Fill the buffer
- protected fun fill_buffer is abstract
-
- # Has the last fill_buffer reached the end
- protected fun end_reached: Bool is abstract
-
- # Allocate a `_buffer` for a given `capacity`.
- protected fun prepare_buffer(capacity: Int)
- do
- _buffer = new CString(capacity)
- _buffer_pos = 0 # need to read
- _buffer_length = 0
- _buffer_capacity = capacity
- end
-end
-
# A `Stream` that can be written to and read from
abstract class Duplex
super Reader
#
# writer.write "Strings "
# writer.write_char '&'
-# writer.write_byte 0x20u8
+# writer.write_byte 0x20
# writer.write_bytes "bytes".to_bytes
#
# assert writer.to_s == "\\x53\\x74\\x72\\x69\\x6E\\x67\\x73\\x20\\x26\\x20\\x62\\x79\\x74\\x65\\x73"
# writer = new BytesWriter
#
# # Write just the character first half
-# writer.write_byte 0xC2u8
+# writer.write_byte 0xC2
# assert writer.to_s == "\\xC2"
# assert writer.bytes.to_s == "�"
#
# # Complete the character
-# writer.write_byte 0xA2u8
+# writer.write_byte 0xA2
# assert writer.to_s == "\\xC2\\xA2"
# assert writer.bytes.to_s == "¢"
# ~~~
#
# writer.write "Strings "
# writer.write_char '&'
-# writer.write_byte 0x20u8
+# writer.write_byte 0x20
# writer.write_bytes "bytes".to_bytes
#
# assert writer.to_s == "Strings & bytes"