lib/core: add no-overhead read_bytes implementation
authorLucas Bajolet <lucas.bajolet@gmail.com>
Wed, 25 Apr 2018 20:27:31 +0000 (16:27 -0400)
committerLucas Bajolet <lucas.bajolet@gmail.com>
Tue, 1 May 2018 21:36:56 +0000 (17:36 -0400)
read_bytes as a simple interface is relevant, but may hinder
performances in case a chunk is repeatedly read and discarded as for
each read operation, a buffer is re-allocated.

A secondary read operation is introduced to read directly to a CString,
using closer-to-C semantics.

Also start using codec lookahead as source for reading bytes.

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

lib/bitmap/bitmap.nit
lib/core/exec.nit
lib/core/file.nit
lib/core/stream.nit

index e18888c..2bfb252 100644 (file)
@@ -169,13 +169,14 @@ class Bitmap
                        for x in [0..self.height[
                        do
                                var row = new Array[Int].with_capacity(self.width)
+                               var rgb_str = new CString(3)
                                for y in [0..self.width[
                                do
-                                       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]
+                                       var bts = fileReader.read_bytes_to_cstring(rgb_str, 3)
+                                       if bts < 3 then return
+                                       var red = rgb_str[0] << 16
+                                       var green = rgb_str[1] << 8
+                                       var blue = rgb_str[2]
                                        row.add(red.to_i + green.to_i + blue.to_i)
                                end
                                self.data.add(row)
index 1d82705..64d6cc7 100644 (file)
@@ -349,7 +349,7 @@ class ProcessReader
 
        redef fun read_char do return stream_in.read_char
 
-       redef fun read_byte do return stream_in.read_byte
+       redef fun raw_read_byte do return stream_in.read_byte
 
        redef fun eof do return stream_in.eof
 
index 643fe08..71f3615 100644 (file)
@@ -511,7 +511,7 @@ class Path
                var output = dest.open_wo
 
                while not input.eof do
-                       var buffer = input.read_bytes(1024)
+                       var buffer = input.read_bytes(4096)
                        output.write_bytes buffer
                end
 
index ccd08d8..9b19dd3 100644 (file)
@@ -109,32 +109,71 @@ end
 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.to_b
+                       rd += 1
+               end
+               return rd
+       end
+
        # Reads a character. Returns `null` on EOF or timeout
        fun read_char: nullable Char is abstract
 
        # Reads a byte. Returns a negative value on error
-       fun read_byte: Int is abstract
+       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 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 or i <= 0 then return new Bytes.empty
-               var s = new CString(i)
-               var buf = new Bytes(s, 0, i)
-               while i > 0 and not eof do
-                       var c = read_byte
-                       if c < 0 then
-                               continue
-                       end
-                       buf.add c.to_b
-                       i -= 1
+       fun read(i: Int): String do
+               var cs = new CString(i)
+               var rd = read_bytes_to_cstring(cs, i)
+               return codec.decode_string(cs, rd)
+       end
+
+       # Reads up to `max` bytes from source
+       fun read_bytes(max: Int): Bytes do
+               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 buf
+               return rd + raw_read_bytes(bytes, max - rd)
        end
 
        # Read a string until the end of the line.
@@ -249,10 +288,10 @@ abstract class Reader
        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 c = read_byte
-                       if c < 0 then continue
-                       s.add(c.to_b)
+                       var rd = read_bytes_to_cstring(buf, 4096)
+                       s.append_ns(buf, rd)
                end
                return s
        end
@@ -550,12 +589,11 @@ abstract class BufferedReader
                return bf
        end
 
-       redef fun read_bytes(i)
+       redef fun read_bytes_to_cstring(buf, i)
        do
-               if last_error != null then return new Bytes.empty
-               var buf = new Bytes.with_capacity(i)
-               read_intern(i, buf)
-               return buf
+               if last_error != null then return 0
+               var bbf = new Bytes(buf, 0, i)
+               return read_intern(i, bbf)
        end
 
        # Fills `buf` with at most `i` bytes read from `self`
@@ -819,6 +857,15 @@ class BytesReader
                return res
        end
 
+       redef fun raw_read_bytes(ns, max) do
+               if cursor >= bytes.length then return 0
+
+               var copy = max.min(bytes.length - cursor)
+               bytes.items.copy_to(ns, copy, cursor, 0)
+               cursor += copy
+               return copy
+       end
+
        redef fun eof do return cursor >= bytes.length
 end