Merge: Apply serialization in calculator, a_star, more_collections, Couple and Container
authorJean Privat <jean@pryen.org>
Tue, 26 May 2015 10:09:51 +0000 (06:09 -0400)
committerJean Privat <jean@pryen.org>
Tue, 26 May 2015 10:09:51 +0000 (06:09 -0400)
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>

contrib/online_ide/sources/nit/pnacl_nit.nit
lib/bitmap/bitmap.nit
lib/pnacl.nit
lib/socket/socket.nit
lib/socket/socket_c.nit
lib/standard/file.nit
lib/standard/stream.nit
lib/websocket/websocket.nit

index 74c7be0..2017a0b 100644 (file)
@@ -72,7 +72,7 @@ redef class FileReader
                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
@@ -82,7 +82,7 @@ redef class FileReader
 
        redef fun fill_buffer
        do
-               _buffer.clear
+               buffer_reset
                end_reached = true
        end
 
index 0343b8e..9be1bc2 100644 (file)
@@ -123,14 +123,20 @@ class Bitmap
 
                # =============== 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
@@ -159,9 +165,11 @@ class Bitmap
                                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)
@@ -170,18 +178,6 @@ class Bitmap
                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
index 516c860..248a4d4 100644 (file)
@@ -653,9 +653,11 @@ class PnaclStream
        # 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
 
index b3cb2f9..d553540 100644 (file)
@@ -57,7 +57,7 @@ class TCPStream
        # 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)
@@ -84,7 +84,7 @@ class TCPStream
        # 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
@@ -110,7 +110,7 @@ class TCPStream
        # 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
@@ -153,6 +153,11 @@ class TCPStream
                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
@@ -162,7 +167,7 @@ class TCPStream
 
        redef fun fill_buffer
        do
-               _buffer.clear
+               _buffer_length = 0
                _buffer_pos = 0
                if not connected then return
                var read = socket.read
@@ -170,7 +175,17 @@ class TCPStream
                        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
index a574485..627fb06 100644 (file)
@@ -137,16 +137,14 @@ extern class NativeSocket `{ int* `}
                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
index 1490ee7..3548a25 100644 (file)
@@ -113,25 +113,24 @@ class FileReader
                        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
 
@@ -179,18 +178,23 @@ class FileWriter
        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)
@@ -435,10 +439,12 @@ class Path
        # ~~~
        #
        # 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
index 7c4e606..b901596 100644 (file)
@@ -13,6 +13,7 @@ module stream
 
 intrude import ropes
 import error
+intrude import bytes
 
 in "C" `{
        #include <unistd.h>
@@ -48,19 +49,23 @@ abstract class Reader
        # 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.
@@ -154,22 +159,27 @@ abstract class Reader
 
        # 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`.
@@ -342,6 +352,10 @@ end
 # 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
 
@@ -365,7 +379,7 @@ interface Writable
 
        # 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
@@ -408,66 +422,87 @@ abstract class BufferedReader
                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)
@@ -475,10 +510,12 @@ abstract class BufferedReader
                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
@@ -516,29 +553,37 @@ abstract class BufferedReader
 
        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
 
@@ -557,6 +602,11 @@ class StringWriter
        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
@@ -607,11 +657,11 @@ class StringReader
                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
index a22c5b2..6eac618 100644 (file)
@@ -23,6 +23,7 @@ import sha1
 import base64
 
 intrude import standard::stream
+intrude import standard::bytes
 
 # Websocket compatible listener
 #
@@ -65,8 +66,10 @@ class WebsocketConnection
        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)
 
@@ -119,22 +122,28 @@ class WebsocketConnection
        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
@@ -149,6 +158,7 @@ class WebsocketConnection
        # 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
@@ -174,10 +184,10 @@ class WebsocketConnection
                        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
@@ -192,76 +202,68 @@ class WebsocketConnection
                        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)