I was looking around the Minecraft's internal packet handling when I saw their VarInt reading code to read the packet length. As a java developer that does not have any course of java I was confused when I saw the statement out |= ( in & 0x7F ) << ( bytes++ * 7 );. Can someone please explain it to me? Thanks in advance!
If you want the whole code, just check the readVarInt function on BungeeCord's Github https://github.com/SpigotMC/BungeeCord/blob/master/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java#L70 .
I didn't look at the link you included, but I would expect this line to be called in a loop to convert a number stored as a collection of 7-bit values back to an int or long.
The line you gave can be expanded to this:
int value = in & 0x7F; // Grab 7 bits of data from "in"
int shift = bytes * 7; // Calculate shift amount based on byte index
bytes = bytes+1; // Increment byte index (from bytes++)
out = out | (value << shift); // Shift value and OR into output integer/long
Related
I have been reading these byte by bytes from streams. Example I read this line like this.
int payloadLength = r.readUnsignedShort();
The problem I have is that 2 bytes value is x3100 so it turns out to be 12544 but I suppose to only read as x31 which makes it to be only 49. How to ignore the extra 00.
Right shift the value by 8 bits and then and it with 0xFF. Right shifting moves the bits 8 bits to the right. Any other bits would also be moved to the right so you need to mask those of by do an ANDing (&) with 0xFF to get rid of them.
int payloadLength = r.readUnsignedShort();
payloadLength = (payloadLength >>> 8)& 0xFF;
System.out.println(payLoadLength);
You may also want to swap the two bytes.
v = 0xa0b;
v = swapBytes(v);
System.out.println(Integer.toHexString(v)); // 0xb0a
public static int swapBytes(int v) {
return ((v << 8)&0xFF00) | ((v >> 8) & 0xFF);
}
Normally, for reading in just 16 bits you would not have to and it with 0xFF since the high order bits are 0's. But I think it is a good practice and will prevent possible problems in the future.
After reading a ByteBuffer into b_array I am trying to convert the ascii values to int.
Output I am expecting is 129 after executing the code as (b_array[] has the decimal equivalents of ascii codes 49,50,59)
Can some one please tell me where am I doing wrong here. I am doing a 0xFF to make it a unsigned value in java and then OR operation to move the bytes.
byte[] b_array=new byte[3];
buffer.get(b_array,0,3);
// Contents inside the b_array in ascii code
// b_array[0]=49;
// b_array[1]=50;
// b_array[2]=57;
int value= b_array[2] & 0xFF | (b_array[1] & 0xFF) << 8 | (b_array[0] & 0xFF) << 16;
System.out.println(value);
Your current approach is effectively treating the three values as a 24-bit number - effectively 0x313239.
It sounds like you should be converting it into a string, then parsing that:
String text = new String(b_array, StandardCharsets.US_ASCII); // "129"
int value = Integer.parseInt(text);
I'm trying to isolate two bytes that are next to each other add them, but there seems to be an extra bit that sometimes shows up and I can't figure out how to get rid of it. It's throwing off the answer.
The code is:
(acc & 0x00000000ff000000L) + ((acc << 8) & 0x00000000ff000000L);
and I'm getting results such as
0x0000000147000000
when it should be
0x0000000047000000
How can I get rid of the 1?
Edit: acc is a long. I'm try to add the 5th and 6th byte and then that value will go into a new long in the 5th byte position.
You need to mask the bits you want at the end, because the addition may carry a bit:
((acc & 0x00000000ff000000L) + ((acc << 8) & 0x00000000ff000000L)) & 0x00000000ff000000L;
I think this might be clearer if you broke it down a little:
acc&=0x00000000FFFF000000L; // isolate bytes 5 and 4
acc+=(acc<<8); // add the two bytes (we'll strip bytes 6 & 4 next)
acc&=0x00000000FF00000000L; // reduce to byte 5 only
which happens to be one less bitwise opperation too.
If I understand correctly, you want to get the value of the 5th and 6th byte, add them together, and store them in a new long that contains just that sum in the 5th byte. This would be done like this:
long 5thByte = acc & 0xff00000000 >>> 32;
long 6thByte = acc & 0xff0000000000 >>> 40;
long sum = 5thByte + 6thByte;
long longWithNewByte = sum << 32;
This will of course carryover to the 6th byte if the sum is higher than 255. To get rid of that carryover, you can use another mask.
long longWithNewByte &= 0xff00000000;
I need to get the tempo value from midi file. I found out, that the set_tempo command has value 0x51, so i have this piece of code:
for (int i = 0; i < tracks[0].size(); i++) {
MidiEvent event = tracks[0].get(i);
MidiMessage message = event.getMessage();
if (message instanceof MetaMessage) {
MetaMessage mm = (MetaMessage) message;
if(mm.getType()==SET_TEMPO){
// now what?
mm.getData();
}
}
}
But the method getData() returns an array of bytes! How can I convert it to normal human form, a.k.a. integer?
I have read it is stored in format like this: "tt tt tt", but the whole big/little endian, signed/unsigned, and variable length things make it too confusing.
Tempo is a 3-byte big-endian integer and Bits Per Minute is calculated asBPM = 60,000,000 / (tt tt tt)
byte[] data = mm.getData();
int tempo = (data[0] & 0xff) << 16 | (data[1] & 0xff) << 8 | (data[2] & 0xff);
int bpm = 60000000 / tempo;
I use:
mpq = ((data[0] & 0x7f) << 14) | ((data[1] & 0x7f) << 7) | (data[2] & 0x7f);
Where mpq represents microseconds per quarter note or microseconds per beat.
The reasoning for this is that Midi messages only use 7 bits in each byte to represent data. It should also be noted that, in Java, a byte data type (of which data is an array) is a signed integer and only has room for 7 data bits.
Since making this post I have had the following response from the MIDI Association:
The parameter number (tttttt) is a 24 bit unsigned integer, in big endian format.
"Set tempo" is a meta-event, belonging to the SMF specification. It applies only to Standard MIDI Files, and like the other meta-events are not supposed to be transmitted over the wires on real time. On the other hand, the Data Byte description that is confusing you applies to the over-the-wire protocol.
The original answer to this topic is therefore correct.
So generally I'm using Netty and it's BigEndianHeapChannelBuffer to receive unsinged short (from c++ software) in Java.
When I do it like this:
buf.readUnsignedByte(); buf.readUnsignedByte();
it returns:
149 and 00. Till now everything is fine. Because server sent 149 as unsigned short [2 bytes].
Instead of this I would like to receive unsigned short (ofc after restarting my application):
buf.readUnsignedShort();
and magic happens. It returns: 38144.
Next step is to retrieve unsigned byte:
short type = buf.readUnsignedByte();
System.out.println(type);
and it returns: 1 which is correct output.
Could anyone help me with this?
I looked deeper and this is what netty does with it:
public short readShort() {
checkReadableBytes(2);
short v = getShort(readerIndex);
readerIndex += 2;
return v;
}
public int readUnsignedShort() {
return readShort() & 0xFFFF;
}
But still I can't figure what is wrong. I would like just be able to read that 149.
You could also borrow a page from the Java DataInputStream.readUnsignedShort() implementation:
public final int readUnsignedShort() throws IOException {
int ch1 = in.read();
int ch2 = in.read();
if ((ch1 | ch2) < 0)
throw new EOFException();
return (ch1 << 8) + (ch2 << 0);
}
The answer is to change endianness. Thanks to Roger who in comments wrote:
Not much magic, 149*256+0=38144. You have specified BigEndian so this seems correct, the most significant byte is sent first
and:
#mickula The short is two bytes, where one of the bytes is "worth" 256 times as much as the other, Since you use BigEndian the first byte is the one "worth" more. Similar to the decimal number 123 of the digits 1, 2, and 3. The first position is with 10 times the next position which is worth 10 times the next, and so on. So 1 * 100 + 2 * 10 + 3 = 123 when the digits are transferred one at a time. If you see 1, 2, and 3 as little endian you would use 1 + 2 * 10 + 3 * 100 = 321. The 256 is because the size of a byte is 256. –
Thanks to his comments I just switched endianess in server bootstrap by adding:
bootstrap.setOption("child.bufferFactory", new
HeapChannelBufferFactory(ByteOrder.LITTLE_ENDIAN));