Stream
that can be read fromcore :: Reader :: append_line_to
Read a string until the end of the line and append it tos
.
core :: Reader :: defaultinit
core :: Reader :: deserialize_msgpack
Deserialize full Nitnullable Object
from MessagePack formated data
core :: Reader :: raw_read_byte
Read a byte directly from the underlying stream, withoutcore :: Reader :: raw_read_bytes
Read at mostmax
bytes from the underlying stream into buf
,
core :: Reader :: read_block
Read the length as a 64 bits integer, then the content of the blockcore :: Reader :: read_bytes_to_cstring
Reads up tomax
bytes from source and stores them in bytes
core :: Reader :: read_double
Read a floating point on 64 bits and return it as aFloat
core :: Reader :: read_float
Read a floating point on 32 bits and return it as aFloat
core :: Reader :: read_int64
Read a signed integer on 64 bits and return is anInt
core :: Reader :: read_msgpack
Read the next MessagePack object and return it as a simple Nit objectcore :: Reader :: read_nonwhitespace
Skip whitespace characters (if any) then return the following non-whitespace character.core :: Reader :: append_line_to
Read a string until the end of the line and append it tos
.
binary :: BinaryStream :: big_endian
Use the big-endian convention? otherwise use little-endian.binary :: BinaryStream :: big_endian=
Use the big-endian convention? otherwise use little-endian.core :: Object :: class_factory
Implementation used byget_class
to create the specific class.
core :: Stream :: defaultinit
binary :: BinaryStream :: defaultinit
core :: Reader :: defaultinit
core :: Object :: defaultinit
core :: Reader :: deserialize_msgpack
Deserialize full Nitnullable Object
from MessagePack formated data
core :: Object :: is_same_instance
Return true ifself
and other
are the same instance (i.e. same identity).
core :: Object :: is_same_serialized
Isself
the same as other
in a serialization context?
core :: Object :: is_same_type
Return true ifself
and other
have the same dynamic type.
core :: Stream :: last_error=
Error produced by the file streamcore :: Stream :: lookahead=
Lookahead buffer for codecscore :: Stream :: lookahead_capacity
Capacity of the lookaheadcore :: Stream :: lookahead_capacity=
Capacity of the lookaheadcore :: Stream :: lookahead_length
Current occupation of the lookaheadcore :: Stream :: lookahead_length=
Current occupation of the lookaheadcore :: Object :: output_class_name
Display class name on stdout (debug only).core :: Reader :: raw_read_byte
Read a byte directly from the underlying stream, withoutcore :: Reader :: raw_read_bytes
Read at mostmax
bytes from the underlying stream into buf
,
core :: Reader :: read_block
Read the length as a 64 bits integer, then the content of the blockcore :: Reader :: read_bytes_to_cstring
Reads up tomax
bytes from source and stores them in bytes
core :: Reader :: read_double
Read a floating point on 64 bits and return it as aFloat
core :: Reader :: read_float
Read a floating point on 32 bits and return it as aFloat
core :: Reader :: read_int64
Read a signed integer on 64 bits and return is anInt
core :: Reader :: read_msgpack
Read the next MessagePack object and return it as a simple Nit objectcore :: Reader :: read_nonwhitespace
Skip whitespace characters (if any) then return the following non-whitespace character.core :: Stream :: write_buffer
Buffer for writing data to a streamcore :: Stream :: write_buffer=
Buffer for writing data to a streamReader
capable of declaring if readable without blocking
Process
on which stdout can be read and stdin can be written to like a Duplex
# A `Stream` that can be read from
abstract class Reader
super Stream
# Read a byte directly from the underlying stream, without
# considering any eventual buffer
protected fun raw_read_byte: Int is abstract
# Read at most `max` bytes from the underlying stream into `buf`,
# without considering any eventual buffer
#
# Returns how many bytes were read
protected fun raw_read_bytes(buf: CString, max: Int): Int do
var rd = 0
for i in [0 .. max[ do
var b = raw_read_byte
if b < 0 then break
buf[i] = b
rd += 1
end
return rd
end
# Reads a character. Returns `null` on EOF or timeout
#
# Returns unicode replacement character '�' if an
# invalid byte sequence is read.
#
# `read_char` may block if:
#
# * No byte could be read from the current buffer
# * An incomplete char is partially read, and more bytes are
# required for full decoding.
fun read_char: nullable Char do
if eof then return null
var cod = codec
var codet_sz = cod.codet_size
var lk = lookahead
var llen = lookahead_length
if llen < codet_sz then
llen += raw_read_bytes(lk.fast_cstring(llen), codet_sz - llen)
end
if llen < codet_sz then
lookahead_length = 0
return 0xFFFD.code_point
end
var ret = cod.is_valid_char(lk, codet_sz)
var max_llen = cod.max_lookahead
while ret == 1 and llen < max_llen do
var rd = raw_read_bytes(lk.fast_cstring(llen), codet_sz)
if rd < codet_sz then
llen -= codet_sz
if llen > 0 then
lookahead.lshift(codet_sz, llen, codet_sz)
end
lookahead_length = llen.max(0)
return 0xFFFD.code_point
end
llen += codet_sz
ret = cod.is_valid_char(lk, llen)
end
if ret == 0 then
var c = cod.decode_char(lk)
var clen = c.u8char_len
llen -= clen
if llen > 0 then
lookahead.lshift(clen, llen, clen)
end
lookahead_length = llen
return c
end
if ret == 2 or ret == 1 then
llen -= codet_sz
if llen > 0 then
lookahead.lshift(codet_sz, llen, codet_sz)
end
lookahead_length = llen
return 0xFFFD.code_point
end
# Should not happen if the decoder works properly
var arr = new Array[Object]
arr.push "Decoder error: could not decode nor recover from byte sequence ["
for i in [0 .. llen[ do
arr.push lk[i]
arr.push ", "
end
arr.push "]"
var err = new IOError(arr.plain_to_s)
err.cause = last_error
last_error = err
return 0xFFFD.code_point
end
# Reads a byte. Returns a negative value on error
fun read_byte: Int do
var llen = lookahead_length
if llen == 0 then return raw_read_byte
var lk = lookahead
var b = lk[0].to_i
if llen == 1 then
lookahead_length = 0
else
lk.lshift(1, llen - 1, 1)
lookahead_length -= 1
end
return b
end
# Reads a String of at most `i` length
fun read(i: Int): String do
assert i >= 0
var cs = new CString(i)
var rd = read_bytes_to_cstring(cs, i)
if rd < 0 then return ""
return codec.decode_string(cs, rd)
end
# Reads up to `max` bytes from source
fun read_bytes(max: Int): Bytes do
assert max >= 0
var cs = new CString(max)
var rd = read_bytes_to_cstring(cs, max)
return new Bytes(cs, rd, max)
end
# Reads up to `max` bytes from source and stores them in `bytes`
fun read_bytes_to_cstring(bytes: CString, max: Int): Int do
var llen = lookahead_length
if llen == 0 then return raw_read_bytes(bytes, max)
var rd = max.min(llen)
var lk = lookahead
lk.copy_to(bytes, rd, 0, 0)
if rd < llen then
lk.lshift(rd, llen - rd, rd)
lookahead_length -= rd
else
lookahead_length = 0
end
return rd + raw_read_bytes(bytes.fast_cstring(rd), max - rd)
end
# Read a string until the end of the line.
#
# The line terminator '\n' and '\r\n', if any, is removed in each line.
#
# ~~~
# var txt = "Hello\n\nWorld\n"
# var i = new StringReader(txt)
# assert i.read_line == "Hello"
# assert i.read_line == ""
# assert i.read_line == "World"
# assert i.eof
# ~~~
#
# Only LINE FEED (`\n`), CARRIAGE RETURN & LINE FEED (`\r\n`), and
# the end or file (EOF) is considered to delimit the end of lines.
# CARRIAGE RETURN (`\r`) alone is not used for the end of line.
#
# ~~~
# var txt2 = "Hello\r\n\n\rWorld"
# var i2 = new StringReader(txt2)
# assert i2.read_line == "Hello"
# assert i2.read_line == ""
# assert i2.read_line == "\rWorld"
# assert i2.eof
# ~~~
#
# NOTE: Use `append_line_to` if the line terminator needs to be preserved.
fun read_line: String
do
if last_error != null then return ""
if eof then return ""
var s = new FlatBuffer
append_line_to(s)
return s.to_s.chomp
end
# Read all the lines until the eof.
#
# The line terminator '\n' and `\r\n` is removed in each line,
#
# ~~~
# var txt = "Hello\n\nWorld\n"
# var i = new StringReader(txt)
# assert i.read_lines == ["Hello", "", "World"]
# ~~~
#
# This method is more efficient that splitting
# the result of `read_all`.
#
# NOTE: SEE `read_line` for details.
fun read_lines: Array[String]
do
var res = new Array[String]
while not eof do
res.add read_line
end
return res
end
# Return an iterator that read each line.
#
# The line terminator '\n' and `\r\n` is removed in each line,
# The line are read with `read_line`. See this method for details.
#
# ~~~
# var txt = "Hello\n\nWorld\n"
# var i = new StringReader(txt)
# assert i.each_line.to_a == ["Hello", "", "World"]
# ~~~
#
# Unlike `read_lines` that read all lines at the call, `each_line` is lazy.
# Therefore, the stream should no be closed until the end of the stream.
#
# ~~~
# i = new StringReader(txt)
# var el = i.each_line
#
# assert el.item == "Hello"
# el.next
# assert el.item == ""
# el.next
#
# i.close
#
# assert not el.is_ok
# # closed before "world" is read
# ~~~
fun each_line: LineIterator do return new LineIterator(self)
# Read all the stream until the eof.
#
# The content of the file is returned as a String.
#
# ~~~
# var txt = "Hello\n\nWorld\n"
# var i = new StringReader(txt)
# assert i.read_all == txt
# ~~~
fun read_all: String do
var s = read_all_bytes
var slen = s.length
if slen == 0 then return ""
return codec.decode_string(s.items, s.length)
end
# Read all the stream until the eof.
#
# The content of the file is returned verbatim.
fun read_all_bytes: Bytes
do
if last_error != null then return new Bytes.empty
var s = new Bytes.empty
var buf = new CString(4096)
while not eof do
var rd = read_bytes_to_cstring(buf, 4096)
s.append_ns(buf, rd)
end
return s
end
# Read a string until the end of the line and append it to `s`.
#
# Unlike `read_line` and other related methods,
# the line terminator '\n', if any, is preserved in each line.
# Use the method `Text::chomp` to safely remove it.
#
# ~~~
# var txt = "Hello\n\nWorld\n"
# var i = new StringReader(txt)
# var b = new FlatBuffer
# i.append_line_to(b)
# assert b == "Hello\n"
# i.append_line_to(b)
# assert b == "Hello\n\n"
# i.append_line_to(b)
# assert b == txt
# assert i.eof
# ~~~
#
# If `\n` is not present at the end of the result, it means that
# a non-eol terminated last line was returned.
#
# ~~~
# var i2 = new StringReader("hello")
# assert not i2.eof
# var b2 = new FlatBuffer
# i2.append_line_to(b2)
# assert b2 == "hello"
# assert i2.eof
# ~~~
#
# NOTE: The single character LINE FEED (`\n`) delimits the end of lines.
# Therefore CARRIAGE RETURN & LINE FEED (`\r\n`) is also recognized.
fun append_line_to(s: Buffer)
do
if last_error != null then return
loop
var x = read_char
if x == null then
if eof then return
else
s.chars.push(x)
if x == '\n' then return
end
end
end
# Is there something to read.
# This function returns 'false' if there is something to read.
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.
#
# Leading whitespace characters are skipped.
# The first whitespace character that follows the result is consumed.
#
# An empty string is returned if the end of the file or an error is encounter.
#
# ~~~
# var w = new StringReader(" Hello, \n\t World!")
# assert w.read_word == "Hello,"
# assert w.read_char == '\n'
# assert w.read_word == "World!"
# assert w.read_word == ""
# ~~~
#
# `Char::is_whitespace` determines what is a whitespace.
fun read_word: String
do
var buf = new FlatBuffer
var c = read_nonwhitespace
if c != null then
buf.add(c)
while not eof do
c = read_char
if c == null then break
if c.is_whitespace then break
buf.add(c)
end
end
var res = buf.to_s
return res
end
# Skip whitespace characters (if any) then return the following non-whitespace character.
#
# Returns the code point of the character.
# Returns `null` on end of file or error.
#
# In fact, this method works like `read_char` except it skips whitespace.
#
# ~~~
# var w = new StringReader(" \nab\tc")
# assert w.read_nonwhitespace == 'a'
# assert w.read_nonwhitespace == 'b'
# assert w.read_nonwhitespace == 'c'
# assert w.read_nonwhitespace == null
# ~~~
#
# `Char::is_whitespace` determines what is a whitespace.
fun read_nonwhitespace: nullable Char
do
var c: nullable Char = null
while not eof do
c = read_char
if c == null or not c.is_whitespace then break
end
return c
end
end
lib/core/stream.nit:108,1--482,3
redef abstract class Reader
super BinaryStream
# Read a single byte and return `true` if its value is different than 0
#
# Returns `false` when an error is pending (`last_error != null`).
fun read_bool: Bool do return read_byte > 0
# Get an `Array` of 8 `Bool` by reading a single byte
#
# To be used with `BinaryWriter::write_bits`.
#
# Returns an array of `false` when an error is pending (`last_error != null`).
fun read_bits: Array[Bool]
do
var int = read_byte
if int < 0 then return new Array[Bool]
var arr = new Array[Bool]
for i in [7 .. 0].step(-1) do
arr.push(((int >> i) & 1) != 0)
end
return arr
end
# Read a null terminated string
#
# To be used with `Writer::write_string`.
#
# Returns a truncated string when an error is pending (`last_error != null`).
fun read_string: String
do
var buf = new Bytes.empty
loop
var byte = read_byte
if byte <= 0 then
return buf.to_s
end
buf.add byte
end
end
# Read the length as a 64 bits integer, then the content of the block
#
# To be used with `Writer::write_block`.
#
# Returns a truncated string when an error is pending (`last_error != null`).
fun read_block: String
do
var length = read_int64
if length == 0 then return ""
return read_bytes(length).to_s
end
# Read a floating point on 32 bits and return it as a `Float`
#
# Using this format may result in a loss of precision as it uses less bits
# than Nit `Float`.
#
# Returns `0.0` when an error is pending (`last_error != null`).
fun read_float: Float
do
if last_error != null then return 0.0
var b0 = read_byte
var b1 = read_byte
var b2 = read_byte
var b3 = read_byte
# Check for error, `last_error` is set by `read_byte`
if b0 < 0 or b1 < 0 or b2 < 0 or b3 < 0 then return 0.0
return native_read_float(b0, b1, b2, b3, big_endian)
end
# Utility for `read_float`
private fun native_read_float(b0, b1, b2, b3: Int, big_endian: Bool): Float `{
union {
unsigned char b[4];
float val;
uint32_t conv;
} u;
u.b[0] = b0;
u.b[1] = b1;
u.b[2] = b2;
u.b[3] = b3;
if (big_endian)
u.conv = be32toh(u.conv);
else u.conv = le32toh(u.conv);
return u.val;
`}
# Read a floating point on 64 bits and return it as a `Float`
#
# Returns `0.0` when an error is pending (`last_error != null`).
fun read_double: Float
do
if last_error != null then return 0.0
var b0 = read_byte
var b1 = read_byte
var b2 = read_byte
var b3 = read_byte
var b4 = read_byte
var b5 = read_byte
var b6 = read_byte
var b7 = read_byte
# Check for error, `last_error` is set by `read_byte`
if b0 < 0 or b1 < 0 or b2 < 0 or b3 < 0 or
b4 < 0 or b5 < 0 or b6 < 0 or b7 < 0 then return 0.0
return native_read_double(b0, b1, b2, b3, b4, b5, b6, b7, big_endian)
end
# Utility for `read_double`
private fun native_read_double(b0, b1, b2, b3, b4, b5, b6, b7: Int, big_endian: Bool): Float `{
union {
unsigned char b[8];
double val;
uint64_t conv;
} u;
u.b[0] = b0;
u.b[1] = b1;
u.b[2] = b2;
u.b[3] = b3;
u.b[4] = b4;
u.b[5] = b5;
u.b[6] = b6;
u.b[7] = b7;
if (big_endian)
u.conv = be64toh(u.conv);
else u.conv = le64toh(u.conv);
return u.val;
`}
# Read a signed integer on 64 bits and return is an `Int`
#
# Using this format may result in a loss of precision as the length of a
# Nit `Int` may be less than 64 bits on some platforms.
#
# Returns `0` when an error is pending (`last_error != null`).
fun read_int64: Int
do
if last_error != null then return 0
var b0 = read_byte
var b1 = read_byte
var b2 = read_byte
var b3 = read_byte
var b4 = read_byte
var b5 = read_byte
var b6 = read_byte
var b7 = read_byte
# Check for error, `last_error` is set by `read_byte`
if b0 < 0 or b1 < 0 or b2 < 0 or b3 < 0 or
b4 < 0 or b5 < 0 or b6 < 0 or b7 < 0 then return 0
return native_read_int64(b0, b1, b2, b3, b4, b5, b6, b7, big_endian)
end
# Utility for `read_int64`
private fun native_read_int64(b0, b1, b2, b3, b4, b5, b6, b7: Int, big_endian: Bool): Int `{
union {
unsigned char b[8];
int64_t val;
uint64_t conv;
} u;
u.b[0] = b0;
u.b[1] = b1;
u.b[2] = b2;
u.b[3] = b3;
u.b[4] = b4;
u.b[5] = b5;
u.b[6] = b6;
u.b[7] = b7;
if (big_endian)
u.conv = be64toh(u.conv);
else u.conv = le64toh(u.conv);
return u.val;
`}
end
lib/binary/binary.nit:150,1--340,3
redef class Reader
# Read the next MessagePack object and return it as a simple Nit object
#
# The return value is composed of:
# * the simple types `null`, `Bool`, `Int`, `Float`, `String` and `Bytes`,
# * collections of simple Nit objects `Array[nullable Serializable]`
# and `Map[nullable Serializable, nullable Serializable]`,
# * and `MsgPackExt` for custom MessagePack *ext* data.
#
# This method reads plain MessagePack data, as written by `MsgPackSerializer`
# when `plain_msgpack == true`. To deserialize full Nit objects from
# MessagePack with metadata use `Reader::deserialize_msgpack`.
fun read_msgpack: nullable Serializable
do
if last_error != null then return 0
var typ = read_byte
if typ < 0 then
# Error, return default `null`
return null
else if typ & 0b1000_0000 == 0 or typ & 0b1110_0000 == 0b1110_0000 then
# fixint
var bytes = new Bytes.with_capacity(1)
bytes.add typ
return bytes.to_i(signed=true)
else if typ & 0b1111_0000 == 0b1000_0000 then
# fixmap
var len = typ & 0b0000_1111
return read_msgpack_map_data(len.to_i)
else if typ & 0b1111_0000 == 0b1001_0000 then
# fixarray
var len = typ & 0b0000_1111
return read_msgpack_array_data(len.to_i)
else if typ & 0b1110_0000 == 0b1010_0000 then
# fixstr
var len = typ & 0b0001_1111
return read_bytes(len.to_i).to_s
else if typ == 0xC0 then
return null
else if typ == 0xC2 then
return false
else if typ == 0xC3 then
return true
else if typ >= 0xCC and typ <= 0xCF then
# uint8, 16, 32 and 64
var len = 1 << (typ - 0xCC)
return read_bytes(len).to_i
else if typ >= 0xD0 and typ <= 0xD3 then
# int8, 16, 32 and 64
var len = 1 << (typ - 0xD0)
return read_bytes(len).to_i(true)
else if typ == 0xCA then
return read_float
else if typ == 0xCB then
return read_double
else if typ >= 0xD9 and typ <= 0xDB then
# str8, 16 and 32
var len_ln = 1 << (typ - 0xD9)
var bf = read_bytes(len_ln)
var len = bf.to_i
if len < 0 then return null
var rd_buf = read_bytes(len)
if rd_buf.length != len then
# Bad formatted message.
return null
end
return rd_buf.to_s
else if typ >= 0xC4 and typ <= 0xC6 then
# bin8, 16 or 32
var len_ln = 1 << (typ - 0xC4)
var bf = read_bytes(len_ln)
var len = bf.to_i
if len < 0 then return null
var rd_buf = read_bytes(len)
if rd_buf.length != len then
# Bad formatted message.
return null
end
return rd_buf
else if typ == 0xDC or typ == 0xDD then
# array16 and array32
var len_ln = 2 << (typ - 0xDC)
var lenbuf = read_bytes(len_ln)
return read_msgpack_array_data(lenbuf.to_i)
else if typ == 0xDE or typ == 0xDF then
# map16 and map32
var len_ln = 2 << (typ - 0xDE)
var lenbuf = read_bytes(len_ln)
return read_msgpack_map_data(lenbuf.to_i)
else if typ == 0xD4 then
# fixext1
return read_msgpack_fixext_data(1)
else if typ == 0xD5 then
# fixext2
return read_msgpack_fixext_data(2)
else if typ == 0xD6 then
# fixext4
return read_msgpack_fixext_data(4)
else if typ == 0xD7 then
# fixext8
return read_msgpack_fixext_data(8)
else if typ == 0xD8 then
# fixext16
return read_msgpack_fixext_data(16)
else if typ == 0xC7 then
# ext1
return read_msgpack_ext_data(1)
else if typ == 0xC8 then
# ext2
return read_msgpack_ext_data(2)
else if typ == 0xC9 then
# ext4
return read_msgpack_ext_data(4)
end
print_error "MessagePack Warning: Found no match for typ {typ.to_base(16)} / 0b{typ.to_base(2)}"
return null
end
# Read the content of a map, `len` keys and values
private fun read_msgpack_map_data(len: Int): Map[nullable Serializable, nullable Serializable]
do
var map = new Map[nullable Serializable, nullable Serializable]
for i in [0..len.to_i[ do map[read_msgpack] = read_msgpack
return map
end
# Read the content of an array of `len` items
private fun read_msgpack_array_data(len: Int): Array[nullable Serializable]
do
return [for i in [0..len[ do read_msgpack]
end
# Read the content of a *fixext* of `len` bytes
#
# ~~~
# var reader = new BytesReader(b"\xC7\x03\x0A\x0B\x0C\x0D")
# var ext = reader.read_msgpack
# assert ext isa MsgPackExt
# assert ext.typ == 0x0a
# assert ext.data == b"\x0B\x0C\x0D"
# ~~~
private fun read_msgpack_fixext_data(len: Int): MsgPackExt
do
var exttyp = read_byte
if exttyp < 0 then exttyp = 0
var data = read_bytes(len)
return new MsgPackExt(exttyp, data)
end
# Read the content of a dynamic *ext* including the length on `len_len` bytes
private fun read_msgpack_ext_data(len_len: Int): MsgPackExt
do
var len = read_bytes(len_len).to_i
return read_msgpack_fixext_data(len)
end
end
lib/msgpack/read.nit:23,1--194,3
redef class Reader
# Deserialize full Nit `nullable Object` from MessagePack formated data
#
# This method use metadata in the MessagePack source to recreate full
# Nit objects serialized by `Writer::serialize_msgpack` or
# `MsgPackSerializer`.
#
# The dynamic type of the deserialized object can be limited to `static_type`.
#
# Warning: Deserialization errors are reported with `print_error`,
# the returned object may be partial or fall back on `null`.
# To handle the errors programmatically, use a `MsgPackDeserializer`.
fun deserialize_msgpack(static_type: nullable String): nullable Object
do
var deserializer = new MsgPackDeserializer(self)
var res = deserializer.deserialize(static_type)
if deserializer.errors.length == 1 then
print_error deserializer.errors.join("")
else if deserializer.errors.not_empty then
print_error "Deserialization Errors:\n* {deserializer.errors.join("\n* ")}"
end
return res
end
end
lib/msgpack/serialization_read.nit:51,1--77,3