core/stream: change read_byte return type to Int
authorLucas Bajolet <lucas.bajolet@gmail.com>
Thu, 29 Mar 2018 19:57:22 +0000 (15:57 -0400)
committerLucas Bajolet <lucas.bajolet@gmail.com>
Thu, 26 Apr 2018 15:56:02 +0000 (11:56 -0400)
A read_byte used to return a nullable Byte, which could cause
unnecessary boxings, and thus could harm performance.

This commit changes its return to Int, and adopts a closer-to-C api,
returning a negative value when an error occurs.

Signed-off-by: Lucas Bajolet <lucas.bajolet@gmail.com>

lib/binary/binary.nit
lib/binary/serialization.nit
lib/core/stream.nit
lib/msgpack/read.nit
lib/saxophonit/lexer.nit
lib/websocket/websocket.nit
tests/sav/test_binary.res
tests/sav/test_binary_alt1.res
tests/sav/test_binary_alt2.res
tests/sav/test_binary_alt3.res
tests/test_binary.nit

index dbe07be..b8f8b5c 100644 (file)
@@ -28,7 +28,7 @@
 # var r = new FileReader.open("/tmp/data.bin")
 # assert r.read(5) == "hello"
 # assert r.read_int64 == 123456789
-# assert r.read_byte == 3u8
+# assert r.read_byte == 3
 # assert r.read_float == 1.25
 # assert r.read_double == 1.234567
 #
@@ -153,7 +153,7 @@ redef abstract class Reader
        # 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 != 0u8
+       fun read_bool: Bool do return read_byte > 0
 
        # Get an `Array` of 8 `Bool` by reading a single byte
        #
@@ -163,10 +163,10 @@ redef abstract class Reader
        fun read_bits: Array[Bool]
        do
                var int = read_byte
-               if int == null then return new Array[Bool]
+               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) & 1u8) != 0u8)
+                       arr.push(((int >> i) & 1) != 0)
                end
                return arr
        end
@@ -181,10 +181,10 @@ redef abstract class Reader
                var buf = new Bytes.empty
                loop
                        var byte = read_byte
-                       if byte == null or byte == 0u8 then
+                       if byte <= 0 then
                                return buf.to_s
                        end
-                       buf.add byte
+                       buf.add byte.to_b
                end
        end
 
@@ -216,13 +216,13 @@ redef abstract class Reader
                var b3 = read_byte
 
                # Check for error, `last_error` is set by `read_byte`
