2020-04-24 03:25:58 +02:00
|
|
|
package net.minestom.server.utils;
|
2019-08-03 15:25:24 +02:00
|
|
|
|
2021-03-19 05:51:42 +01:00
|
|
|
import it.unimi.dsi.fastutil.shorts.Short2ShortLinkedOpenHashMap;
|
2021-06-12 10:39:44 +02:00
|
|
|
import net.minestom.server.instance.palette.Palette;
|
2021-08-03 12:57:13 +02:00
|
|
|
import org.jetbrains.annotations.ApiStatus;
|
2021-03-26 15:58:46 +01:00
|
|
|
import org.jetbrains.annotations.NotNull;
|
2019-08-03 15:25:24 +02:00
|
|
|
|
2021-08-03 12:57:13 +02:00
|
|
|
import java.nio.ByteBuffer;
|
2020-10-04 03:04:51 +02:00
|
|
|
import java.util.UUID;
|
|
|
|
|
2021-08-03 12:57:13 +02:00
|
|
|
@ApiStatus.Internal
|
2020-08-07 08:10:10 +02:00
|
|
|
public final class Utils {
|
|
|
|
|
|
|
|
private Utils() {
|
|
|
|
}
|
2019-08-03 15:25:24 +02:00
|
|
|
|
2020-06-28 22:07:48 +02:00
|
|
|
public static int getVarIntSize(int input) {
|
|
|
|
return (input & 0xFFFFFF80) == 0
|
|
|
|
? 1 : (input & 0xFFFFC000) == 0
|
|
|
|
? 2 : (input & 0xFFE00000) == 0
|
|
|
|
? 3 : (input & 0xF0000000) == 0
|
|
|
|
? 4 : 5;
|
|
|
|
}
|
|
|
|
|
2021-08-03 12:57:13 +02:00
|
|
|
public static void writeVarInt(ByteBuffer buf, int value) {
|
2021-05-09 07:36:09 +02:00
|
|
|
if ((value & (0xFFFFFFFF << 7)) == 0) {
|
2021-08-03 12:57:13 +02:00
|
|
|
buf.put((byte) value);
|
2021-05-09 07:36:09 +02:00
|
|
|
} else if ((value & (0xFFFFFFFF << 14)) == 0) {
|
2021-08-03 12:57:13 +02:00
|
|
|
buf.putShort((short) ((value & 0x7F | 0x80) << 8 | (value >>> 7)));
|
2021-05-09 07:36:09 +02:00
|
|
|
} else if ((value & (0xFFFFFFFF << 21)) == 0) {
|
2021-08-03 12:57:13 +02:00
|
|
|
buf.put((byte) (value & 0x7F | 0x80));
|
|
|
|
buf.put((byte) ((value >>> 7) & 0x7F | 0x80));
|
|
|
|
buf.put((byte) (value >>> 14));
|
|
|
|
} else if ((value & (0xFFFFFFFF << 28)) == 0) {
|
|
|
|
buf.putInt((value & 0x7F | 0x80) << 24 | (((value >>> 7) & 0x7F | 0x80) << 16)
|
|
|
|
| ((value >>> 14) & 0x7F | 0x80) << 8 | (value >>> 21));
|
2021-05-09 07:36:09 +02:00
|
|
|
} else {
|
2021-08-03 12:57:13 +02:00
|
|
|
buf.putInt((value & 0x7F | 0x80) << 24 | ((value >>> 7) & 0x7F | 0x80) << 16
|
|
|
|
| ((value >>> 14) & 0x7F | 0x80) << 8 | ((value >>> 21) & 0x7F | 0x80));
|
|
|
|
buf.put((byte) (value >>> 28));
|
2021-05-09 07:36:09 +02:00
|
|
|
}
|
2019-09-02 06:02:12 +02:00
|
|
|
}
|
|
|
|
|
2021-08-03 12:57:13 +02:00
|
|
|
public static void writeVarIntHeader(@NotNull ByteBuffer buffer, int startIndex, int value) {
|
|
|
|
final int indexCache = buffer.position();
|
|
|
|
buffer.position(startIndex);
|
|
|
|
buffer.put((byte) (value & 0x7F | 0x80));
|
|
|
|
buffer.put((byte) ((value >>> 7) & 0x7F | 0x80));
|
|
|
|
buffer.put((byte) (value >>> 14));
|
|
|
|
buffer.position(indexCache);
|
2021-03-26 15:58:46 +01:00
|
|
|
}
|
|
|
|
|
2021-08-03 12:57:13 +02:00
|
|
|
public static int writeEmptyVarIntHeader(@NotNull ByteBuffer buffer) {
|
|
|
|
final int index = buffer.position();
|
|
|
|
buffer.putShort((short) 0);
|
|
|
|
buffer.put((byte) 0);
|
2021-03-26 15:58:46 +01:00
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
2021-08-03 12:57:13 +02:00
|
|
|
public static int readVarInt(ByteBuffer buf) {
|
2021-05-11 03:10:18 +02:00
|
|
|
int i = 0;
|
2021-08-03 12:57:13 +02:00
|
|
|
final int maxRead = Math.min(5, buf.remaining());
|
2021-05-11 03:10:18 +02:00
|
|
|
for (int j = 0; j < maxRead; j++) {
|
2021-08-03 12:57:13 +02:00
|
|
|
final int k = buf.get();
|
2021-05-11 03:10:18 +02:00
|
|
|
i |= (k & 0x7F) << j * 7;
|
|
|
|
if ((k & 0x80) != 128) {
|
|
|
|
return i;
|
2020-04-17 01:16:02 +02:00
|
|
|
}
|
2021-05-11 03:10:18 +02:00
|
|
|
}
|
|
|
|
throw new RuntimeException("VarInt is too big");
|
2019-08-03 15:25:24 +02:00
|
|
|
}
|
|
|
|
|
2021-08-03 12:57:13 +02:00
|
|
|
public static long readVarLong(@NotNull ByteBuffer buffer) {
|
2021-02-06 18:58:52 +01:00
|
|
|
int numRead = 0;
|
|
|
|
long result = 0;
|
|
|
|
byte read;
|
|
|
|
do {
|
2021-08-03 12:57:13 +02:00
|
|
|
read = buffer.get();
|
2021-02-06 18:58:52 +01:00
|
|
|
long value = (read & 0b01111111);
|
|
|
|
result |= (value << (7 * numRead));
|
|
|
|
|
|
|
|
numRead++;
|
|
|
|
if (numRead > 10) {
|
|
|
|
throw new RuntimeException("VarLong is too big");
|
|
|
|
}
|
|
|
|
} while ((read & 0b10000000) != 0);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-08-03 19:44:10 +02:00
|
|
|
public static void writeVarLong(ByteBuffer buffer, long value) {
|
2020-05-26 19:22:47 +02:00
|
|
|
do {
|
|
|
|
byte temp = (byte) (value & 0b01111111);
|
|
|
|
value >>>= 7;
|
|
|
|
if (value != 0) {
|
|
|
|
temp |= 0b10000000;
|
|
|
|
}
|
2021-08-03 19:44:10 +02:00
|
|
|
buffer.put(temp);
|
2020-05-26 19:22:47 +02:00
|
|
|
} while (value != 0);
|
|
|
|
}
|
|
|
|
|
2020-10-04 03:04:51 +02:00
|
|
|
public static int[] uuidToIntArray(UUID uuid) {
|
|
|
|
int[] array = new int[4];
|
|
|
|
|
|
|
|
final long uuidMost = uuid.getMostSignificantBits();
|
|
|
|
final long uuidLeast = uuid.getLeastSignificantBits();
|
|
|
|
|
|
|
|
array[0] = (int) (uuidMost >> 32);
|
|
|
|
array[1] = (int) uuidMost;
|
|
|
|
|
|
|
|
array[2] = (int) (uuidLeast >> 32);
|
|
|
|
array[3] = (int) uuidLeast;
|
|
|
|
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static UUID intArrayToUuid(int[] array) {
|
|
|
|
final long uuidMost = (long) array[0] << 32 | array[1] & 0xFFFFFFFFL;
|
|
|
|
final long uuidLeast = (long) array[2] << 32 | array[3] & 0xFFFFFFFFL;
|
|
|
|
|
|
|
|
return new UUID(uuidMost, uuidLeast);
|
|
|
|
}
|
|
|
|
|
2021-08-03 12:57:13 +02:00
|
|
|
public static void writePaletteBlocks(ByteBuffer buffer, Palette palette) {
|
2021-06-12 10:39:44 +02:00
|
|
|
final short blockCount = palette.getBlockCount();
|
|
|
|
final int bitsPerEntry = palette.getBitsPerEntry();
|
2021-03-19 05:51:42 +01:00
|
|
|
|
2021-08-03 12:57:13 +02:00
|
|
|
buffer.putShort(blockCount);
|
|
|
|
buffer.put((byte) bitsPerEntry);
|
2020-11-01 22:53:36 +01:00
|
|
|
|
2020-11-11 17:13:36 +01:00
|
|
|
// Palette
|
|
|
|
if (bitsPerEntry < 9) {
|
|
|
|
// Palette has to exist
|
2021-06-12 10:39:44 +02:00
|
|
|
final Short2ShortLinkedOpenHashMap paletteBlockMap = palette.getPaletteBlockMap();
|
2021-05-11 03:10:18 +02:00
|
|
|
writeVarInt(buffer, paletteBlockMap.size());
|
2021-03-19 05:51:42 +01:00
|
|
|
for (short paletteValue : paletteBlockMap.values()) {
|
2021-05-11 03:10:18 +02:00
|
|
|
writeVarInt(buffer, paletteValue);
|
2020-11-11 17:13:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-12 10:39:44 +02:00
|
|
|
final long[] blocks = palette.getBlocks();
|
2021-05-11 03:10:18 +02:00
|
|
|
writeVarInt(buffer, blocks.length);
|
2021-03-19 05:51:42 +01:00
|
|
|
for (long datum : blocks) {
|
2021-08-03 12:57:13 +02:00
|
|
|
buffer.putLong(datum);
|
2020-11-01 22:53:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-14 09:02:29 +01:00
|
|
|
private static final int[] MAGIC = {
|
|
|
|
-1, -1, 0, Integer.MIN_VALUE, 0, 0, 1431655765, 1431655765, 0, Integer.MIN_VALUE,
|
|
|
|
0, 1, 858993459, 858993459, 0, 715827882, 715827882, 0, 613566756, 613566756,
|
|
|
|
0, Integer.MIN_VALUE, 0, 2, 477218588, 477218588, 0, 429496729, 429496729, 0,
|
|
|
|
390451572, 390451572, 0, 357913941, 357913941, 0, 330382099, 330382099, 0, 306783378,
|
|
|
|
306783378, 0, 286331153, 286331153, 0, Integer.MIN_VALUE, 0, 3, 252645135, 252645135,
|
|
|
|
0, 238609294, 238609294, 0, 226050910, 226050910, 0, 214748364, 214748364, 0,
|
|
|
|
204522252, 204522252, 0, 195225786, 195225786, 0, 186737708, 186737708, 0, 178956970,
|
|
|
|
178956970, 0, 171798691, 171798691, 0, 165191049, 165191049, 0, 159072862, 159072862,
|
|
|
|
0, 153391689, 153391689, 0, 148102320, 148102320, 0, 143165576, 143165576, 0,
|
|
|
|
138547332, 138547332, 0, Integer.MIN_VALUE, 0, 4, 130150524, 130150524, 0, 126322567,
|
|
|
|
126322567, 0, 122713351, 122713351, 0, 119304647, 119304647, 0, 116080197, 116080197,
|
|
|
|
0, 113025455, 113025455, 0, 110127366, 110127366, 0, 107374182, 107374182, 0,
|
|
|
|
104755299, 104755299, 0, 102261126, 102261126, 0, 99882960, 99882960, 0, 97612893,
|
|
|
|
97612893, 0, 95443717, 95443717, 0, 93368854, 93368854, 0, 91382282, 91382282,
|
|
|
|
0, 89478485, 89478485, 0, 87652393, 87652393, 0, 85899345, 85899345, 0,
|
|
|
|
84215045, 84215045, 0, 82595524, 82595524, 0, 81037118, 81037118, 0, 79536431,
|
|
|
|
79536431, 0, 78090314, 78090314, 0, 76695844, 76695844, 0, 75350303, 75350303,
|
|
|
|
0, 74051160, 74051160, 0, 72796055, 72796055, 0, 71582788, 71582788, 0,
|
|
|
|
70409299, 70409299, 0, 69273666, 69273666, 0, 68174084, 68174084, 0, Integer.MIN_VALUE,
|
|
|
|
0, 5};
|
2020-07-06 12:39:48 +02:00
|
|
|
|
2020-06-25 21:05:58 +02:00
|
|
|
public static long[] encodeBlocks(int[] blocks, int bitsPerEntry) {
|
2020-08-21 13:16:44 +02:00
|
|
|
final long maxEntryValue = (1L << bitsPerEntry) - 1;
|
|
|
|
final char valuesPerLong = (char) (64 / bitsPerEntry);
|
|
|
|
final int magicIndex = 3 * (valuesPerLong - 1);
|
|
|
|
final long divideMul = Integer.toUnsignedLong(MAGIC[magicIndex]);
|
|
|
|
final long divideAdd = Integer.toUnsignedLong(MAGIC[magicIndex + 1]);
|
|
|
|
final int divideShift = MAGIC[magicIndex + 2];
|
|
|
|
final int size = (blocks.length + valuesPerLong - 1) / valuesPerLong;
|
2020-06-25 21:05:58 +02:00
|
|
|
|
|
|
|
long[] data = new long[size];
|
|
|
|
|
|
|
|
for (int i = 0; i < blocks.length; i++) {
|
2020-08-21 13:16:44 +02:00
|
|
|
final long value = blocks[i];
|
|
|
|
final int cellIndex = (int) (i * divideMul + divideAdd >> 32L >> divideShift);
|
|
|
|
final int bitIndex = (i - cellIndex * valuesPerLong) * bitsPerEntry;
|
2020-06-25 21:05:58 +02:00
|
|
|
data[cellIndex] = data[cellIndex] & ~(maxEntryValue << bitIndex) | (value & maxEntryValue) << bitIndex;
|
|
|
|
}
|
2019-08-10 04:16:01 +02:00
|
|
|
|
2020-06-25 21:05:58 +02:00
|
|
|
return data;
|
|
|
|
}
|
2020-11-01 22:53:36 +01:00
|
|
|
|
2019-08-03 15:25:24 +02:00
|
|
|
}
|