Reputation: 919
I'm curious how in the Dart programming language a byte is represented by the int
type. I am confused because in Java, which Dart closely resembles, an int
is 32 bits.
I ask because the leading flutter ble library, Flutter Blue, seems to handle List<int>
while handling the ble bytes.
However according to the official documentation: https://flutter.dev/docs/development/platform-integration/platform-channels#codec Uint8List is used - which is what makes sense as the byte[] equivalent.
It seems the unsigned 8 bit integers are just then converted to a 32 bit signed int going from Uint8List
-> List<int>
? i.e. decimal 2 is then converted from 00000010
to 00000000000000000000000000000010
?
It seems this has ramifications if one would like to write a byte stream. Would one need to cast the int
's to Uint8
's?
Upvotes: 2
Views: 5779
Reputation: 71828
The Dart int
type is a 64-bit two's complement number—except when compiled for the web, there it's a 64-bit floating point number with no fractional part (a JavaScript number which has an integer value).
How those values are represented internally depends on optimizations, they can be represented as something smaller if the runtime system knows for sure that the value will fit. That's an optimization, you won't be able to tell the difference.
A "byte" value is an integer in the range 0..255. You can obviously store a byte value in an int
.
The most efficient way to store multiple bytes is a Uint8List
, which implements List<int>
. It stores each element as a single octet.
When you read a value out of a Unit8List
, its byte value is represented by an int
. When you store an int
in the Uint8List
, only the low 8 bites are stored.
So it does expanding reads and truncating writes to move values between an octet and a 64-bit value.
Upvotes: 3
Reputation: 43206
There shouldn't be any confusion as to how an int
can be used to represent bytes because it all comes down to bits and how you store and manipulate them.
For the sake of simplicity, let us assume that an int
is larger than a byte - an int is 32 bits, and a byte is 8 bits. Since, we are dealing with just bits at this point, you can see it is possible for an int to contain 4 bytes because 32/8 is 4 i.e. we can use an int to store bytes without loosing information.
It seems as those the unsigned 8 bit integers are just then converted to a 32 bit signed int going from Uint8List -> List ? i.e. decimal 2 is is then converted from 00000010 to 00000000000000000000000000000010?
You could do it that way, but from I said previously, you can store multiple bytes in an int.
Consider if you have the string Hello, World!
, which comes up to 13 bytes and you want to store these bytes in a list of int
s, given each int
is 32 bits; We only need to use 4 ints to represent this string because 13 * 8
is 104 bits, and four 32bit ints can hold 128 bits of information.
It seems this has ramifications if one would like to write a byte stream. Would need to cast the int's to Uint8's.
Not necessarily.
A byte stream consisting of the bytes 'H', 'e', 'l', 'l', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!'
can be written into a data structure known as a Bit Array
. The bit array for the above stream would look like:
[01001000011001010110110001101100011011110010110000100000010101110110111101110010011011000110010000100001]
Or a list of 32 bit ints:
[1214606444,1865162839,1869769828,33]
If we wanted to convert this list of ints back to bytes, we just need to read the data in chunks of 8 bits to retrieve the original bytes. I will demonstrate it with this simply written dart program:
String readInts(List<int> bitArray) {
final buffer = StringBuffer();
for (var chunk in bitArray) {
for (var offset = 24; offset >= 0; offset -= 8) {
if (chunk < 1 << offset) {
continue;
}
buffer.writeCharCode((chunk >> offset) & 0xff);
}
}
return buffer.toString();
}
void main() {
print(readInts([1214606444,1865162839,1869769828,33]));
}
The same process can be followed to convert the bytes to integers - you just combine every 4 bytes to form a 32 bit integer.
Hello, World!
Of course, you should not need to write such a code by yourself because dart already does this for you in the Uint8List
class
Upvotes: 2
Reputation: 90155
Uint8List
derives from List<int>
; passing a Uint8List
where a List<int>
is expected does not change the representation of the data.
When you read a single int
from a Uint8List
(e.g. with operator []
), you'll get a copy of the octet widened to whatever int
is.
When you write an int
to a Uint8List
(e.g. with operator []=
), the stored value will be truncated to include only the lower 8-bits.
Upvotes: 1
Reputation: 24746
First, let's clear up one thing. An int
is not inherently 32-bits or 64-bits universally. That's just a convention put in place by common languages, including Java. In C, for example, the size of int
is an implementation detail that depends on the compiler, the architecture, and the size of a memory address, so it could be 8, 16, 32, or 64 bits (or on more esoteric platforms, something else entirely, like 24 bits). So the notion that Dart is doing something "wrong" by not having int
be a 32-bit integer type is somewhat absurd.
Now that that, is out of the way, an int
in Dart is not a fixed data type. Like C, it depends on the platform that it is running on.
int
is a 64-bit integerint
is mapped to the JavaScript Number
which is a 64-bit floating point (i.e. double
)int
can be defined as an implementation detailAnd that's it. Dart has no concept of any other integral type. (i.e. There's no such thing as a byte, short, char, long, long long, etc. as primitive types.)
What you are seeing in Uint8List
is an abstraction over a list of 64-bit integers to make it appear like a list of bytes. I'm not sure how it is represented internally (either each "byte" is its own int
, using bit-flags to store 8 bytes worth of information in a single int
, or it's a native implementation doing something else entirely), but at the end of the day it doesn't really matter.
Upvotes: 1
Reputation: 17141
The size of an int
in dart is not completely predictable for local variables as Irn mentions here. The size of the int
may be reduced as an optimization if it is seen as possible.
If you do an explicit conversion from Uint8List
to List<int>
that creates a new List
object, the new object will have int
elements that are a larger size than 8 bits. Possibly 32 bits, maybe 64 bits, maybe less. The compiler will choose based on what it sees. According to the int
class, the default is a signed 64 bit integer.
If you are trying to get from int
s to a Uint8List
, each int
will be truncated to 8 bits.
Upvotes: 1