diff --git a/src/main/java/net/minecraft/nbt/EbsSaveFakeNbt.java b/src/main/java/net/minecraft/nbt/EbsSaveFakeNbt.java new file mode 100644 index 0000000..5af2d35 --- /dev/null +++ b/src/main/java/net/minecraft/nbt/EbsSaveFakeNbt.java @@ -0,0 +1,237 @@ +package net.minecraft.nbt; + +import io.netty.util.IllegalReferenceCountException; +import io.netty.util.ReferenceCounted; +import net.minecraft.world.chunk.storage.ExtendedBlockStorage; +import org.ultramine.server.chunk.alloc.MemSlot; +import org.ultramine.server.internal.LambdaHolder; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + +// Localed in net.minecraft.nbt package due to access issues +public class EbsSaveFakeNbt extends NBTTagCompound implements ReferenceCounted +{ + private static final byte[] EMPTY_2048_ARR = new byte[2048]; + private static final ThreadLocal LOCAL_BUFFER = ThreadLocal.withInitial(LambdaHolder.newByteArray(4096)); + private static final AtomicIntegerFieldUpdater REF_CNT_UPDATER = AtomicIntegerFieldUpdater.newUpdater(EbsSaveFakeNbt.class, "refCnt"); + private final ExtendedBlockStorage ebs; + private final boolean hasNoSky; + + private volatile boolean isNbt; + private volatile int refCnt = 1; + + public EbsSaveFakeNbt(ExtendedBlockStorage ebs, boolean hasNoSky) + { + super(Collections.emptyMap()); + this.ebs = ebs; + this.hasNoSky = hasNoSky; + } + + public ExtendedBlockStorage getEbs() + { + return ebs; + } + + public void convertToNbt() + { + if(isNbt) + return; + createMap(0); + setByte("Y", (byte)(ebs.getYLocation() >> 4 & 255)); + MemSlot slot = ebs.getSlot(); + setByteArray("Blocks", slot.copyLSB()); + setByteArray("Add", slot.copyMSB()); + setByteArray("Data", slot.copyBlockMetadata()); + setByteArray("BlockLight", slot.copyBlocklight()); + setByteArray("SkyLight", hasNoSky ? new byte[2048] : slot.copySkylight()); + + isNbt = true; + } + + @Override + public void write(DataOutput out) throws IOException + { + if(isNbt) + { + super.write(out); + return; + } + writeNbt(out, "Y", new NBTTagByte((byte)(ebs.getYLocation() >> 4 & 255))); + MemSlot slot = ebs.getSlot(); + byte[] buf = LOCAL_BUFFER.get(); + slot.copyLSB(buf, 0); + writeByteArray(out, "Blocks", buf, 0, 4096); + slot.copyMSB(buf, 0); + writeByteArray(out, "Add", buf, 0, 2048); + slot.copyBlockMetadata(buf, 0); + writeByteArray(out, "Data", buf, 0, 2048); + slot.copyBlocklight(buf, 0); + writeByteArray(out, "BlockLight", buf, 0, 2048); + if(hasNoSky) + { + writeByteArray(out, "SkyLight", EMPTY_2048_ARR, 0, 2048); + } + else + { + slot.copySkylight(buf, 0); + writeByteArray(out, "SkyLight", buf, 0, 2048); + } + out.writeByte(0); + } + + private static void writeNbt(DataOutput out, String key, NBTBase nbt) throws IOException + { + out.writeByte(nbt.getId()); + + if(nbt.getId() != 0) + { + out.writeUTF(key); + nbt.write(out); + } + } + + private static void writeByteArray(DataOutput out, String key, byte[] byteArray, int off, int len) throws IOException + { + out.writeByte((byte)7); + out.writeUTF(key); + out.writeInt(len - off); + out.write(byteArray, off, len); + } + + @Override + public void func_152446_a(DataInput p_152446_1_, int p_152446_2_, NBTSizeTracker p_152446_3_) throws IOException + { + if(!isNbt) + throw new UnsupportedOperationException(); + super.func_152446_a(p_152446_1_, p_152446_2_, p_152446_3_); + } + + @Override + public String toString() + { + if(!isNbt) + return ""; + return super.toString(); + } + + @Override + public NBTBase copy() + { + if(!isNbt) + return new EbsSaveFakeNbt(ebs.copy(), hasNoSky); + return super.copy(); + } + + @Override + public boolean equals(Object o) + { + if(!isNbt) + throw new UnsupportedOperationException(); + return super.equals(o); + } + + @Override + public int hashCode() + { + if(!isNbt) + throw new UnsupportedOperationException(); + return super.hashCode(); + } + + // Copied from io.netty.buffer.AbstractReferenceCountedByteBuf + + @Override + public final int refCnt() { + return REF_CNT_UPDATER.get(this); + } + + @Override + public EbsSaveFakeNbt retain() { + for (;;) { + int refCnt = this.refCnt; + if (refCnt == 0) { + throw new IllegalReferenceCountException(0, 1); + } + if (refCnt == Integer.MAX_VALUE) { + throw new IllegalReferenceCountException(Integer.MAX_VALUE, 1); + } + if (REF_CNT_UPDATER.compareAndSet(this, refCnt, refCnt + 1)) { + break; + } + } + return this; + } + + @Override + public EbsSaveFakeNbt retain(int increment) { + if (increment <= 0) { + throw new IllegalArgumentException("increment: " + increment + " (expected: > 0)"); + } + + for (;;) { + int refCnt = this.refCnt; + if (refCnt == 0) { + throw new IllegalReferenceCountException(0, increment); + } + if (refCnt > Integer.MAX_VALUE - increment) { + throw new IllegalReferenceCountException(refCnt, increment); + } + if (REF_CNT_UPDATER.compareAndSet(this, refCnt, refCnt + increment)) { + break; + } + } + return this; + } + + @Override + public final boolean release() { + for (;;) { + int refCnt = this.refCnt; + if (refCnt == 0) { + throw new IllegalReferenceCountException(0, -1); + } + + if (REF_CNT_UPDATER.compareAndSet(this, refCnt, refCnt - 1)) { + if (refCnt == 1) { + deallocate(); + return true; + } + return false; + } + } + } + + @Override + public final boolean release(int decrement) { + if (decrement <= 0) { + throw new IllegalArgumentException("decrement: " + decrement + " (expected: > 0)"); + } + + for (;;) { + int refCnt = this.refCnt; + if (refCnt < decrement) { + throw new IllegalReferenceCountException(refCnt, -decrement); + } + + if (REF_CNT_UPDATER.compareAndSet(this, refCnt, refCnt - decrement)) { + if (refCnt == decrement) { + deallocate(); + return true; + } + return false; + } + } + } + + /** + * Called once {@link #refCnt()} is equals 0. + */ + private void deallocate() + { + ebs.release(); + } +} diff --git a/src/main/java/net/minecraft/nbt/NBTTagCompound.java b/src/main/java/net/minecraft/nbt/NBTTagCompound.java index 73d87b9..fa293bf 100644 --- a/src/main/java/net/minecraft/nbt/NBTTagCompound.java +++ b/src/main/java/net/minecraft/nbt/NBTTagCompound.java @@ -10,16 +10,26 @@ import java.util.concurrent.Callable; import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReportCategory; +import net.minecraft.util.MathHelper; import net.minecraft.util.ReportedException; +import net.openhft.koloboke.collect.hash.HashConfig; +import net.openhft.koloboke.collect.map.hash.HashObjObjMapFactory; +import net.openhft.koloboke.collect.map.hash.HashObjObjMaps; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.ultramine.server.internal.LambdaHolder; public class NBTTagCompound extends NBTBase { private static final Logger logger = LogManager.getLogger(); - private Map tagMap = new HashMap(); + private Map tagMap; private static final String __OBFID = "CL_00001215"; + public NBTTagCompound() + { + this(0); + } + void write(DataOutput p_74734_1_) throws IOException { Iterator iterator = this.tagMap.keySet().iterator(); @@ -336,7 +346,7 @@ public NBTBase copy() { - NBTTagCompound nbttagcompound = new NBTTagCompound(); + NBTTagCompound nbttagcompound = new NBTTagCompound(tagMap.size()); Iterator iterator = this.tagMap.keySet().iterator(); while (iterator.hasNext()) @@ -407,4 +417,53 @@ throw new ReportedException(crashreport); } } + + /*======================================== ULTRAMINE START =====================================*/ + + private static final boolean USE_KOLOBOKE_MAP = Boolean.getBoolean("org.ultramine.core.nbt.useKolobokeMap"); + private static final ThreadLocal LOCAL_USE_KOLOBOKE_MAP = ThreadLocal.withInitial(LambdaHolder.BOOLEAN_FALSE_SUPPLIER); + private static final HashObjObjMapFactory K_MAP_FACTORY = HashObjObjMaps.getDefaultFactory() + .withHashConfig(HashConfig.getDefault().withGrowFactor(1.5).withMaxLoad(1.0).withTargetLoad(0.9)); // QHash used + + public static boolean setUseKolobokeMap(boolean isUse) + { + boolean toRet = LOCAL_USE_KOLOBOKE_MAP.get(); + LOCAL_USE_KOLOBOKE_MAP.set(isUse); + return toRet; + } + + public NBTTagCompound(Map tagMap) + { + this.tagMap = tagMap; + } + + public NBTTagCompound(int expectedSize) + { + createMap(expectedSize); + } + + @SuppressWarnings("unchecked") + public Map geTagMap() + { + return tagMap; + } + + public void setTagMap(Map tagMap) + { + this.tagMap = tagMap; + } + + public void clear() + { + if(tagMap == null) + createMap(0); + else + tagMap.clear(); + } + + protected void createMap(int expectedSize) + { + boolean useKolobokeMap = USE_KOLOBOKE_MAP || LOCAL_USE_KOLOBOKE_MAP.get(); + this.tagMap = USE_KOLOBOKE_MAP ? K_MAP_FACTORY.newMutableMap(expectedSize) : expectedSize == 0 ? new HashMap() : new HashMap(MathHelper.ceiling_float_int(expectedSize / 0.75f)); + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/nbt/NBTTagList.java b/src/main/java/net/minecraft/nbt/NBTTagList.java index a70e04c..3801600 100644 --- a/src/main/java/net/minecraft/nbt/NBTTagList.java +++ b/src/main/java/net/minecraft/nbt/NBTTagList.java @@ -9,10 +9,25 @@ public class NBTTagList extends NBTBase { - private List tagList = new ArrayList(); + private List tagList; private byte tagType = 0; private static final String __OBFID = "CL_00001224"; + public NBTTagList(List tagList) + { + this.tagList = tagList; + } + + public NBTTagList(int size) + { + this(new ArrayList(size)); + } + + public NBTTagList() + { + this(new ArrayList()); + } + void write(DataOutput p_74734_1_) throws IOException { if (!this.tagList.isEmpty()) @@ -190,7 +205,7 @@ public NBTBase copy() { - NBTTagList nbttaglist = new NBTTagList(); + NBTTagList nbttaglist = new NBTTagList(tagList.size()); nbttaglist.tagType = this.tagType; Iterator iterator = this.tagList.iterator(); diff --git a/src/main/java/net/minecraft/world/chunk/storage/AnvilChunkLoader.java b/src/main/java/net/minecraft/world/chunk/storage/AnvilChunkLoader.java index 8e09b10..264bf6d 100644 --- a/src/main/java/net/minecraft/world/chunk/storage/AnvilChunkLoader.java +++ b/src/main/java/net/minecraft/world/chunk/storage/AnvilChunkLoader.java @@ -9,10 +9,13 @@ import java.util.List; import java.util.Set; +import com.google.common.base.Function; +import com.google.common.collect.Lists; import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityList; import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.EbsSaveFakeNbt; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.tileentity.TileEntity; @@ -37,13 +40,33 @@ import cpw.mods.fml.common.FMLLog; +import javax.annotation.Nullable; + public class AnvilChunkLoader implements IChunkLoader, IThreadedFileIO { private static final Logger logger = LogManager.getLogger(); protected final IntObjMap pendingSaves = HashIntObjMaps.newMutableMap(); - private final List chunksToRemove = new ArrayList(); //mods compatibility + private final List chunksToRemoveUm = new ArrayList<>(); + private final List chunksToRemove = Lists.transform(chunksToRemoveUm, new Function() + { + @Nullable + @Override + public PendingChunk apply(@Nullable PendingChunk input) + { + if(input == null) + return null; + NBTTagList sections = input.nbtTags.getCompoundTag("Level").getTagList("Sections", 10); + for(int i = 0; i < sections.tagCount(); i++) + { + NBTTagCompound nbt = sections.getCompoundTagAt(i); + if(nbt instanceof EbsSaveFakeNbt) + ((EbsSaveFakeNbt)nbt).convertToNbt(); + } + return input; + } + }); //mods compatibility private final Set pendingAnvilChunksCoordinates = new VanillaChunkCoordIntPairSet(pendingSaves.keySet()); //mods compatibility - protected Object syncLockObject = new Object(); + protected final Object syncLockObject = new Object(); public File chunkSaveLocation; private static final String __OBFID = "CL_00000384"; @@ -78,6 +101,7 @@ NBTTagCompound nbttagcompound = (NBTTagCompound) data[1]; this.loadEntities(par1World, nbttagcompound.getCompoundTag("Level"), chunk); MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Load(chunk, nbttagcompound)); + releaseNbt(nbttagcompound); return chunk; } @@ -96,6 +120,8 @@ if(anvilchunkloaderpending != null) { nbttagcompound = anvilchunkloaderpending.nbtTags; + + retainNbt(nbttagcompound); } } @@ -181,10 +207,11 @@ { par1World.checkSessionLock(); + boolean prevValue = NBTTagCompound.setUseKolobokeMap(true); try { NBTTagCompound nbttagcompound = new NBTTagCompound(); - NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + NBTTagCompound nbttagcompound1 = new NBTTagCompound(13); nbttagcompound.setTag("Level", nbttagcompound1); this.writeChunkToNBT(par2Chunk, par1World, nbttagcompound1); MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Save(par2Chunk, nbttagcompound)); @@ -194,6 +221,10 @@ { exception.printStackTrace(); } + finally + { + NBTTagCompound.setUseKolobokeMap(prevValue); + } } protected void addChunkToPending(ChunkCoordIntPair par1ChunkCoordIntPair, NBTTagCompound par2NBTTagCompound) @@ -204,11 +235,11 @@ { // if (this.pendingAnvilChunksCoordinates.contains(par1ChunkCoordIntPair)) // { -// for (int i = 0; i < this.chunksToRemove.size(); ++i) +// for (int i = 0; i < this.chunksToRemoveUm.size(); ++i) // { -// if (((AnvilChunkLoader.PendingChunk)this.chunksToRemove.get(i)).chunkCoordinate.equals(par1ChunkCoordIntPair)) +// if (((AnvilChunkLoader.PendingChunk)this.chunksToRemoveUm.get(i)).chunkCoordinate.equals(par1ChunkCoordIntPair)) // { -// this.chunksToRemove.set(i, new AnvilChunkLoader.PendingChunk(par1ChunkCoordIntPair, par2NBTTagCompound)); +// this.chunksToRemoveUm.set(i, new AnvilChunkLoader.PendingChunk(par1ChunkCoordIntPair, par2NBTTagCompound)); // return; // } // } @@ -219,14 +250,14 @@ PendingChunk pendingChunk = new PendingChunk(par1ChunkCoordIntPair, par2NBTTagCompound); if(pendingSaves.put(hash, pendingChunk) != null) { - for(int i = 0, s = chunksToRemove.size(); i < s; i++) - if(chunksToRemove.get(i).chunkCoordinate.equals(par1ChunkCoordIntPair)) + for(int i = 0, s = chunksToRemoveUm.size(); i < s; i++) + if(chunksToRemoveUm.get(i).chunkCoordinate.equals(par1ChunkCoordIntPair)) { - chunksToRemove.set(i, pendingChunk); + chunksToRemoveUm.set(i, pendingChunk); return; } } - chunksToRemove.add(pendingChunk); + chunksToRemoveUm.add(pendingChunk); //this.pendingAnvilChunksCoordinates.add(par1ChunkCoordIntPair); ThreadedFileIOBase.threadedIOInstance.queueIO(this); } @@ -265,8 +296,11 @@ synchronized (this.syncLockObject) { pendingSaves.remove(key); - chunksToRemove.remove(pendingchunk); + chunksToRemoveUm.remove(pendingchunk); } + + // release only after remove from lists (may be reread by other threads) + releaseNbt(pendingchunk.nbtTags); } return true; @@ -320,28 +354,28 @@ if (extendedblockstorage != null) { - nbttagcompound1 = new NBTTagCompound(); - nbttagcompound1.setByte("Y", (byte)(extendedblockstorage.getYLocation() >> 4 & 255)); - nbttagcompound1.setByteArray("Blocks", extendedblockstorage.getSlot().copyLSB()); +// nbttagcompound1 = new NBTTagCompound(); +// nbttagcompound1.setByte("Y", (byte)(extendedblockstorage.getYLocation() >> 4 & 255)); +// nbttagcompound1.setByteArray("Blocks", extendedblockstorage.getSlot().copyLSB()); +// +// if (true/*extendedblockstorage.getBlockMSBArray() != null*/) +// { +// nbttagcompound1.setByteArray("Add", extendedblockstorage.getSlot().copyMSB()); +// } +// +// nbttagcompound1.setByteArray("Data", extendedblockstorage.getSlot().copyBlockMetadata()); +// nbttagcompound1.setByteArray("BlockLight", extendedblockstorage.getSlot().copyBlocklight()); +// +// if (flag) +// { +// nbttagcompound1.setByteArray("SkyLight", extendedblockstorage.getSlot().copySkylight()); +// } +// else +// { +// nbttagcompound1.setByteArray("SkyLight", new byte[2048]); +// } - if (true/*extendedblockstorage.getBlockMSBArray() != null*/) - { - nbttagcompound1.setByteArray("Add", extendedblockstorage.getSlot().copyMSB()); - } - - nbttagcompound1.setByteArray("Data", extendedblockstorage.getSlot().copyBlockMetadata()); - nbttagcompound1.setByteArray("BlockLight", extendedblockstorage.getSlot().copyBlocklight()); - - if (flag) - { - nbttagcompound1.setByteArray("SkyLight", extendedblockstorage.getSlot().copySkylight()); - } - else - { - nbttagcompound1.setByteArray("SkyLight", new byte[2048]); - } - - nbttaglist.appendTag(nbttagcompound1); + nbttaglist.appendTag(new EbsSaveFakeNbt(extendedblockstorage.copy(), !flag)); } } @@ -358,7 +392,7 @@ while (iterator1.hasNext()) { Entity entity = (Entity)iterator1.next(); - nbttagcompound1 = new NBTTagCompound(); + nbttagcompound1 = new NBTTagCompound(34); try { @@ -378,13 +412,13 @@ } par3NBTTagCompound.setTag("Entities", nbttaglist2); - NBTTagList nbttaglist3 = new NBTTagList(); + NBTTagList nbttaglist3 = new NBTTagList(par1Chunk.chunkTileEntityMap.size()); iterator1 = par1Chunk.chunkTileEntityMap.values().iterator(); while (iterator1.hasNext()) { TileEntity tileentity = (TileEntity)iterator1.next(); - nbttagcompound1 = new NBTTagCompound(); + nbttagcompound1 = new NBTTagCompound(7); try { tileentity.writeToNBT(nbttagcompound1); nbttaglist3.appendTag(nbttagcompound1); @@ -406,11 +440,11 @@ int z = par1Chunk.zPosition << 4; long k = par2World.getTotalWorldTime(); - NBTTagList nbttaglist1 = new NBTTagList(); + NBTTagList nbttaglist1 = new NBTTagList(set.size()); for(PendingBlockUpdate p : set) { - NBTTagCompound nbttagcompound2 = new NBTTagCompound(); + NBTTagCompound nbttagcompound2 = new NBTTagCompound(6); nbttagcompound2.setInteger("i", Block.getIdFromBlock(p.getBlock())); nbttagcompound2.setInteger("x", x + p.x); nbttagcompound2.setInteger("y", p.y); @@ -441,6 +475,12 @@ for (int k = 0; k < nbttaglist.tagCount(); ++k) { NBTTagCompound nbttagcompound1 = nbttaglist.getCompoundTagAt(k); + if(nbttagcompound1 instanceof EbsSaveFakeNbt) + { + ExtendedBlockStorage ebs = ((EbsSaveFakeNbt)nbttagcompound1).getEbs().copy(); + aextendedblockstorage[ebs.getYLocation() >> 4 & 255] = ebs; + continue; + } byte b1 = nbttagcompound1.getByte("Y"); ExtendedBlockStorage extendedblockstorage = new ExtendedBlockStorage(b1 << 4, flag, false); byte[] lsb = nbttagcompound1.getByteArray("Blocks"); @@ -566,7 +606,29 @@ synchronized(syncLockObject) { pendingSaves.clear(); - chunksToRemove.clear(); + chunksToRemoveUm.clear(); + } + } + + public static void releaseNbt(NBTTagCompound root) + { + NBTTagList sections = root.getCompoundTag("Level").getTagList("Sections", 10); + for(int i = 0; i < sections.tagCount(); i++) + { + NBTTagCompound nbt = sections.getCompoundTagAt(i); + if(nbt instanceof EbsSaveFakeNbt) + ((EbsSaveFakeNbt)nbt).release(); + } + } + + private static void retainNbt(NBTTagCompound root) + { + NBTTagList sections = root.getCompoundTag("Level").getTagList("Sections", 10); + for(int i = 0; i < sections.tagCount(); i++) + { + NBTTagCompound nbt = sections.getCompoundTagAt(i); + if(nbt instanceof EbsSaveFakeNbt) + ((EbsSaveFakeNbt)nbt).retain(); } } diff --git a/src/main/java/net/minecraftforge/common/chunkio/ChunkIOProvider.java b/src/main/java/net/minecraftforge/common/chunkio/ChunkIOProvider.java index 037e1f2..2084c2c 100644 --- a/src/main/java/net/minecraftforge/common/chunkio/ChunkIOProvider.java +++ b/src/main/java/net/minecraftforge/common/chunkio/ChunkIOProvider.java @@ -2,6 +2,7 @@ import net.minecraft.world.ChunkCoordIntPair; +import net.minecraft.world.chunk.storage.AnvilChunkLoader; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.AsynchronousExecutor; import net.minecraftforge.event.world.ChunkDataEvent; @@ -47,6 +48,7 @@ queuedChunk.loader.loadEntities(queuedChunk.world, queuedChunk.compound.getCompoundTag("Level"), chunk); MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Load(chunk, queuedChunk.compound)); // Don't call ChunkDataEvent.Load async + AnvilChunkLoader.releaseNbt(queuedChunk.compound); chunk.lastSaveTime = queuedChunk.provider.worldObj.getTotalWorldTime(); queuedChunk.provider.chunkMap.put(ChunkHash.chunkToKey(queuedChunk.x, queuedChunk.z), chunk); chunk.onChunkLoad(); diff --git a/src/main/java/org/ultramine/server/internal/LambdaHolder.java b/src/main/java/org/ultramine/server/internal/LambdaHolder.java index 2d14247..94d4b43 100644 --- a/src/main/java/org/ultramine/server/internal/LambdaHolder.java +++ b/src/main/java/org/ultramine/server/internal/LambdaHolder.java @@ -15,6 +15,7 @@ { public static final Predicate ENTITY_REMOVAL_PREDICATE = o -> ((Entity)o).removeThisTick; public static final Predicate TILE_ENTITY_REMOVAL_PREDICATE = o -> ((TileEntity)o).removeThisTick; + public static final Supplier BOOLEAN_FALSE_SUPPLIER = () -> Boolean.FALSE; public static Supplier> newTreeSet() { @@ -31,4 +32,9 @@ { return CachedEntry::getValueAndUpdateTime; } + + public static Supplier newByteArray(int size) + { + return () -> new byte[size]; + } }