1 # This file is part of NIT (http://www.nitlanguage.org).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Add reading and writing binary services
18 # var w = new FileWriter.open("/tmp/data.bin")
20 # w.write_int64 123456789
23 # w.write_double 1.234567
24 # w.write_bits(true, false, true)
25 # assert w.last_error == null
28 # var r = new FileReader.open("/tmp/data.bin")
29 # assert r.read(5) == "hello"
30 # assert r.read_int64 == 123456789
31 # assert r.read_byte == 3
32 # assert r.read_float == 1.25
33 # assert r.read_double == 1.234567
35 # var bits = r.read_bits
36 # assert bits[0] and not bits[1] and bits[2]
38 # assert r.last_error == null
47 // Android compatibility
49 #define be64toh(val) betoh64(val)
52 #define le64toh(val) letoh64(val)
56 # A stream of binary data
57 abstract class BinaryStream
60 # Use the big-endian convention? otherwise use little-endian.
62 # By default, `true` to use big-endian.
63 var big_endian
= true is writable
66 redef abstract class Writer
69 # Write a boolean `value` on a byte, using 0 for `false` and 1 for `true`
70 fun write_bool
(value
: Bool) do write_byte
if value
then 1 else 0
72 # Write up to 8 `Bool` in a byte
74 # To be used with `BinaryReader::read_bits`.
76 # Ensure: `bits.length <= 8`
77 fun write_bits
(bits
: Bool...)
79 assert bits
.length
<= 8
82 for b
in bits
.length
.times
do
83 if bits
[b
] then int
+= 2**b
89 # Write a floating point `value` on 32 bits
91 # Using this format may result in a loss of precision as it uses less bits
93 fun write_float
(value
: Float)
95 for i
in [0..4[ do write_byte value
.float_byte_at
(i
, big_endian
)
98 # Write a floating point `value` on 64 bits
99 fun write_double
(value
: Float)
101 for i
in [0..8[ do write_byte value
.double_byte_at
(i
, big_endian
)
104 # Write `value` as a signed integer on 64 bits
106 # Using this format may result in a loss of precision as the length of a
107 # Nit `Int` may be more than 64 bits on some platforms.
108 fun write_int64
(value
: Int)
110 for i
in [0..8[ do write_byte value
.int64_byte_at
(i
, big_endian
)
116 # fun write_uint8 # No need for this one, it is the current `read_char`
122 # fun write_long_double?
125 redef abstract class Reader
128 # Read a single byte and return `true` if its value is different than 0
129 fun read_bool
: Bool do return read_byte
!= 0
131 # Get an `Array` of 8 `Bool` by reading a single byte
133 # To be used with `BinaryWriter::write_bits`.
134 fun read_bits
: Array[Bool]
137 if int
== null then return new Array[Bool]
138 return [for b
in 8.times
do int
.bin_and
(2**b
) > 0]
141 # Read a floating point on 32 bits and return it as a `Float`
143 # Using this format may result in a loss of precision as it uses less bits
145 fun read_float
: Float
147 if last_error
!= null then return 0.0
154 # Check for error, `last_error` is set by `read_byte`
155 if b0
== null or b1
== null or b2
== null or b3
== null then return 0.0
157 return native_read_float
(b0
, b1
, b2
, b3
, big_endian
)
160 # Utility for `read_float`
161 private fun native_read_float
(b0
, b1
, b2
, b3
: Int, big_endian
: Bool): Float `{
174 u.conv = be32toh(u.conv);
175 else u.conv = le32toh(u.conv);
180 # Read a floating point on 64 bits and return it as a `Float`
181 fun read_double
: Float
183 if last_error
!= null then return 0.0
194 # Check for error, `last_error` is set by `read_byte`
195 if b0
== null or b1
== null or b2
== null or b3
== null or
196 b4
== null or b5
== null or b6
== null or b7
== null then return 0.0
198 return native_read_double
(b0
, b1
, b2
, b3
, b4
, b5
, b6
, b7
, big_endian
)
201 # Utility for `read_double`
202 private fun native_read_double
(b0
, b1
, b2
, b3
, b4
, b5
, b6
, b7
: Int, big_endian
: Bool): Float `{
219 u.conv = be64toh(u.conv);
220 else u.conv = le64toh(u.conv);
225 # Read a signed integer on 64 bits and return is an `Int`
227 # Using this format may result in a loss of precision as the length of a
228 # Nit `Int` may be less than 64 bits on some platforms.
231 if last_error
!= null then return 0
242 # Check for error, `last_error` is set by `read_byte`
243 if b0
== null or b1
== null or b2
== null or b3
== null or
244 b4
== null or b5
== null or b6
== null or b7
== null then return 0
246 return native_read_int64
(b0
, b1
, b2
, b3
, b4
, b5
, b6
, b7
, big_endian
)
249 # Utility for `read_int64`
250 private fun native_read_int64
(b0
, b1
, b2
, b3
, b4
, b5
, b6
, b7
: Int, big_endian
: Bool): Int `{
267 u.conv = be64toh(u.conv);
268 else u.conv = le64toh(u.conv);
275 # Utility for `BinaryWriter`
276 private fun int64_byte_at
(index
: Int, big_endian
: Bool): Int `{
278 unsigned char bytes[8];
286 u.conv = htobe64(u.conv);
287 else u.conv = htole64(u.conv);
289 return u.bytes[index];
294 # Utility for `BinaryWriter`
295 private fun float_byte_at
(index
: Int, big_endian
: Bool): Int `{
297 unsigned char bytes[4];
305 u.conv = htobe32(u.conv);
306 else u.conv = htole32(u.conv);
308 return u.bytes[index];
311 # Utility for `BinaryWriter`
312 private fun double_byte_at
(index
: Int, big_endian
: Bool): Int `{
314 unsigned char bytes[8];
322 u.conv = htobe64(u.conv);
323 else u.conv = htole64(u.conv);
325 return u.bytes[index];