As discussed in #1267, we need a Byte data type to manipulate low-level data as numerics instead of how it is done for now using chars.
This PR will serve as base for future updates, notably related to #1370 which will introduce a way to express Bytes in their literal form.
Pull-Request: #1403
Reviewed-by: Romain Chanoir <chanoir.romain@courrier.uqam.ca>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>
Reviewed-by: Jean Privat <jean@pryen.org>
# assert 5.to_f != 5 # Float and Int are not equals
fun to_f: Float is abstract
+ # The byte equivalent of `self`
+ #
+ # assert (-1).to_b == 0xFF.to_b
+ # assert (1.9).to_b == 1.to_b
+ fun to_b: Byte is abstract
+
# Is this the value of zero in its domain?
fun is_zero: Bool do return self == zero
redef fun to_i is intern
redef fun to_f do return self
+ redef fun to_b is intern
redef fun zero do return 0.0
redef fun value_of(val) do return val.to_f
end
end
+# Native bytes.
+# Same as a C `unsigned char`
+universal Byte
+ super Discrete
+ super Numeric
+
+ redef type OTHER: Byte
+
+ redef fun successor(i) do return self + i.to_b
+ redef fun predecessor(i) do return self - i.to_b
+
+ redef fun object_id is intern
+ redef fun hash do return self.to_i
+ redef fun ==(i) is intern
+ redef fun !=(i) is intern
+ redef fun output is intern
+
+ redef fun <=(i) is intern
+ redef fun <(i) is intern
+ redef fun >=(i) is intern
+ redef fun >(i) is intern
+ redef fun +(i) is intern
+
+ # On an Byte, unary minus will return `(256 - self) % 256`
+ #
+ # assert -(1.to_b) == 0xFF.to_b
+ # assert -(0.to_b) == 0x00.to_b
+ redef fun - is intern
+ redef fun -(i) is intern
+ redef fun *(i) is intern
+ redef fun /(i) is intern
+
+ # Modulo of `self` with `i`.
+ #
+ # Finds the remainder of division of `self` by `i`.
+ #
+ # assert 5.to_b % 2.to_b == 1.to_b
+ # assert 10.to_b % 2.to_b == 0.to_b
+ fun %(i: Byte): Byte is intern
+
+ redef fun zero do return 0.to_b
+ redef fun value_of(val) do return val.to_b
+
+ # `i` bits shift fo the left (aka <<)
+ #
+ # assert 5.to_b.lshift(1) == 10.to_b
+ fun lshift(i: Int): Byte is intern
+
+ # alias of `lshift`
+ fun <<(i: Int): Byte do return lshift(i)
+
+ # `i` bits shift fo the right (aka >>)
+ #
+ # assert 5.to_b.rshift(1) == 2.to_b
+ fun rshift(i: Int): Byte is intern
+
+ # alias of `rshift`
+ fun >>(i: Int): Byte do return rshift(i)
+
+ redef fun to_i is intern
+ redef fun to_f is intern
+ redef fun to_b do return self
+
+ redef fun distance(i) do return (self - i).to_i
+
+ redef fun <=>(other)
+ do
+ if self < other then
+ return -1
+ else if other < self then
+ return 1
+ else
+ return 0
+ end
+ end
+
+ redef fun is_between(c, d)
+ do
+ if self < c or d < self then
+ return false
+ else
+ return true
+ end
+ end
+
+ redef fun max(other)
+ do
+ if self < other then
+ return other
+ else
+ return self
+ end
+ end
+
+ redef fun min(c)
+ do
+ if c < self then
+ return c
+ else
+ return self
+ end
+ end
+end
+
# Native integer numbers.
# Correspond to C int.
universal Int
redef fun to_i do return self
redef fun to_f is intern
+ redef fun to_b is intern
redef fun distance(i)
do
end
end
+redef class Byte
+ # C function to calculate the length of the `NativeString` to receive `self`
+ private fun byte_to_s_len: Int is extern "native_byte_length_str"
+
+ # C function to convert an nit Int to a NativeString (char*)
+ private fun native_byte_to_s(nstr: NativeString, strlen: Int) is extern "native_byte_to_s"
+
+ # Displayable byte in its hexadecimal form (0x..)
+ #
+ # assert 1.to_b.to_s == "0x01"
+ # assert (-123).to_b.to_s == "0x85"
+ redef fun to_s do
+ var nslen = byte_to_s_len
+ var ns = new NativeString(nslen + 1)
+ ns[nslen] = '\0'
+ native_byte_to_s(ns, nslen + 1)
+ return ns.to_s_with_length(nslen)
+ end
+end
+
redef class Int
# Wrapper of strerror C function
void native_int_to_s(long recv, char* str, long buflen){
snprintf(str, buflen, "%ld", recv);
}
+
+// Returns the length of `recv` as a `char*` (excluding the null character)
+long native_byte_length_str(unsigned char recv){
+ return snprintf(NULL, 0, "0x%02x", recv);
+}
+
+// Byte to NativeString method
+void native_byte_to_s(unsigned char recv, char* str, long buflen){
+ snprintf(str, buflen, "0x%02x", recv);
+}
long native_int_length_str(long recv);
void native_int_to_s(long recv, char* str, long buflen);
+long native_byte_length_str(unsigned char recv);
+void native_byte_to_s(unsigned char recv, char* str, long buflen);
#endif
return res
end
+ # Generate a byte value
+ fun byte_instance(value: Byte): RuntimeVariable
+ do
+ var t = mmodule.byte_type
+ var res = new RuntimeVariable("((unsigned char){value.to_s})", t, t)
+ return res
+ end
+
# Generate a char value
fun char_instance(value: Char): RuntimeVariable
do
return "char"
else if mclass.name == "Float" then
return "double"
+ else if mclass.name == "Byte" then
+ return "unsigned char"
else if mclass.name == "NativeString" then
return "char*"
else if mclass.name == "NativeArray" then
return "c"
else if mclass.name == "Float" then
return "d"
+ else if mclass.name == "Byte" then
+ return "b"
else if mclass.name == "NativeString" then
return "str"
else if mclass.name == "NativeArray" then
else if pname == "to_f" then
v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
return true
+ else if pname == "to_b" then
+ v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
+ return true
else if pname == "ascii" then
v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
return true
v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
return true
end
+ else if cname == "Byte" then
+ if pname == "output" then
+ v.add("printf(\"%x\\n\", {arguments.first});")
+ return true
+ else if pname == "object_id" then
+ v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
+ return true
+ else if pname == "+" then
+ v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "-" then
+ v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "unary -" then
+ v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
+ return true
+ else if pname == "unary +" then
+ v.ret(arguments[0])
+ return true
+ else if pname == "*" then
+ v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "/" then
+ v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "%" then
+ v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "lshift" then
+ v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "rshift" then
+ v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "==" then
+ v.ret(v.equal_test(arguments[0], arguments[1]))
+ return true
+ else if pname == "!=" then
+ var res = v.equal_test(arguments[0], arguments[1])
+ v.ret(v.new_expr("!{res}", ret.as(not null)))
+ return true
+ else if pname == "<" then
+ v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == ">" then
+ v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "<=" then
+ v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == ">=" then
+ v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "to_i" then
+ v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
+ return true
+ else if pname == "to_f" then
+ v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
+ return true
+ else if pname == "ascii" then
+ v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
+ return true
+ end
else if cname == "Bool" then
if pname == "output" then
v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
else if pname == "to_i" then
v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
return true
+ else if pname == "to_b" then
+ v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
+ return true
end
else if cname == "NativeString" then
if pname == "[]" then
do
# Collect all bas box class
# FIXME: this is not completely fine with a separate compilation scheme
- for classname in ["Int", "Bool", "Char", "Float", "NativeString", "Pointer"] do
+ for classname in ["Int", "Bool", "Byte", "Char", "Float", "NativeString", "Pointer"] do
var classes = self.mainmodule.model.get_mclasses_by_name(classname)
if classes == null then continue
assert classes.length == 1 else print classes.join(", ")
if mclass.name == "Char" then return "char"
if mclass.name == "Int" then return "long"
if mclass.name == "Float" then return "double"
+ if mclass.name == "Byte" then return "byte"
return super
end
if mclass.name == "Char" then return "jchar"
if mclass.name == "Int" then return "jlong"
if mclass.name == "Float" then return "jdouble"
+ if mclass.name == "Byte" then return "jbyte"
return super
end
if mclass.name == "Char" then return "C"
if mclass.name == "Int" then return "J"
if mclass.name == "Float" then return "D"
+ if mclass.name == "Byte" then return "B"
return super
end
if mclass.name == "Char" then return "Char"
if mclass.name == "Int" then return "Long"
if mclass.name == "Float" then return "Double"
+ if mclass.name == "Byte" then return "Byte"
return super
end
end
return instance
end
+ # Return the byte instance associated with `val`.
+ fun byte_instance(val: Byte): Instance
+ do
+ var t = mainmodule.byte_type
+ var instance = new PrimitiveInstance[Byte](t, val)
+ init_instance_primitive(instance)
+ return instance
+ end
+
# Return the char instance associated with `val`.
fun char_instance(val: Char): Instance
do
# else aborts
fun to_f: Float do abort
+ # Return the integer value if the instance is a byte.
+ # else aborts
+ fun to_b: Byte do abort
+
# The real value encapsulated if the instance is primitive.
# Else aborts.
fun val: nullable Object do abort
redef fun to_i do return val.as(Int)
redef fun to_f do return val.as(Float)
+
+ redef fun to_b do return val.as(Byte)
end
# Information about local variables in a running method
return v.char_instance(args[0].to_i.ascii)
else if pname == "to_f" then
return v.float_instance(args[0].to_i.to_f)
+ else if pname == "to_b" then
+ return v.byte_instance(args[0].to_i.to_b)
else if pname == "lshift" then
return v.int_instance(args[0].to_i.lshift(args[1].to_i))
else if pname == "rshift" then
else if pname == "strerror_ext" then
return v.native_string_instance(recvval.strerror)
end
+ else if cname == "Byte" then
+ var recvval = args[0].to_b
+ if pname == "unary -" then
+ return v.byte_instance(-args[0].to_b)
+ else if pname == "unary +" then
+ return args[0]
+ else if pname == "+" then
+ return v.byte_instance(args[0].to_b + args[1].to_b)
+ else if pname == "-" then
+ return v.byte_instance(args[0].to_b - args[1].to_b)
+ else if pname == "*" then
+ return v.byte_instance(args[0].to_b * args[1].to_b)
+ else if pname == "%" then
+ return v.byte_instance(args[0].to_b % args[1].to_b)
+ else if pname == "/" then
+ return v.byte_instance(args[0].to_b / args[1].to_b)
+ else if pname == "<" then
+ return v.bool_instance(args[0].to_b < args[1].to_b)
+ else if pname == ">" then
+ return v.bool_instance(args[0].to_b > args[1].to_b)
+ else if pname == "<=" then
+ return v.bool_instance(args[0].to_b <= args[1].to_b)
+ else if pname == ">=" then
+ return v.bool_instance(args[0].to_b >= args[1].to_b)
+ else if pname == "<=>" then
+ return v.int_instance(args[0].to_b <=> args[1].to_b)
+ else if pname == "to_f" then
+ return v.float_instance(args[0].to_b.to_f)
+ else if pname == "to_i" then
+ return v.int_instance(args[0].to_b.to_i)
+ else if pname == "lshift" then
+ return v.byte_instance(args[0].to_b.lshift(args[1].to_i))
+ else if pname == "rshift" then
+ return v.byte_instance(args[0].to_b.rshift(args[1].to_i))
+ else if pname == "byte_to_s_len" then
+ return v.int_instance(recvval.to_s.length)
+ else if pname == "native_byte_to_s" then
+ var s = recvval.to_s
+ var srecv = args[1].val.as(Buffer)
+ srecv.clear
+ srecv.append(s)
+ srecv.add('\0')
+ return null
+ end
else if cname == "Char" then
var recv = args[0].val.as(Char)
if pname == "ascii" then
return v.bool_instance(recv >= args[1].to_f)
else if pname == "to_i" then
return v.int_instance(recv.to_i)
+ else if pname == "to_b" then
+ return v.byte_instance(recv.to_b)
else if pname == "cos" then
return v.float_instance(args[0].to_f.cos)
else if pname == "sin" then
# The primitive type `Int`
var int_type: MClassType = self.get_primitive_class("Int").mclass_type is lazy
+ # The primitive type `Byte`
+ var byte_type: MClassType = self.get_primitive_class("Byte").mclass_type is lazy
+
# The primitive type `Char`
var char_type: MClassType = self.get_primitive_class("Char").mclass_type is lazy
if name == "Char" then return "char"
if name == "Float" then return "double"
if name == "Int" then return "long"
+ if name == "Byte" then return "unsigned char"
if name == "NativeString" then return "char*"
if mclass.kind == extern_kind then
var ctype = mclass.ctype
if name == "Char" then return "char"
if name == "Float" then return "double"
if name == "Int" then return "long"
+ if name == "Byte" then return "unsigned char"
if name == "NativeString" then return "char*"
if mclass.kind == extern_kind then return "void*"
return "struct nitni_instance *"
force_alive("Float")
force_alive("Char")
force_alive("Pointer")
+ force_alive("Byte")
while not todo.is_empty do
var mmethoddef = todo.shift
-Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:426)
+Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:537)
11
21
31
-Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:426)
+Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:537)
11
21
31
-Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:426)
+Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:537)
11
21
31
Object -> Cloneable [dir=back arrowtail=open style=dashed];
Numeric [
- label = "{interface\nNumeric||+ +(i: OTHER): OTHER\l+ -(i: OTHER): OTHER\l+ unary -(): OTHER\l+ *(i: OTHER): OTHER\l+ /(i: OTHER): OTHER\l+ to_i(): Int\l+ to_f(): Float\l+ is_zero(): Bool\l+ zero(): OTHER\l+ value_of(val: Numeric): OTHER\l}"
+ label = "{interface\nNumeric||+ +(i: OTHER): OTHER\l+ -(i: OTHER): OTHER\l+ unary -(): OTHER\l+ *(i: OTHER): OTHER\l+ /(i: OTHER): OTHER\l+ to_i(): Int\l+ to_f(): Float\l+ to_b(): Byte\l+ is_zero(): Bool\l+ zero(): OTHER\l+ value_of(val: Numeric): OTHER\l}"
]
Comparable -> Numeric [dir=back arrowtail=open style=dashed];
]
Numeric -> Float [dir=back arrowtail=open style=dashed];
+Byte [
+ label = "{Byte||+ %(i: Byte): Byte\l+ lshift(i: Int): Byte\l+ \<\<(i: Int): Byte\l+ rshift(i: Int): Byte\l+ \>\>(i: Int): Byte\l}"
+]
+Discrete -> Byte [dir=back arrowtail=open style=dashed];
+Numeric -> Byte [dir=back arrowtail=open style=dashed];
+
Int [
label = "{Int||+ %(i: Int): Int\l+ lshift(i: Int): Int\l+ \<\<(i: Int): Int\l+ rshift(i: Int): Int\l+ \>\>(i: Int): Int\l+ ascii(): Char\l+ digit_count(b: Int): Int\l+ digit_count_base_10(): Int\l+ to_c(): Char\l+ abs(): Int\l}"
]
Object -> Cloneable [dir=back arrowtail=open style=dashed];
Numeric [
- label = "{interface\nNumeric||+ +(i: OTHER): OTHER\l+ -(i: OTHER): OTHER\l+ unary -(): OTHER\l+ *(i: OTHER): OTHER\l+ /(i: OTHER): OTHER\l+ to_i(): Int\l+ to_f(): Float\l+ is_zero(): Bool\l+ zero(): OTHER\l+ value_of(val: Numeric): OTHER\l}"
+ label = "{interface\nNumeric||+ +(i: OTHER): OTHER\l+ -(i: OTHER): OTHER\l+ unary -(): OTHER\l+ *(i: OTHER): OTHER\l+ /(i: OTHER): OTHER\l+ to_i(): Int\l+ to_f(): Float\l+ to_b(): Byte\l+ is_zero(): Bool\l+ zero(): OTHER\l+ value_of(val: Numeric): OTHER\l}"
]
Comparable -> Numeric [dir=back arrowtail=open style=dashed];
]
Numeric -> Float [dir=back arrowtail=open style=dashed];
+Byte [
+ label = "{Byte||+ %(i: Byte): Byte\l+ lshift(i: Int): Byte\l+ \<\<(i: Int): Byte\l+ rshift(i: Int): Byte\l+ \>\>(i: Int): Byte\l}"
+]
+Discrete -> Byte [dir=back arrowtail=open style=dashed];
+Numeric -> Byte [dir=back arrowtail=open style=dashed];
+
Int [
label = "{Int||+ %(i: Int): Int\l+ lshift(i: Int): Int\l+ \<\<(i: Int): Int\l+ rshift(i: Int): Int\l+ \>\>(i: Int): Int\l+ ascii(): Char\l+ digit_count(b: Int): Int\l+ digit_count_base_10(): Int\l+ to_c(): Char\l+ abs(): Int\l}"
]
--- /dev/null
+0xff
+0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+0xff
+0x01
+0x85
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+var b = 0xFF.to_b
+
+print b
+
+var a = new Array[Byte]
+
+for i in [0 .. 0xFF] do a.push i.to_b
+
+print a.join(", ")
+
+print ((-1).to_b)
+
+print 1.to_b
+print((-123).to_b)