-               if b0 == null or b1 == null or b2 == null or b3 == null then return 0.0
+               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: Byte, big_endian: Bool): Float `{
+       private fun native_read_float(b0, b1, b2, b3: Int, big_endian: Bool): Float `{
                union {
                        unsigned char b[4];
                        float val;
@@ -258,14 +258,14 @@ redef abstract class Reader
                var b7 = read_byte
 
                # Check for error, `last_error` is set by `read_byte`
-               if b0 == null or b1 == null or b2 == null or b3 == null or
-                  b4 == null or b5 == null or b6 == null or b7 == null then return 0.0
+               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: Byte, big_endian: Bool): Float `{
+       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;
@@ -308,14 +308,14 @@ redef abstract class Reader
                var b7 = read_byte
 
                # Check for error, `last_error` is set by `read_byte`
-               if b0 == null or b1 == null or b2 == null or b3 == null or
-                  b4 == null or b5 == null or b6 == null or b7 == null then return 0
+               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: Byte, big_endian: Bool): Int `{
+       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;
index ddb87ad..af1b1b4 100644 (file)
@@ -212,11 +212,12 @@ class BinaryDeserializer
        # Convert from simple Json object to Nit object
        private fun deserialize_next_object: nullable Object
        do
-               var kind = stream.read_byte
-               assert kind isa Byte else
+               var kindi = stream.read_byte
+               assert kindi >= 0 else
                        # TODO break even on keep_going
                        return null
                end
+               var kind = kindi.to_b
 
                # After this point, all stream reading errors are caught later
 
@@ -227,13 +228,13 @@ class BinaryDeserializer
                if kind == kind_char then
                        var bf = char_buf
                        var b = stream.read_byte
-                       if b == null then return '�'
-                       var ln = b.u8len
-                       bf[0] = b
+                       if b < 0 then return '�'
+                       var ln = b.to_b.u8len
+                       bf[0] = b.to_b
                        for i in [1 .. ln[ do
                                b = stream.read_byte
-                               if b == null then return '�'
-                               bf[i] = b
+                               if b < 0 then return '�'
+                               bf[i] = b.to_b
                        end
                        return bf.to_s_unsafe(ln, copy=false)[0]
                end
@@ -290,7 +291,7 @@ class BinaryDeserializer
 
                        # Check for the attributes end marker
                        loop
-                               var next_byte = stream.read_byte
+                               var next_byte = stream.read_byte.to_b
                                if next_byte == new_object_end then break
 
                                # Fetch an additional attribute, even if it isn't expected
index b006398..61b59dd 100644 (file)
@@ -71,24 +71,27 @@ abstract class Reader
        # Reads a character. Returns `null` on EOF or timeout
        fun read_char: nullable Char is abstract
 
-       # Reads a byte. Returns `null` on EOF or timeout
-       fun read_byte: nullable Byte is abstract
+       # Reads a byte. Returns a negative value on error
+       fun read_byte: 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
+       #
+       # If i <= 0, an empty buffer will be returned
        fun read_bytes(i: Int): Bytes
        do
-               if last_error != null then return new Bytes.empty
+               if last_error != null or i <= 0 then return new Bytes.empty
                var s = new CString(i)
-               var buf = new Bytes(s, 0, 0)
+               var buf = new Bytes(s, 0, i)
                while i > 0 and not eof do
                        var c = read_byte
-                       if c != null then
-                               buf.add c
-                               i -= 1
+                       if c < 0 then
+                               continue
                        end
+                       buf.add c.to_b
+                       i -= 1
                end
                return buf
        end
@@ -229,7 +232,8 @@ abstract class Reader
                var s = new Bytes.empty
                while not eof do
                        var c = read_byte
-                       if c != null then s.add(c)
+                       if c < 0 then continue
+                       s.add(c.to_b)
                end
                return s
        end
@@ -478,14 +482,14 @@ abstract class BufferedReader
 
        redef fun read_byte
        do
-               if last_error != null then return null
+               if last_error != null then return -1
                if eof then
                        last_error = new IOError("Stream has reached eof")
-                       return null
+                       return -1
                end
                var c = _buffer[_buffer_pos]
                _buffer_pos += 1
-               return c
+               return c.to_i
        end
 
        # Resets the internal buffer
@@ -757,8 +761,8 @@ end
 # ~~~
 # var reader = new BytesReader(b"a…b")
 # assert reader.read_char == 'a'
-# assert reader.read_byte == 0xE2u8 # 1st byte of '…'
-# assert reader.read_byte == 0x80u8 # 2nd byte of '…'
+# assert reader.read_byte == 0xE2 # 1st byte of '…'
+# assert reader.read_byte == 0x80 # 2nd byte of '…'
 # assert reader.read_char == '�' # Reads the last byte as an invalid char
 # assert reader.read_all_bytes == b"b"
 # ~~~
@@ -783,11 +787,11 @@ class BytesReader
 
        redef fun read_byte
        do
-               if cursor >= bytes.length then return null
+               if cursor >= bytes.length then return -1
 
                var c = bytes[cursor]
                cursor += 1
-               return c
+               return c.to_i
        end
 
        redef fun close do bytes = new Bytes.empty
@@ -810,8 +814,8 @@ end
 # ~~~
 # var reader = new StringReader("a…b")
 # assert reader.read_char == 'a'
-# assert reader.read_byte == 0xE2u8 # 1st byte of '…'
-# assert reader.read_byte == 0x80u8 # 2nd byte of '…'
+# assert reader.read_byte == 0xE2 # 1st byte of '…'
+# assert reader.read_byte == 0x80 # 2nd byte of '…'
 # assert reader.read_char == '�' # Reads the last byte as an invalid char
 # assert reader.read_all == "b"
 # ~~~
index b068a81..1148ec6 100644 (file)
@@ -38,142 +38,144 @@ redef class Reader
                if last_error != null then return 0
 
                var typ = read_byte
-               if typ == null then
+               if typ < 0 then
                        # Error, return default `null`
                        return null
 
-               else if typ & 0b1000_0000u8 == 0u8 or typ & 0b1110_0000u8 == 0b1110_0000u8 then
+               else if typ & 0b1000_0000 == 0 or typ & 0b1110_0000 == 0b1110_0000 then
                        # fixint
                        var bytes = new Bytes.with_capacity(1)
-                       bytes.add typ
+                       bytes.add typ.to_b
                        return bytes.to_i(signed=true)
 
-               else if typ & 0b1111_0000u8 == 0b1000_0000u8 then
+               else if typ & 0b1111_0000 == 0b1000_0000 then
                        # fixmap
-                       var len = typ & 0b0000_1111u8
+                       var len = typ & 0b0000_1111
                        return read_msgpack_map_data(len.to_i)
 
-               else if typ & 0b1111_0000u8 == 0b1001_0000u8 then
+               else if typ & 0b1111_0000 == 0b1001_0000 then
                        # fixarray
-                       var len = typ & 0b0000_1111u8
+                       var len = typ & 0b0000_1111
                        return read_msgpack_array_data(len.to_i)
 
-               else if typ & 0b1110_0000u8 == 0b1010_0000u8 then
+               else if typ & 0b1110_0000 == 0b1010_0000 then
                        # fixstr
-                       var len = typ & 0b0001_1111u8
+                       var len = typ & 0b0001_1111
                        return read_bytes(len.to_i).to_s
 
-               else if typ == 0xC0u8 then
+               else if typ == 0xC0 then
                        return null
-               else if typ == 0xC2u8 then
+               else if typ == 0xC2 then
                        return false
-               else if typ == 0xC3u8 then
+               else if typ == 0xC3 then
                        return true
 
-               else if typ == 0xCCu8 then
+               else if typ == 0xCC then
                        # uint8
-                       return (read_byte or else 0u8).to_i
-               else if typ == 0xCDu8 then
+                       var val = read_byte
+                       if val < 0 then return 0
+                       return val
+               else if typ == 0xCD then
                        # uint16
                        return read_bytes(2).to_i
-               else if typ == 0xCEu8 then
+               else if typ == 0xCE then
                        # uint32
                        return read_bytes(4).to_i
-               else if typ == 0xCFu8 then
+               else if typ == 0xCF then
                        # uint64
                        return read_bytes(8).to_i
-               else if typ == 0xD0u8 then
+               else if typ == 0xD0 then
                        # int8
                        return read_bytes(1).to_i(true)
-               else if typ == 0xD1u8 then
+               else if typ == 0xD1 then
                        # int16
                        return read_bytes(2).to_i(true)
-               else if typ == 0xD2u8 then
+               else if typ == 0xD2 then
                        # int32
                        return read_bytes(4).to_i(true)
-               else if typ == 0xD3u8 then
+               else if typ == 0xD3 then
                        # int64
                        return read_int64
 
-               else if typ == 0xCAu8 then
+               else if typ == 0xCA then
                        return read_float
-               else if typ == 0xCBu8 then
+               else if typ == 0xCB then
                        return read_double
 
-               else if typ == 0xD9u8 then
+               else if typ == 0xD9 then
                        # str8
                        var len = read_byte
-                       if len == null then return null
+                       if len < 0 then return null
                        return read_bytes(len.to_i).to_s
-               else if typ == 0xDAu8 then
+               else if typ == 0xDA then
                        # str16
                        var len = read_bytes(2)
                        return read_bytes(len.to_i).to_s
-               else if typ == 0xDBu8 then
+               else if typ == 0xDB then
                        # str32
                        var len = read_bytes(4)
                        return read_bytes(len.to_i).to_s
 
-               else if typ == 0xC4u8 then
+               else if typ == 0xC4 then
                        # bin8
                        var len = read_byte
-                       if len == null then return null
+                       if len < 0 then return null
                        return read_bytes(len.to_i)
-               else if typ == 0xC5u8 then
+               else if typ == 0xC5 then
                        # bin16
                        var len = read_bytes(2)
                        return read_bytes(len.to_i)
-               else if typ == 0xC6u8 then
+               else if typ == 0xC6 then
                        # bin32
                        var len = read_bytes(4)
                        return read_bytes(len.to_i)
 
-               else if typ == 0xDCu8 then
+               else if typ == 0xDC then
                        # array16
                        var len = read_bytes(2)
                        return read_msgpack_array_data(len.to_i)
-               else if typ == 0xDDu8 then
+               else if typ == 0xDD then
                        # array32
                        var len = read_bytes(4)
                        return read_msgpack_array_data(len.to_i)
 
-               else if typ == 0xDEu8 then
+               else if typ == 0xDE then
                        # map16
                        var len = read_bytes(2)
                        return read_msgpack_map_data(len.to_i)
-               else if typ == 0xDFu8 then
+               else if typ == 0xDF then
                        # map32
                        var len = read_bytes(4)
                        return read_msgpack_map_data(len.to_i)
 
-               else if typ == 0xD4u8 then
+               else if typ == 0xD4 then
                        # fixext1
                        return read_msgpack_fixext_data(1)
-               else if typ == 0xD5u8 then
+               else if typ == 0xD5 then
                        # fixext2
                        return read_msgpack_fixext_data(2)
-               else if typ == 0xD6u8 then
+               else if typ == 0xD6 then
                        # fixext4
                        return read_msgpack_fixext_data(4)
-               else if typ == 0xD7u8 then
+               else if typ == 0xD7 then
                        # fixext8
                        return read_msgpack_fixext_data(8)
-               else if typ == 0xD8u8 then
+               else if typ == 0xD8 then
                        # fixext16
                        return read_msgpack_fixext_data(16)
 
-               else if typ == 0xC7u8 then
+               else if typ == 0xC7 then
                        # ext1
                        return read_msgpack_ext_data(1)
-               else if typ == 0xC8u8 then
+               else if typ == 0xC8 then
                        # ext2
                        return read_msgpack_ext_data(2)
-               else if typ == 0xC9u8 then
+               else if typ == 0xC9 then
                        # ext4
                        return read_msgpack_ext_data(4)
                end
 
-               print_error "MessagePack Warning: Found no match for typ {typ} / 0b{typ.to_i.to_base(2)}"
+               print_error "MessagePack Warning: Found no match for typ {typ} / 0b{typ.to_base(2)}"
                return null
        end
 
@@ -202,9 +204,10 @@ redef class Reader
        # ~~~
        private fun read_msgpack_fixext_data(len: Int): MsgPackExt
        do
-               var exttyp = read_byte or else 0u8
+               var exttyp = read_byte
+               if exttyp < 0 then exttyp = 0
                var data = read_bytes(len)
-               return new MsgPackExt(exttyp, data)
+               return new MsgPackExt(exttyp.to_b, data)
        end
 
        # Read the content of a dynamic *ext* including the length on `len_len` bytes
index a1f7b29..2876087 100644 (file)
@@ -255,7 +255,7 @@ class XophonLexer
                end
 
                var s = input.read_byte
-               if s == null then
+               if s < 0 then
                        last_char = -1
                        return
                end
@@ -267,10 +267,10 @@ class XophonLexer
                if was_cr and last_char == '\n'.code_point then
                        # EOL already reported. => Skip this byte.
                        s = input.read_byte
-                       if s == null then
+                       if s < 0 then
                                last_char = -1
                        else
-                               last_char = s.to_i
+                               last_char = s
                        end
                end
                was_cr = last_char == '\r'.code_point
index 8a97421..f4ee1b0 100644 (file)
@@ -163,7 +163,7 @@ class WebsocketConnection
                while not fin do
                        var fst_byte = client.read_byte
                        var snd_byte = client.read_byte
-                       if fst_byte == null or snd_byte == null then
+                       if fst_byte < 0 or snd_byte < 0 then
                                last_error = new IOError("Error: bad frame")
                                client.close
                                return
@@ -181,9 +181,9 @@ class WebsocketConnection
                        #       %x9 denotes a ping
                        #       %xA denotes a pong
                        #       %xB-F are reserved for further control frames
-                       var fin_flag = fst_byte & 0b1000_0000u8
+                       var fin_flag = fst_byte & 0b1000_0000
                        if fin_flag != 0 then fin = true
-                       var opcode = fst_byte & 0b0000_1111u8
+                       var opcode = fst_byte & 0b0000_1111
                        if opcode == 9 then
                                bf.add(138u8)
                                bf.add(0u8)
@@ -199,8 +199,8 @@ class WebsocketConnection
                        # |(mask - 1bit)|(payload length - 7 bits)
                        # As specified, if the payload length is 126 or 127
                        # The next 16 or 64 bits contain an extended payload length
-                       var mask_flag = snd_byte & 0b1000_0000u8
-                       var len = (snd_byte & 0b0111_1111u8).to_i
+                       var mask_flag = snd_byte & 0b1000_0000
+                       var len = snd_byte & 0b0111_1111
                        var payload_ext_len = 0
                        if len == 126 then
                                var tmp = client.read_bytes(2)
index 887631f..f071d47 100644 (file)
@@ -1,6 +1,6 @@
 no error
 hello
-0x4d
+77
 1.235
 1.235
 123456789
index 832041b..eb0468f 100644 (file)
@@ -1,6 +1,6 @@
 no error
 hello
-0x4d
+77
 144545136640.0
 0.0
 1571011930645069824
index 832041b..eb0468f 100644 (file)
@@ -1,6 +1,6 @@
 no error
 hello
-0x4d
+77
 144545136640.0
 0.0
 1571011930645069824
index 887631f..f071d47 100644 (file)
@@ -1,6 +1,6 @@
 no error
 hello
-0x4d
+77
 1.235
 1.235
 123456789
index 3af2fd6..45750de 100644 (file)
@@ -30,7 +30,12 @@ var reader = new FileReader.open(path)
 #alt2# reader.big_endian = false
 #alt3# reader.big_endian = false
 print reader.read(5)
-print reader.read_byte or else "null"
+var b = reader.read_byte
+if b >= 0 then
+       print b
+else
+       print "null"
+end
 print reader.read_float
 print reader.read_double
 print reader.read_int64