diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java
index f2b192f59..5543c1cb5 100644
--- a/src/main/java/net/minestom/server/entity/Entity.java
+++ b/src/main/java/net/minestom/server/entity/Entity.java
@@ -42,6 +42,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
+/**
+ * Could be a player, a monster, or an object.
+ *
+ * To create your own entity you probably want to extends {@link ObjectEntity} or {@link EntityCreature} instead.
+ */
public abstract class Entity implements Viewable, EventHandler, DataContainer {
private static final Map entityById = new ConcurrentHashMap<>();
diff --git a/src/main/java/net/minestom/server/instance/Chunk.java b/src/main/java/net/minestom/server/instance/Chunk.java
index 959edf807..3a18e89ce 100644
--- a/src/main/java/net/minestom/server/instance/Chunk.java
+++ b/src/main/java/net/minestom/server/instance/Chunk.java
@@ -1,6 +1,5 @@
package net.minestom.server.instance;
-import io.netty.buffer.ByteBuf;
import net.minestom.server.MinecraftServer;
import net.minestom.server.Viewable;
import net.minestom.server.data.Data;
@@ -32,7 +31,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
-import java.util.function.Consumer;
// TODO light data & API
@@ -69,11 +67,6 @@ public abstract class Chunk implements Viewable, DataContainer {
private final boolean shouldGenerate;
private boolean readOnly;
- // Packet cache
- private volatile boolean enableCachePacket;
- protected volatile boolean packetUpdated;
- private ByteBuf fullDataPacket;
-
protected volatile boolean loaded = true;
protected final Set viewers = new CopyOnWriteArraySet<>();
@@ -89,9 +82,6 @@ public abstract class Chunk implements Viewable, DataContainer {
this.chunkZ = chunkZ;
this.shouldGenerate = shouldGenerate;
- // true by default
- this.enableCachePacket = true;
-
if (biomes != null && biomes.length == BIOME_COUNT) {
this.biomes = biomes;
} else {
@@ -324,51 +314,6 @@ public abstract class Chunk implements Viewable, DataContainer {
this.readOnly = readOnly;
}
- /**
- * Gets if this chunk automatically cache the latest {@link ChunkDataPacket} version.
- *
- * Retrieved with {@link #retrieveDataBuffer(Consumer)}.
- *
- * @return true if the chunk automatically cache the chunk packet
- */
- public boolean enableCachePacket() {
- return enableCachePacket;
- }
-
- /**
- * Enables or disable the automatic {@link ChunkDataPacket} caching.
- *
- * @param enableCachePacket true to enable to chunk packet caching
- */
- public synchronized void setEnableCachePacket(boolean enableCachePacket) {
- this.enableCachePacket = enableCachePacket;
- if (enableCachePacket && fullDataPacket != null) {
- this.fullDataPacket.release();
- this.fullDataPacket = null;
- }
- }
-
- /**
- * Gets the cached data packet.
- *
- * Use {@link #retrieveDataBuffer(Consumer)} to be sure to get the updated version.
- *
- * @return the last cached data packet, can be null or outdated
- */
- public ByteBuf getFullDataPacket() {
- return fullDataPacket;
- }
-
- /**
- * Sets the cached {@link ChunkDataPacket} of this chunk.
- *
- * @param fullDataPacket the new cached chunk packet
- */
- public void setFullDataPacket(ByteBuf fullDataPacket) {
- this.fullDataPacket = fullDataPacket;
- this.packetUpdated = true;
- }
-
/**
* Changes this chunk columnar space.
*
@@ -378,27 +323,6 @@ public abstract class Chunk implements Viewable, DataContainer {
this.columnarSpace = columnarSpace;
}
- /**
- * Retrieves (and cache if needed) the updated data packet.
- *
- * @param consumer the consumer called once the packet is sure to be up-to-date
- */
- public void retrieveDataBuffer(Consumer consumer) {
- final ByteBuf data = getFullDataPacket();
- if (data == null || !packetUpdated) {
- // Packet has never been wrote or is outdated, write it
- PacketWriterUtils.writeCallbackPacket(getFreshFullDataPacket(), packet -> {
- if (enableCachePacket) {
- setFullDataPacket(packet);
- }
- consumer.accept(packet);
- });
- } else {
- // Packet is up-to-date
- consumer.accept(data);
- }
- }
-
/**
* Gets a {@link ChunkDataPacket} which should contain the full chunk.
*
@@ -509,7 +433,7 @@ public abstract class Chunk implements Viewable, DataContainer {
final PlayerConnection playerConnection = player.getPlayerConnection();
// Retrieve & send the buffer to the connection
- retrieveDataBuffer(buf -> playerConnection.sendPacket(buf, true));
+ playerConnection.sendPacket(getFreshFullDataPacket());
// TODO do not hardcode
if (MinecraftServer.isFixLighting()) {
@@ -542,10 +466,8 @@ public abstract class Chunk implements Viewable, DataContainer {
* @param player the player to update the chunk to
*/
public void sendChunkUpdate(@NotNull Player player) {
- retrieveDataBuffer(buf -> {
- final PlayerConnection playerConnection = player.getPlayerConnection();
- playerConnection.sendPacket(buf, true);
- });
+ final PlayerConnection playerConnection = player.getPlayerConnection();
+ playerConnection.sendPacket(getFreshFullDataPacket());
}
/**
@@ -554,14 +476,10 @@ public abstract class Chunk implements Viewable, DataContainer {
public void sendChunkUpdate() {
final Set chunkViewers = getViewers();
if (!chunkViewers.isEmpty()) {
- retrieveDataBuffer(buf -> chunkViewers.forEach(player -> {
+ chunkViewers.forEach(player -> {
final PlayerConnection playerConnection = player.getPlayerConnection();
- if (!PlayerUtils.isNettyClient(playerConnection))
- return;
-
- playerConnection.sendPacket(buf, true);
- }));
-
+ playerConnection.sendPacket(getFreshFullDataPacket());
+ });
}
}
diff --git a/src/main/java/net/minestom/server/instance/DynamicChunk.java b/src/main/java/net/minestom/server/instance/DynamicChunk.java
index f378a3b7f..5ba8408c1 100644
--- a/src/main/java/net/minestom/server/instance/DynamicChunk.java
+++ b/src/main/java/net/minestom/server/instance/DynamicChunk.java
@@ -13,7 +13,6 @@ import net.minestom.server.instance.block.CustomBlock;
import net.minestom.server.network.packet.server.play.ChunkDataPacket;
import net.minestom.server.utils.ArrayUtils;
import net.minestom.server.utils.BlockPosition;
-import net.minestom.server.utils.MathUtils;
import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.BinaryWriter;
import net.minestom.server.utils.block.CustomBlockUtils;
@@ -36,6 +35,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
*/
public class DynamicChunk extends Chunk {
+ private static final int BITS_PER_ENTRY = 15;
+
/**
* Represents the version which will be present in the serialized output.
* Used to define which deserializer to use.
@@ -45,9 +46,11 @@ public class DynamicChunk extends Chunk {
// blocks id based on coordinate, see Chunk#getBlockIndex
// WARNING: those arrays are NOT thread-safe
// and modifying them can cause issue with block data, update, block entity and the cached chunk packet
- protected final short[] blocksStateId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
+ //protected final short[] blocksStateId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
protected final short[] customBlocksId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
+ protected long[][] sectionBlocks = new long[CHUNK_SECTION_COUNT][0];
+
// Used to get all blocks with data (no null)
// Key is still chunk coordinates (see #getBlockIndex)
protected final Int2ObjectMap blocksData = new Int2ObjectOpenHashMap<>();
@@ -80,12 +83,13 @@ public class DynamicChunk extends Chunk {
// True if the block is not complete air without any custom block capabilities
final boolean hasBlock = blockStateId != 0 || customBlockId != 0;
if (hasBlock) {
- this.blocksStateId[index] = blockStateId;
+ setBlockAt(x, y, z, blockStateId);
this.customBlocksId[index] = customBlockId;
} else {
// Block has been deleted, clear cache and return
- this.blocksStateId[index] = 0; // Set to air
+ setBlockAt(x, y, z, (short) 0);
+ //this.blocksStateId[index] = 0; // Set to air
this.customBlocksId[index] = 0; // Remove custom block
this.blocksData.remove(index);
@@ -94,8 +98,6 @@ public class DynamicChunk extends Chunk {
this.updatableBlocksLastUpdate.remove(index);
this.blockEntities.remove(index);
-
- this.packetUpdated = false;
return;
}
@@ -121,8 +123,6 @@ public class DynamicChunk extends Chunk {
} else {
this.blockEntities.remove(index);
}
-
- this.packetUpdated = false;
}
@Override
@@ -156,40 +156,26 @@ public class DynamicChunk extends Chunk {
@Override
public short getBlockStateId(int x, int y, int z) {
final int index = getBlockIndex(x, y, z);
- if (!MathUtils.isBetween(index, 0, blocksStateId.length)) {
- return 0; // TODO: custom invalid block
- }
- return blocksStateId[index];
+ return getBlockAt(x, y, z);
}
@Override
public short getCustomBlockId(int x, int y, int z) {
final int index = getBlockIndex(x, y, z);
- if (!MathUtils.isBetween(index, 0, blocksStateId.length)) {
- return 0; // TODO: custom invalid block
- }
return customBlocksId[index];
}
@Override
protected void refreshBlockValue(int x, int y, int z, short blockStateId, short customBlockId) {
final int blockIndex = getBlockIndex(x, y, z);
- if (!MathUtils.isBetween(blockIndex, 0, blocksStateId.length)) {
- return;
- }
-
- this.blocksStateId[blockIndex] = blockStateId;
+ setBlockAt(x, y, z, blockStateId);
this.customBlocksId[blockIndex] = customBlockId;
}
@Override
protected void refreshBlockStateId(int x, int y, int z, short blockStateId) {
final int blockIndex = getBlockIndex(x, y, z);
- if (!MathUtils.isBetween(blockIndex, 0, blocksStateId.length)) {
- return;
- }
-
- this.blocksStateId[blockIndex] = blockStateId;
+ setBlockAt(x, y, z, blockStateId);
}
@Override
@@ -260,7 +246,7 @@ public class DynamicChunk extends Chunk {
for (byte z = 0; z < CHUNK_SIZE_Z; z++) {
final int index = getBlockIndex(x, y, z);
- final short blockStateId = blocksStateId[index];
+ final short blockStateId = getBlockAt(x, y, z);
final short customBlockId = customBlocksId[index];
// No block at the position
@@ -398,7 +384,8 @@ public class DynamicChunk extends Chunk {
fullDataPacket.biomes = biomes.clone();
fullDataPacket.chunkX = chunkX;
fullDataPacket.chunkZ = chunkZ;
- fullDataPacket.blocksStateId = blocksStateId.clone();
+ fullDataPacket.bitsPerEntry = BITS_PER_ENTRY;
+ fullDataPacket.sectionBlocks = sectionBlocks.clone();
fullDataPacket.customBlocksId = customBlocksId.clone();
fullDataPacket.blockEntities = new HashSet<>(blockEntities);
fullDataPacket.blocksData = new Int2ObjectOpenHashMap<>(blocksData);
@@ -409,7 +396,7 @@ public class DynamicChunk extends Chunk {
@Override
public Chunk copy(@NotNull Instance instance, int chunkX, int chunkZ) {
DynamicChunk dynamicChunk = new DynamicChunk(instance, biomes.clone(), chunkX, chunkZ);
- ArrayUtils.copyToDestination(blocksStateId, dynamicChunk.blocksStateId);
+ dynamicChunk.sectionBlocks = sectionBlocks.clone();
ArrayUtils.copyToDestination(customBlocksId, dynamicChunk.customBlocksId);
dynamicChunk.blocksData.putAll(blocksData);
dynamicChunk.updatableBlocks.addAll(updatableBlocks);
@@ -418,4 +405,82 @@ public class DynamicChunk extends Chunk {
return dynamicChunk;
}
+
+ private void setBlockAt(int x, int y, int z, short blockId) {
+ x %= 16;
+ if (x < 0) {
+ x = CHUNK_SIZE_X + x;
+ }
+ z %= 16;
+ if (z < 0) {
+ z = CHUNK_SIZE_Z + z;
+ }
+
+ final char valuesPerLong = (char) (Long.SIZE / BITS_PER_ENTRY);
+
+ int sectionY = y % CHUNK_SECTION_SIZE;
+ int sectionIndex = (((sectionY * 16) + z) * 16) + x;
+
+ final int index = sectionIndex / valuesPerLong;
+ final int bitIndex = sectionIndex % valuesPerLong * BITS_PER_ENTRY;
+
+ final int section = y / CHUNK_SECTION_SIZE;
+
+ if (sectionBlocks[section].length == 0) {
+ sectionBlocks[section] = new long[getSize()];
+ }
+
+ long[] sectionBlock = sectionBlocks[section];
+
+ //System.out.println("test1 " + binary(sectionBlock[index]));
+ //System.out.println("test2 " + binary(((long) blockId << (bitIndex))));
+ sectionBlock[index] |= ((long) blockId << (bitIndex));
+ }
+
+ private static String binary(long value) {
+ return "0b" + Long.toBinaryString(value);
+ }
+
+ private short getBlockAt(int x, int y, int z) {
+ x %= 16;
+ if (x < 0) {
+ x = CHUNK_SIZE_X + x;
+ }
+ z %= 16;
+ if (z < 0) {
+ z = CHUNK_SIZE_Z + z;
+ }
+
+ final char valuesPerLong = (char) (Long.SIZE / BITS_PER_ENTRY);
+
+ int sectionY = y % CHUNK_SECTION_SIZE;
+ int sectionIndex = (((sectionY * 16) + z) * 16) + x;
+
+ final int index = sectionIndex / valuesPerLong;
+ final int bitIndex = sectionIndex % valuesPerLong * BITS_PER_ENTRY;
+
+ final int section = y / CHUNK_SECTION_SIZE;
+
+ long[] blocks = sectionBlocks[section];
+
+ if (blocks.length == 0) {
+ return 0;
+ }
+
+ long mask = (1 << (bitIndex)) - 1;
+
+ /*System.out.println("data " + index + " " + bitIndex + " " + sectionIndex);
+ System.out.println("POS " + x + " " + y + " " + z);
+ System.out.println("mask " + binary(mask));
+ System.out.println("bin " + binary(blocks[index]));
+ System.out.println("result " + ((blocks[index] >> bitIndex) & mask));*/
+ return (short) (blocks[index] >> bitIndex & mask);
+ }
+
+ private int getSize() {
+ final int blockCount = 16 * 16 * 16; // A whole chunk section
+ final char valuesPerLong = (char) (Long.SIZE / BITS_PER_ENTRY);
+ final int arraySize = blockCount / valuesPerLong;
+ return arraySize;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/net/minestom/server/instance/StaticChunk.java b/src/main/java/net/minestom/server/instance/StaticChunk.java
index 7fd749d26..b34a7757d 100644
--- a/src/main/java/net/minestom/server/instance/StaticChunk.java
+++ b/src/main/java/net/minestom/server/instance/StaticChunk.java
@@ -1,6 +1,5 @@
package net.minestom.server.instance;
-import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minestom.server.data.Data;
import net.minestom.server.instance.block.BlockProvider;
@@ -105,7 +104,7 @@ public class StaticChunk extends Chunk {
final int z = ChunkUtils.blockIndexToChunkPositionZ(i);
blocksStateId[i] = blockProvider.getBlockStateId(x, y, z);
}
- fullDataPacket.blocksStateId = blocksStateId;
+ //fullDataPacket.blocksStateId = blocksStateId;
fullDataPacket.customBlocksId = new short[0];
fullDataPacket.blockEntities = new HashSet<>();
fullDataPacket.blocksData = new Int2ObjectOpenHashMap<>();
@@ -117,10 +116,10 @@ public class StaticChunk extends Chunk {
public Chunk copy(@NotNull Instance instance, int chunkX, int chunkZ) {
StaticChunk staticChunk = new StaticChunk(instance, biomes.clone(), chunkX, chunkZ, blockProvider);
// Prevent re-writing the whole packet since it is static anyway
- final ByteBuf packetBuffer = getFullDataPacket();
+ /*final ByteBuf packetBuffer = getFullDataPacket();
if (packetBuffer != null) {
staticChunk.setFullDataPacket(packetBuffer);
- }
+ }*/
return staticChunk;
}
diff --git a/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java
index d16aa6499..2a74778e6 100644
--- a/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java
+++ b/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java
@@ -5,7 +5,6 @@ import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minestom.server.MinecraftServer;
import net.minestom.server.data.Data;
-import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.block.BlockManager;
import net.minestom.server.instance.block.CustomBlock;
import net.minestom.server.network.packet.server.ServerPacket;
@@ -28,18 +27,18 @@ public class ChunkDataPacket implements ServerPacket {
public Biome[] biomes;
public int chunkX, chunkZ;
- public short[] blocksStateId;
+ public int bitsPerEntry;
+ public long[][] sectionBlocks;
public short[] customBlocksId;
public Set blockEntities;
public Int2ObjectMap blocksData;
- //public Chunk chunk;
public int[] sections;
private static final byte CHUNK_SECTION_COUNT = 16;
- private static final int BITS_PER_ENTRY = 15;
- private static final int MAX_BUFFER_SIZE = (Short.BYTES + Byte.BYTES + 5 * Byte.BYTES + (4096 * BITS_PER_ENTRY / Long.SIZE * Long.BYTES)) * CHUNK_SECTION_COUNT + 256 * Integer.BYTES;
+ private static final int MAX_BITS_PER_ENTRY = 15;
+ private static final int MAX_BUFFER_SIZE = (Short.BYTES + Byte.BYTES + 5 * Byte.BYTES + (4096 * MAX_BITS_PER_ENTRY / Long.SIZE * Long.BYTES)) * CHUNK_SECTION_COUNT + 256 * Integer.BYTES;
@Override
public void write(@NotNull BinaryWriter writer) {
@@ -51,10 +50,11 @@ public class ChunkDataPacket implements ServerPacket {
ByteBuf blocks = Unpooled.buffer(MAX_BUFFER_SIZE);
for (byte i = 0; i < CHUNK_SECTION_COUNT; i++) {
if (fullChunk || (sections.length == CHUNK_SECTION_COUNT && sections[i] != 0)) {
- short[] section = getSection(i);
- if (section != null) { // section contains at least one block
+ final long[] section = sectionBlocks[i];
+ if (section.length > 0) { // section contains at least one block
+ //if (true) {
mask |= 1 << i;
- Utils.writeBlocks(blocks, section, BITS_PER_ENTRY);
+ Utils.writeBlocks(blocks, section, bitsPerEntry);
} else {
mask |= 0;
}
@@ -116,26 +116,6 @@ public class ChunkDataPacket implements ServerPacket {
}
}
- private short[] getSection(byte section) {
- short[] blocks = new short[Chunk.CHUNK_SIZE_X * Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SIZE_Z];
- boolean empty = true;
- for (byte y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) {
- for (byte x = 0; x < Chunk.CHUNK_SIZE_X; x++) {
- for (byte z = 0; z < Chunk.CHUNK_SIZE_Z; z++) {
- final int yPos = (y + Chunk.CHUNK_SECTION_SIZE * section);
- final int index = ChunkUtils.getBlockIndex(x, yPos, z);
- final short blockStateId = blocksStateId[index];
- if (blockStateId != 0)
- empty = false;
-
- final int packetIndex = (((y * 16) + x) * 16) + z;
- blocks[packetIndex] = blockStateId;
- }
- }
- }
- return empty ? null : blocks;
- }
-
@Override
public int getId() {
return ServerPacketIdentifier.CHUNK_DATA;
diff --git a/src/main/java/net/minestom/server/utils/Utils.java b/src/main/java/net/minestom/server/utils/Utils.java
index 13f185d00..8753945a8 100644
--- a/src/main/java/net/minestom/server/utils/Utils.java
+++ b/src/main/java/net/minestom/server/utils/Utils.java
@@ -2,6 +2,7 @@ package net.minestom.server.utils;
import io.netty.buffer.ByteBuf;
import net.minestom.server.instance.Chunk;
+import net.minestom.server.instance.block.Block;
import net.minestom.server.utils.binary.BinaryWriter;
import java.util.UUID;
@@ -115,31 +116,57 @@ public final class Utils {
70409299, 70409299, 0, 69273666, 69273666, 0, 68174084, 68174084, 0, Integer.MIN_VALUE,
0, 5};
- public static void writeBlocks(ByteBuf buffer, short[] blocksId, int bitsPerEntry) {
- short count = 0;
+ public static void writeBlocks(ByteBuf buffer, long[] blocksId, int bitsPerEntry) {
+ /*short count = 0;
for (short id : blocksId)
if (id != 0)
- count++;
+ count++;*/
- buffer.writeShort(count);
+
+ //buffer.writeShort(count);
+ buffer.writeShort(200);
buffer.writeByte((byte) bitsPerEntry);
- int[] blocksData = new int[Chunk.CHUNK_SIZE_X * Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SIZE_Z];
- for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) {
- for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) {
- for (int z = 0; z < Chunk.CHUNK_SIZE_Z; z++) {
- int sectionIndex = (((y * 16) + x) * 16) + z;
- int index = y << 8 | z << 4 | x;
- blocksData[index] = blocksId[sectionIndex];
- }
- }
- }
- final long[] data = encodeBlocks(blocksData, bitsPerEntry);
+
+ final long[] data = blocksId;//encodeBlocksTEST(bitsPerEntry);
writeVarIntBuf(buffer, data.length);
for (long datum : data) {
buffer.writeLong(datum);
}
}
+ public synchronized static long[] encodeBlocksTEST(int bitsPerEntry) {
+ //long test = (Block.TORCH.getBlockId() << (64 - 50 - bitsPerEntry + 1));
+ //System.out.println("BINARY: 0b" + Long.toBinaryString(test) + " " + (64 - 50 - bitsPerEntry + 1));
+ final int blockCount = 16 * 16 * 16; // A whole chunk section
+ final int longSize = Long.SIZE; // 64
+ final char valuesPerLong = (char) (longSize / bitsPerEntry);
+ final int arraySize = blockCount / valuesPerLong;
+
+ long[] data = new long[arraySize];
+ //data[0] = 0b000000000000001_000000000000001_000000000000001_000000000000001L;
+ //data[1] = 0b000000000000001_000000000000001_000000000000001_000000000000010L;
+
+ for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) {
+ for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) {
+ for (int z = 0; z < Chunk.CHUNK_SIZE_Z; z++) {
+ final long blockId = x % 2 == 0 && z % 2 == 0 ? Block.AIR.getBlockId() : Block.LAVA.getBlockId();
+ int sectionIndex = (((y * 16) + z) * 16) + x;
+
+ final int index = sectionIndex / valuesPerLong;
+ final int bitIndex = sectionIndex % valuesPerLong * bitsPerEntry;
+
+ data[index] |= (blockId << bitIndex);
+ }
+ }
+ }
+
+ return data;
+ }
+
+ private static String binary(long value) {
+ return "0b" + Long.toBinaryString(value);
+ }
+
public static long[] encodeBlocks(int[] blocks, int bitsPerEntry) {
final long maxEntryValue = (1L << bitsPerEntry) - 1;
final char valuesPerLong = (char) (64 / bitsPerEntry);
@@ -160,4 +187,5 @@ public final class Utils {
return data;
}
+
}