X-Git-Url: http://nitlanguage.org diff --git a/lib/core/bytes.nit b/lib/core/bytes.nit index d250760..84276ac 100644 --- a/lib/core/bytes.nit +++ b/lib/core/bytes.nit @@ -53,7 +53,7 @@ redef class Byte super BytePattern # Write self as a string into `ns` at position `pos` - private fun add_digest_at(ns: NativeString, pos: Int) do + private fun add_digest_at(ns: CString, pos: Int) do var tmp = (0xF0u8 & self) >> 4 ns[pos] = if tmp >= 0x0Au8 then tmp + 0x37u8 else tmp + 0x30u8 tmp = 0x0Fu8 & self @@ -146,8 +146,8 @@ class Bytes super AbstractArray[Byte] super BytePattern - # A NativeString being a char*, it can be used as underlying representation here. - var items: NativeString + # A CString being a char*, it can be used as underlying representation here. + var items: CString # Number of bytes in the array redef var length @@ -163,13 +163,13 @@ class Bytes # var b = new Bytes.empty # assert b.to_s == "" init empty do - var ns = new NativeString(0) + var ns = new CString(0) init(ns, 0, 0) end # Init a `Bytes` with capacity `cap` init with_capacity(cap: Int) do - var ns = new NativeString(cap) + var ns = new CString(cap) init(ns, 0, cap) end @@ -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" @@ -262,7 +262,7 @@ class Bytes # ~~~ fun hexdigest: String do var elen = length * 2 - var ns = new NativeString(elen) + var ns = new CString(elen) var i = 0 var oi = 0 while i < length do @@ -285,7 +285,7 @@ class Bytes # ~~~ fun chexdigest: String do var elen = length * 4 - var ns = new NativeString(elen) + var ns = new CString(elen) var i = 0 var oi = 0 while i < length do @@ -308,7 +308,7 @@ class Bytes # ~~~ fun binarydigest: String do var elen = length * 8 - var ns = new NativeString(elen) + var ns = new CString(elen) var i = 0 var oi = 0 while i < length do @@ -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 @@ -430,13 +455,13 @@ class Bytes # Regenerates the buffer, necessary when it was persisted private fun regen do - var nns = new NativeString(capacity) + var nns = new CString(capacity) items.copy_to(nns, length, 0, 0) persisted = false end # Appends the `ln` first bytes of `ns` to self - fun append_ns(ns: NativeString, ln: Int) do + fun append_ns(ns: CString, ln: Int) do if persisted then regen var nlen = length + ln if nlen > capacity then enlarge(nlen) @@ -445,7 +470,7 @@ class Bytes end # Appends `ln` bytes from `ns` starting at index `from` to self - fun append_ns_from(ns: NativeString, ln, from: Int) do + fun append_ns_from(ns: CString, ln, from: Int) do if persisted then regen var nlen = length + ln if nlen > capacity then enlarge(nlen) @@ -453,20 +478,17 @@ 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 NativeString(capacity) + var ns = new CString(capacity) items.copy_to(ns, length, 0, 0) items = ns end @@ -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 @@ -581,6 +603,7 @@ class Bytes # assert "incomplete %".to_bytes.from_percent_encoding == "incomplete %".to_bytes # assert "invalid % usage".to_bytes.from_percent_encoding == "invalid % usage".to_bytes # assert "%c3%a9%e3%81%82%e3%81%84%e3%81%86".to_bytes.from_percent_encoding == "éあいう".to_bytes + # assert "%1 %A %C3%A9A9".to_bytes.from_percent_encoding == "%1 %A éA9".to_bytes fun from_percent_encoding: Bytes do var tmp = new Bytes.with_capacity(length) var pos = 0 @@ -637,7 +660,7 @@ end private class BytesIterator super IndexedIterator[Byte] - var tgt: NativeString + var tgt: CString redef var index @@ -653,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" @@ -663,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 @@ -796,7 +862,7 @@ redef class Text # assert "<STRING/&rt;".hexdigest == "266C743B535452494E47262334373B2672743B" fun hexdigest: String do var ln = byte_length - var outns = new NativeString(ln * 2) + var outns = new CString(ln * 2) var oi = 0 for i in [0 .. ln[ do bytes[i].add_digest_at(outns, oi) @@ -930,11 +996,11 @@ 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 -redef class NativeString +redef class CString # Creates a new `Bytes` object from `self` with `len` as length # # If `len` is null, strlen will determine the length of the Bytes @@ -948,7 +1014,7 @@ redef class NativeString # If `len` is null, strlen will determine the length of the Bytes fun to_bytes_with_copy(len: nullable Int): Bytes do if len == null then len = cstring_length - var nns = new NativeString(len) + var nns = new CString(len) copy_to(nns, len, 0, 0) return new Bytes(nns, len, len) end