manual: CI check with nitunit
[nit.git] / lib / binary / binary.nit
index 0084227..8b39d31 100644 (file)
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Add reading and writing binary services
+# Read and write binary data with any `Reader` and `Writer`
 #
 # ~~~
 # var w = new FileWriter.open("/tmp/data.bin")
@@ -45,10 +45,13 @@ in "C" `{
        #include <endian.h>
 
        // Android compatibility
+       #ifndef be32toh
+               #define be32toh(val) betoh32(val)
+               #define le32toh(val) letoh32(val)
+       #endif
+
        #ifndef be64toh
                #define be64toh(val) betoh64(val)
-       #endif
-       #ifndef le64toh
                #define le64toh(val) letoh64(val)
        #endif
 `}
@@ -80,12 +83,34 @@ redef abstract class Writer
 
                var int = 0
                for b in bits.length.times do
-                       if bits[b] then int += 2**b
+                       if bits[b] then int |= 1 << (7 - b)
                end
 
                write_byte int
        end
 
+       # Write `text` as a null terminated string
+       #
+       # To be used with `Reader::read_string`.
+       #
+       # Require: `text` has no null bytes.
+       fun write_string(text: Text)
+       do
+               write text
+               write_byte 0x00
+       end
+
+       # Write the length as a 64 bits integer, then the content of `text`
+       #
+       # To be used with `Reader::read_block`.
+       #
+       # Compared to `write_string`, this method supports null bytes in `text`.
+       fun write_block(text: Text)
+       do
+               write_int64 text.byte_length
+               write text
+       end
+
        # Write a floating point `value` on 32 bits
        #
        # Using this format may result in a loss of precision as it uses less bits
@@ -126,22 +151,61 @@ redef abstract class Reader
        super BinaryStream
 
        # Read a single byte and return `true` if its value is different than 0
-       fun read_bool: Bool do return read_byte != 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 == null then return new Array[Bool]
-               return [for b in 8.times do int.bin_and(2**b) > 0]
+               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
@@ -152,7 +216,7 @@ 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
@@ -178,6 +242,8 @@ redef abstract class Reader
        `}
 
        # 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
@@ -192,8 +258,8 @@ 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
@@ -226,6 +292,8 @@ redef abstract class Reader
        #
        # 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
@@ -240,8 +308,8 @@ 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
@@ -280,7 +348,7 @@ redef class Int
                        uint64_t conv;
                } u;
 
-               u.val = recv;
+               u.val = self;
 
                if (big_endian)
                        u.conv = htobe64(u.conv);
@@ -299,7 +367,7 @@ redef class Float
                        uint32_t conv;
                } u;
 
-               u.val = recv;
+               u.val = self;
 
                if (big_endian)
                        u.conv = htobe32(u.conv);
@@ -316,7 +384,7 @@ redef class Float
                        uint64_t conv;
                } u;
 
-               u.val = recv;
+               u.val = self;
 
                if (big_endian)
                        u.conv = htobe64(u.conv);