Reputation: 23800
I have the following method:
int convert3ByteChunkTo24Bits(final byte[] bytes) {
int bitsFor3ByteChunk = 0;
bitsFor3ByteChunk = bitsFor3ByteChunk | (bytes[0] & 0b11111111);
bitsFor3ByteChunk = bitsFor3ByteChunk << 8;
bitsFor3ByteChunk = bitsFor3ByteChunk | (bytes[1] & 0b11111111);
bitsFor3ByteChunk = bitsFor3ByteChunk << 8;
bitsFor3ByteChunk = bitsFor3ByteChunk | (bytes[2] & 0b11111111);
return bitsFor3ByteChunk & 0b00000000_11111111_11111111_11111111;
}
Purpose of the method is to append bytes passed and return it in an int format. Here is a test method I have:
void testConvert3ByteChunkTo24Bits() {
final byte ca = (byte) 0xCA;
final byte fe = (byte) 0xFE;
final byte ba = (byte) 0xBA;
final byte[] man = {ca, fe, ba};
final int bytesIn24Bits = base64EncoderHelper.convert3ByteChunkTo24Bits(man);
System.out.println(bytesIn24Bits);
}
Output of the result will be:
13303482
which in binary is:
00000000110010101111111010111010
This is correct and is the intended results since:
0xCA = 11001010
0xFE = 11111110
0xBA = 10111010
So, so far so good. There is just one thing I do not understand, why do I need the logical ands as in
(bytes[0] & 0b11111111)
When I do not do the bitwise-and operations, so if the implementation is as follows:
int convert3ByteChunkTo24Bits(final byte[] bytes) {
int bitsFor3ByteChunk = 0;
bitsFor3ByteChunk = bitsFor3ByteChunk | (bytes[0]);
bitsFor3ByteChunk = bitsFor3ByteChunk << 8;
bitsFor3ByteChunk = bitsFor3ByteChunk | (bytes[1]);
bitsFor3ByteChunk = bitsFor3ByteChunk << 8;
bitsFor3ByteChunk = bitsFor3ByteChunk | (bytes[2]);
return bitsFor3ByteChunk & 0b00000000_11111111_11111111_11111111;
}
result will be:
00000000111111111111111110111010
What am I missing? What does
bytes[0] & 0b11111111
fix here? (I found it kind of by trial - error and still curious why it works..)
Upvotes: 1
Views: 893
Reputation: 661
The human-readable equivalent is simply
Byte.toUnsignedInt(b)
or if you want it really explicitly but inefficiently:
b < 0? b + 256: b
Honestly, writing code like that when jdk methods or simple math exist to do the same thing is just obfuscation and I hope people who write code like that don't ever end up in a team with other people... Unless they're writing a device driver in C or some other inherently low-level byte-wrangling project like that.
Upvotes: 0
Reputation: 36431
In:
bitsFor3ByteChunk = bitsFor3ByteChunk | (bytes[0] & 0b11111111);
bytes[0]
is first extended to int
value (integers are all represented in two's-complement). Then for a value like 0xFF
stored in a byte
, the corresponding int
value will be 0xFFFFFFFF
. It is then masked to retain the lower 8 bits.
Upvotes: 1
Reputation: 30335
It handles signed bytes.
say byte[0]
has a value of 0b10000000. That's looks like 128 to you and me, but to Java it looks like -128
, due to two's complement arithmatic. When you use that value in a calculation with an int, it'll be sign-extended to an int with a value of 11111111111111111111111110000000
, which is an int with a value of -128
To avoid the problem, this code performs a bitwise and with 0b11111111
. This is an integer operation, so the byte is implicitly cast to the int 11111111111111111111111110000000
, but the and
turns it into 00000000000000000000000010000000
, which is the desired 128.
This is a hack for the notorious Java problem of not having unsigned types.
Upvotes: 1