X-Git-Url: http://nitlanguage.org diff --git a/lib/core/bytes.nit b/lib/core/bytes.nit index 9bf70c0..84276ac 100644 --- a/lib/core/bytes.nit +++ b/lib/core/bytes.nit @@ -214,7 +214,7 @@ class Bytes return slice(st, ed - st + 1) end - # Returns a subset of the content of `self` starting at `from` and of length `count` + # Copy a subset of `self` starting at `from` and of `count` bytes # # var b = "abcd".to_bytes # assert b.slice(1, 2).hexdigest == "6263" @@ -239,7 +239,7 @@ class Bytes return ret end - # Returns a copy of `self` starting at `from` + # Copy of `self` starting at `from` # # var b = "abcd".to_bytes # assert b.slice_from(1).hexdigest == "626364" @@ -328,35 +328,48 @@ class Bytes return new FlatString.full(ns, elen, 0, elen) end - # Interprets `self` as a big-endian positive integer. + # Interprets `self` as a big-endian integer (unsigned by default) # # ~~~ # var b = "0102".hexdigest_to_bytes # assert b.to_i == 258 + # + # assert "01".hexdigest_to_bytes.to_i == 1 + # assert "FF".hexdigest_to_bytes.to_i == 255 + # assert "0000".hexdigest_to_bytes.to_i == 0 # ~~~ # - # Nul bytes on the left are trimmed. - # 0 is returned for an empty Bytes object. + # If `self.is_empty`, 0 is returned. # # ~~~ - # assert "01".hexdigest_to_bytes.to_i == 1 - # assert "0001".hexdigest_to_bytes.to_i == 1 - # - # assert "0000".hexdigest_to_bytes.to_i == 0 - # assert "00".hexdigest_to_bytes.to_i == 0 # assert "".hexdigest_to_bytes.to_i == 0 # ~~~ # + # If `signed == true`, the bytes are read as a signed integer. + # As usual, the sign bit is the left most bit, no matter the + # `length` of `self`. + # + # ~~~ + # assert "01".hexdigest_to_bytes.to_i(true) == 1 + # assert "FF".hexdigest_to_bytes.to_i(true) == -1 + # assert "00FF".hexdigest_to_bytes.to_i(true) == 255 + # assert "E0".hexdigest_to_bytes.to_i(true) == -32 + # assert "FE00".hexdigest_to_bytes.to_i(true) == -512 + # assert "FEFEFE".hexdigest_to_bytes.to_i(true) == -65794 + # ~~~ + # # `Int::to_bytes` is a loosely reverse method. # # ~~~ # assert b.to_i.to_bytes == b # assert (b.to_i + 1).to_bytes.hexdigest == "0103" # assert "0001".hexdigest_to_bytes.to_i.to_bytes.hexdigest == "01" + # + # assert (-32).to_bytes.to_i(true) == -32 # ~~~ # # Warning: `Int` might overflow for bytes with more than 60 bits. - fun to_i: Int do + fun to_i(signed: nullable Bool): Int do var res = 0 var i = 0 while i < length do @@ -364,6 +377,18 @@ class Bytes res += self[i].to_i i += 1 end + + # Two's complement is `signed` + if signed == true and not_empty and first > 0x80u8 then + var ff = 0 + for j in [0..length[ do + ff *= 0x100 + ff += 0xFF + end + + res = -((res ^ ff) + 1) + end + return res end @@ -453,18 +478,15 @@ class Bytes length += ln end - # Appends the bytes of `s` to `selftextextt` - fun append_text(s: Text) do - for i in s.substrings do - append_ns(i.fast_cstring, i.byte_length) - end - end + # Appends the bytes of `str` to `self` + fun append_text(str: Text) do str.append_to_bytes self redef fun append_to(b) do b.append self redef fun enlarge(sz) do if capacity >= sz then return persisted = false + if capacity < 16 then capacity = 16 while capacity < sz do capacity = capacity * 2 + 2 var ns = new CString(capacity) items.copy_to(ns, length, 0, 0) @@ -474,7 +496,7 @@ class Bytes redef fun to_s do persisted = true var b = self - var r = b.items.to_s_with_length(length) + var r = b.items.to_s_unsafe(length, copy=false) if r != items then persisted = false return r end @@ -654,7 +676,7 @@ private class BytesIterator end redef class Int - # A big-endian representation of self. + # A signed big-endian representation of `self` # # ~~~ # assert 1.to_bytes.hexdigest == "01" @@ -664,43 +686,86 @@ redef class Int # assert 65536.to_bytes.hexdigest == "010000" # ~~~ # + # Negative values are converted to their two's complement. + # Be careful as the result can be ambiguous. + # + # ~~~ + # assert (-1).to_bytes.hexdigest == "FF" + # assert (-32).to_bytes.hexdigest == "E0" + # assert (-512).to_bytes.hexdigest == "FE00" + # assert (-65794).to_bytes.hexdigest == "FEFEFE" + # ~~~ + # + # Optionally, set `n_bytes` to the desired number of bytes in the output. + # This setting can disambiguate the result between positive and negative + # integers. Be careful with this parameter as the result may overflow. + # + # ~~~ + # assert 1.to_bytes(2).hexdigest == "0001" + # assert 65535.to_bytes(2).hexdigest == "FFFF" + # assert (-1).to_bytes(2).hexdigest == "FFFF" + # assert (-512).to_bytes(4).hexdigest == "FFFFFE00" + # assert 0x123456.to_bytes(2).hexdigest == "3456" + # ~~~ + # # For 0, a Bytes object with single nul byte is returned (instead of an empty Bytes object). # # ~~~ # assert 0.to_bytes.hexdigest == "00" # ~~~ # - # `Bytes::to_i` can be used to do the reverse operation. + # For positive integers, `Bytes::to_i` can reverse the operation. # # ~~~ # assert 1234.to_bytes.to_i == 1234 # ~~~ # # Require self >= 0 - fun to_bytes: Bytes do - if self == 0 then return "\0".to_bytes - assert self > 0 + fun to_bytes(n_bytes: nullable Int): Bytes do + + # If 0, force using at least one byte + if self == 0 and n_bytes == null then n_bytes = 1 # Compute the len (log256) var len = 1 var max = 256 - while self >= max do + var s = self.abs + while s >= max do len += 1 max *= 256 end + # Two's complement + s = self + if self < 0 then + var ff = 0 + for j in [0..len[ do + ff *= 0x100 + ff += 0xFF + end + + s = ((-self) ^ ff) + 1 + end + + # Cut long values + if n_bytes != null and len > n_bytes then len = n_bytes + # Allocate the buffer - var res = new Bytes.with_capacity(len) - for i in [0..len[ do res[i] = 0u8 + var cap = n_bytes or else len + var res = new Bytes.with_capacity(cap) + + var filler = if self < 0 then 0xFFu8 else 0u8 + for i in [0..cap[ do res[i] = filler # Fill it starting with the end - var i = len - var sum = self - while i > 0 do + var i = cap + var sum = s + while i > cap - len do i -= 1 res[i] = (sum % 256).to_b sum /= 256 end + return res end end @@ -931,7 +996,7 @@ end redef class FlatText redef fun append_to_bytes(b) do var from = if self isa FlatString then first_byte else 0 - b.append_ns_from(items, byte_length, from) + if isset _items then b.append_ns_from(items, byte_length, from) end end