Use `auto_serialize` in the calculator example, and remove the old custom JSON serialization.
The `a_star` module needed special care and could not rely only on `auto_serialize`. This was to avoid serializing graphs as a deep tree, instead we serialize the nodes first, then the links and we rebuild the graph at deserialization.
Besides that, more collections support the serialization.
Pull-Request: #1364
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
self.path = path
var file = sys.files[path]
prepare_buffer(file.length)
- _buffer.append(file)
+ path.copy_to_native(_buffer, file.length, 0, 0)
end
redef fun close
redef fun fill_buffer
do
- _buffer.clear
+ buffer_reset
end_reached = true
end
# =============== Bitmap header ================
for x in [0..13] do
- bitmap_header[x] = fileReader.read(1)[0].ascii
+ var b = fileReader.read_byte
+ if b == null then
+ return
+ end
+ bitmap_header[x] = b
end
self.file_size = get_value(bitmap_header.subarray(2, 4))
self.data_offset = get_value(bitmap_header.subarray(10, 4))
# =============== DIB header ================
for x in [0..39] do
- dib_header[x] = fileReader.read(1)[0].ascii
+ var b = fileReader.read_byte
+ if b == null then return
+ dib_header[x] = b
end
var dib_size = get_value(dib_header.subarray(0, 4))
# only support BITMAPINFOHEADER
var row = new Array[Int].with_capacity(self.width)
for y in [0..self.width[
do
- var red = fileReader.read(1)[0].ascii * 256 * 256
- var green = fileReader.read(1)[0].ascii * 256
- var blue = fileReader.read(1)[0].ascii
+ var bts = fileReader.read_bytes(3)
+ if bts.length != 3 then return
+ var red = bts[0] << 16
+ var green = bts[1] << 8
+ var blue = bts[2]
row.add(red + green + blue)
end
self.data.add(row)
fileReader.close
end #end of load_from_file method
- # Reads in a series of bytes from the FileReader when loading a Bitmap from a file
- # FileReader.read(1) is used due to https://github.com/privat/nit/issues/1264
- private fun read_chars(fr: FileReader, howMany: Int): String
- do
- var s = ""
- for x in [1..howMany]
- do
- s += fr.read(1)
- end
- return s
- end
-
# Converts the value contained in two or four bytes into an Int. Since Nit
# does not have a byte type, Int is used
private fun get_value(array: Array[Int]): Int
# fill_buffer now checks for a message in the message queue which is filled by user inputs.
redef fun fill_buffer
do
- _buffer.clear
_buffer_pos = 0
- _buffer.append check_message.to_s
+ var nns = check_message
+ var nslen = nns.cstring_length
+ _buffer_length = nslen
+ nns.copy_to(buffer, nslen, 0, 0)
end
end
# Creates a socket connection to host `host` on port `port`
init connect(host: String, port: Int)
do
- _buffer = new FlatBuffer
+ _buffer = new NativeString(1024)
_buffer_pos = 0
socket = new NativeSocket.socket(new NativeSocketAddressFamilies.af_inet,
new NativeSocketTypes.sock_stream, new NativeSocketProtocolFamilies.pf_null)
# Creates a client socket, this is meant to be used by accept only
private init server_side(h: SocketAcceptResult)
do
- _buffer = new FlatBuffer
+ _buffer = new NativeString(1024)
_buffer_pos = 0
socket = h.socket
addrin = h.addr_in
# timeout : Time in milliseconds before stopping to wait for events
fun ready_to_read(timeout: Int): Bool
do
- if _buffer_pos < _buffer.length then return true
+ if _buffer_pos < _buffer_length then return true
if end_reached then return false
var events = [new NativeSocketPollValues.pollin]
return pollin(events, timeout).length != 0
socket.write_byte value
end
+ redef fun write_bytes(s) do
+ if closed then return
+ socket.write(s.to_s)
+ end
+
fun write_ln(msg: Text)
do
if end_reached then return
redef fun fill_buffer
do
- _buffer.clear
+ _buffer_length = 0
_buffer_pos = 0
if not connected then return
var read = socket.read
close
end_reached = true
end
- _buffer.append(read)
+ enlarge(_buffer_capacity + read.length)
+ read.copy_to_native(_buffer, read.length, 0, 0)
+ _buffer_length = read.length
+ end
+
+ fun enlarge(len: Int) do
+ if _buffer_capacity >= len then return
+ while _buffer_capacity < len do _buffer_capacity *= 2
+ var ns = new NativeString(_buffer_capacity)
+ _buffer.copy_to(ns, _buffer_length - _buffer_pos, _buffer_pos, 0)
+ _buffer = ns
end
redef fun close
return write(*recv, &value, 1);
`}
- fun read: String import NativeString.to_s_with_length `{
+ fun read: String import NativeString.to_s_with_length, NativeString.to_s_with_copy `{
static char c[1024];
- int n = read(*recv, c, 1024);
+ int n = read(*recv, c, 1023);
if(n < 0) {
return NativeString_to_s_with_length("",0);
}
- char* ret = malloc(n + 1);
- memcpy(ret, c, n);
- ret[n] = '\0';
- return NativeString_to_s_with_length(ret, n);
+ c[n] = 0;
+ return NativeString_to_s_with_copy(c);
`}
# Sets an option for the socket
return
end
end_reached = false
- _buffer_pos = 0
- _buffer.clear
+ buffer_reset
end
redef fun close
do
super
- _buffer.clear
+ buffer_reset
end_reached = true
end
redef fun fill_buffer
do
- var nb = _file.io_read(_buffer.items, _buffer.capacity)
+ var nb = _file.io_read(_buffer, _buffer_capacity)
if nb <= 0 then
end_reached = true
nb = 0
end
- _buffer.length = nb
+ _buffer_length = nb
_buffer_pos = 0
end
super FileStream
super Writer
+ redef fun write_bytes(s) do
+ if last_error != null then return
+ if not _is_writable then
+ last_error = new IOError("cannot write to non-writable stream")
+ return
+ end
+ write_native(s.items, s.length)
+ end
+
redef fun write(s)
do
if last_error != null then return
if not _is_writable then
- last_error = new IOError("Cannot write to non-writable stream")
+ last_error = new IOError("cannot write to non-writable stream")
return
end
- if s isa FlatText then
- write_native(s.to_cstring, s.length)
- else
- for i in s.substrings do write_native(i.to_cstring, i.length)
- end
+ for i in s.substrings do write_native(i.to_cstring, i.length)
end
redef fun write_byte(value)
# ~~~
#
# See `Reader::read_all` for details.
- fun read_all: String
+ fun read_all: String do return read_all_bytes.to_s
+
+ fun read_all_bytes: Bytes
do
var s = open_ro
- var res = s.read_all
+ var res = s.read_all_bytes
s.close
return res
end
intrude import ropes
import error
+intrude import bytes
in "C" `{
#include <unistd.h>
# Reads a byte. Returns `null` on EOF or timeout
fun read_byte: nullable Int is abstract
+ # Reads a String of at most `i` length
+ fun read(i: Int): String do return read_bytes(i).to_s
+
# Read at most i bytes
- fun read(i: Int): String
+ fun read_bytes(i: Int): Bytes
do
- if last_error != null then return ""
- var s = new FlatBuffer.with_capacity(i)
+ if last_error != null then return new Bytes.empty
+ var s = new NativeString(i)
+ var buf = new Bytes(s, 0, 0)
while i > 0 and not eof do
- var c = read_char
+ var c = read_byte
if c != null then
- s.add(c)
+ buf.add c
i -= 1
end
end
- return s.to_s
+ return buf
end
# Read a string until the end of the line.
# Read all the stream until the eof.
#
- # The content of the file is returned verbatim.
+ # 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
+ fun read_all: String do return read_all_bytes.to_s
+
+ # 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 ""
- var s = new FlatBuffer
+ if last_error != null then return new Bytes.empty
+ var s = new Bytes.empty
while not eof do
- var c = read_char
+ var c = read_byte
if c != null then s.add(c)
end
- return s.to_s
+ return s
end
# Read a string until the end of the line and append it to `s`.
# A `Stream` that can be written to
abstract class Writer
super Stream
+
+ # Writes bytes from `s`
+ fun write_bytes(s: Bytes) is abstract
+
# write a string
fun write(s: Text) is abstract
# Like `write_to` but return a new String (may be quite large)
#
- # This funtionnality is anectodical, since the point
+ # 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
fun write_to_string: String
return c
end
- # Peeks up to `n` bytes in the buffer, returns an empty string on EOF
+ 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("File.txt")
- # assert x.peek(5) == x.read(5)
+ # var x = new FileReader.open("File.txt")
+ # assert x.peek(5) == x.read(5)
# ~~~
- fun peek(i: Int): String do
- if eof then return ""
- var b = new FlatBuffer.with_capacity(i)
- while i > 0 and not eof do
- b.add _buffer[_buffer_pos]
- _buffer_pos += 1
- i -= 1
+ 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 nbuflen = b.length + (_buffer.length - _buffer_pos)
- var nbuf = new FlatBuffer.with_capacity(nbuflen)
- nbuf.append(b)
- while _buffer_pos < _buffer.length do
- nbuf.add(_buffer[_buffer_pos])
- _buffer_pos += 1
+ 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 NativeString(_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 = nbuf
- return b.to_s
+ _buffer_length = full_len
+ return bf
end
- redef fun read(i)
+ redef fun read_bytes(i)
do
- if last_error != null then return ""
- if eof then return ""
+ if last_error != null then return new Bytes.empty
+ var buf = new Bytes.with_capacity(i)
+ read_intern(i, buf)
+ return buf
+ 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
+ var bufsp = _buffer_length - p
if bufsp >= i then
_buffer_pos += i
- return _buffer.substring(p, i).to_s
+ buf.append_ns_from(_buffer, i, p)
+ return i
end
- _buffer_pos = _buffer.length
- var readln = _buffer.length - p
- var s = _buffer.substring(p, readln).to_s
- fill_buffer
- return s + read(i - readln)
+ _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
+ redef fun read_all_bytes
do
- if last_error != null then return ""
- var s = new FlatBuffer
+ if last_error != null then return new Bytes.empty
+ var s = new Bytes.with_capacity(10)
while not eof do
var j = _buffer_pos
- var k = _buffer.length
+ var k = _buffer_length
while j < k do
- s.add(_buffer[j])
+ s.add(_buffer[j].ascii)
j += 1
end
_buffer_pos = j
fill_buffer
end
- return s.to_s
+ return s
end
redef fun append_line_to(s)
loop
# First phase: look for a '\n'
var i = _buffer_pos
- while i < _buffer.length and _buffer[i] != '\n' do i += 1
+ while i < _buffer_length and _buffer[i] != '\n' do
+ i += 1
+ end
var eol
- if i < _buffer.length then
+ if i < _buffer_length then
assert _buffer[i] == '\n'
i += 1
eol = true
redef fun eof
do
- if _buffer_pos < _buffer.length then return false
+ if _buffer_pos < _buffer_length then return false
if end_reached then return true
fill_buffer
- return _buffer_pos >= _buffer.length and end_reached
+ return _buffer_pos >= _buffer_length and end_reached
end
# The buffer
- private var buffer: nullable FlatBuffer = null
+ private var buffer: NativeString = new NativeString(0)
# The current position in the buffer
- private var buffer_pos: Int = 0
+ 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
- # Is the last fill_buffer reach the end
+ # 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 FlatBuffer.with_capacity(capacity)
+ _buffer = new NativeString(capacity)
_buffer_pos = 0 # need to read
+ _buffer_length = 0
+ _buffer_capacity = capacity
end
end
private var content = new Array[String]
redef fun to_s do return content.to_s
redef fun is_writable do return not closed
+
+ redef fun write_bytes(b) do
+ content.add(b.to_s)
+ end
+
redef fun write(str)
do
assert not closed
source = ""
end
- redef fun read_all do
- var c = cursor
- cursor = source.length
- if c == 0 then return source
- return source.substring_from(c)
+ redef fun read_all_bytes do
+ var nslen = source.length - cursor
+ var nns = new NativeString(nslen)
+ source.copy_to_native(nns, nslen, cursor, 0)
+ return new Bytes(nns, nslen, nslen)
end
redef fun eof do return cursor >= source.length
import base64
intrude import standard::stream
+intrude import standard::bytes
# Websocket compatible listener
#
super TCPStream
init do
- _buffer = new FlatBuffer
+ _buffer = new NativeString(1024)
_buffer_pos = 0
+ _buffer_capacity = 1024
+ _buffer_length = 0
var headers = parse_handshake
var resp = handshake_response(headers)
end
# Frames a text message to be sent to a client
- private fun frame_message(msg: String): String
+ private fun frame_message(msg: String): Bytes
do
- var ans_buffer = new FlatBuffer
+ var ans_buffer = new Bytes.with_capacity(msg.length)
# Flag for final frame set to 1
# opcode set to 1 (for text)
- ans_buffer.add(129.ascii)
+ ans_buffer.add(129)
if msg.length < 126 then
- ans_buffer.add(msg.length.ascii)
+ ans_buffer.add(msg.length)
end
if msg.length >= 126 and msg.length <= 65535 then
- ans_buffer.add(126.ascii)
- ans_buffer.add(msg.length.rshift(8).ascii)
- ans_buffer.add(msg.length.ascii)
+ ans_buffer.add(126)
+ ans_buffer.add(msg.length.rshift(8))
+ ans_buffer.add(msg.length)
end
- ans_buffer.append(msg)
- return ans_buffer.to_s
+ if msg isa FlatString then
+ ans_buffer.append_ns_from(msg.items, msg.length, msg.index_from)
+ else
+ for i in msg.substrings do
+ ans_buffer.append_ns_from(i.as(FlatString).items, i.length, i.as(FlatString).index_from)
+ end
+ end
+ return ans_buffer
end
# Reads an HTTP frame
# Gets the message from the client, unpads it and reconstitutes the message
private fun unpad_message do
var fin = false
+ var bf = new Bytes.empty
while not fin do
var fst_byte = client.read_byte
var snd_byte = client.read_byte
if fin_flag != 0 then fin = true
var opcode = fst_byte.bin_and(15)
if opcode == 9 then
- _buffer.add(138.ascii)
- _buffer.add('\0')
- client.write(_buffer.to_s)
- _buffer_pos += 2
+ bf.add(138)
+ bf.add(0)
+ client.write(bf.to_s)
+ _buffer_pos = _buffer_length
return
end
if opcode == 8 then
var len = snd_byte.bin_and(127)
var payload_ext_len = 0
if len == 126 then
- var tmp = client.read(2)
+ var tmp = client.read_bytes(2)
if tmp.length != 2 then
last_error = new IOError("Error: received interrupted frame")
client.close
return
end
- payload_ext_len = tmp[1].ascii + tmp[0].ascii.lshift(8)
+ payload_ext_len = tmp[1] + tmp[0].lshift(8)
else if len == 127 then
# 64 bits for length are not supported,
# only the last 32 will be interpreted as a Nit Integer
- var tmp = client.read(8)
+ var tmp = client.read_bytes(8)
if tmp.length != 8 then
last_error = new IOError("Error: received interrupted frame")
client.close
return
end
for pos in [0 .. tmp.length[ do
- var i = tmp[pos].ascii
+ var i = tmp[pos]
payload_ext_len += i.lshift(8 * (7 - pos))
end
end
if mask_flag != 0 then
+ var mask = client.read_bytes(4).items
if payload_ext_len != 0 then
- var msg = client.read(payload_ext_len+4)
- var mask = msg.substring(0,4)
- _buffer.append(unmask_message(mask, msg.substring(4, payload_ext_len)))
- else
- if len == 0 then
- return
- end
- var msg = client.read(len+4)
- var mask = msg.substring(0,4)
- _buffer.append(unmask_message(mask, msg.substring(4, len)))
+ len = payload_ext_len
end
+ var msg = client.read_bytes(len).items
+ bf.append_ns(unmask_message(mask, msg, len), len)
end
end
+ _buffer = bf.items
+ _buffer_length = bf.length
end
# Unmasks a message sent by a client
- private fun unmask_message(key: String, message: String): String
+ private fun unmask_message(key: NativeString, message: NativeString, len: Int): NativeString
do
- var return_message = new FlatBuffer.with_capacity(message.length)
- var msg_iter = message.chars.iterator
+ var return_message = new NativeString(len)
- while msg_iter.is_ok do
- return_message.chars[msg_iter.index] = msg_iter.item.ascii.bin_xor(key.chars[msg_iter.index%4].ascii).ascii
- msg_iter.next
+ for i in [0 .. len[ do
+ return_message[i] = message[i].ascii.bin_xor(key[i%4].ascii).ascii
end
- return return_message.to_s
+ return return_message
end
# Checks if a connection to a client is available
redef fun connected do return client.connected
- redef fun write(msg)
- do
- client.write(frame_message(msg.to_s))
- end
+ redef fun write_bytes(s) do client.write_bytes(frame_message(s.to_s))
+
+ redef fun write(msg) do client.write(frame_message(msg.to_s).to_s)
redef fun is_writable do return client.connected
redef fun fill_buffer
do
- _buffer.clear
- _buffer_pos = 0
+ buffer_reset
unpad_message
end
- redef fun end_reached do return client._buffer_pos >= client._buffer.length and client.end_reached
+ redef fun end_reached do return client._buffer_pos >= client._buffer_length and client.end_reached
# Is there some data available to be read ?
fun can_read(timeout: Int): Bool do return client.ready_to_read(timeout)