diff --git a/build.gradle b/build.gradle index 9bb25154..65edfcc8 100644 --- a/build.gradle +++ b/build.gradle @@ -7,8 +7,8 @@ buildscript { dependencies { classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.3' classpath 'org.ajoberstar:grgit:1.7.0' +// classpath 'it.unimi.dsi:fastutil:7.0.12' } - } apply plugin: 'java' diff --git a/bukkit0/src/main/java/com/boydti/fawe/bukkit/Metrics.java b/bukkit0/src/main/java/com/boydti/fawe/bukkit/Metrics.java index 3d2a1494..41bda87a 100644 --- a/bukkit0/src/main/java/com/boydti/fawe/bukkit/Metrics.java +++ b/bukkit0/src/main/java/com/boydti/fawe/bukkit/Metrics.java @@ -1,6 +1,7 @@ package com.boydti.fawe.bukkit; import com.boydti.fawe.Fawe; +import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.boydti.fawe.util.MainUtil; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; @@ -84,7 +85,7 @@ public class Metrics { * @return byte[] the file as a byte array */ public static byte[] gzip(String input) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); + FastByteArrayOutputStream baos = new FastByteArrayOutputStream(); GZIPOutputStream gzos = null; try { gzos = new GZIPOutputStream(baos); diff --git a/bukkit0/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java b/bukkit0/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java index 8c191d85..66f291ba 100644 --- a/bukkit0/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java +++ b/bukkit0/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java @@ -83,7 +83,7 @@ public abstract class BukkitQueue_0 extends NMSMa } } - public static ConcurrentHashMap keepLoaded = new ConcurrentHashMap<>(); + public static ConcurrentHashMap keepLoaded = new ConcurrentHashMap<>(8, 0.9f, 1); @EventHandler public static void onChunkUnload(ChunkUnloadEvent event) { diff --git a/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitMain_110.java b/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitMain_110.java index e04bdfe6..92ac5e86 100644 --- a/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitMain_110.java +++ b/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitMain_110.java @@ -3,15 +3,24 @@ package com.boydti.fawe.bukkit.v1_10; import com.boydti.fawe.bukkit.ABukkitMain; import com.boydti.fawe.bukkit.v0.BukkitQueue_0; import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.io.BufferedRandomAccessFile; +import com.boydti.fawe.object.io.FastByteArrayInputStream; +import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.boydti.fawe.util.ReflectionUtils; +import java.io.BufferedInputStream; import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; +import java.io.RandomAccessFile; +import java.lang.reflect.Field; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; +import java.util.zip.GZIPInputStream; +import java.util.zip.InflaterInputStream; import net.minecraft.server.v1_10_R1.RegionFile; import net.minecraft.server.v1_10_R1.RegionFileCache; @@ -21,44 +30,131 @@ public class BukkitMain_110 extends ABukkitMain { return new BukkitQueue_1_10(world); } - public BukkitMain_110() { - try { - ReflectionUtils.setFailsafeFieldValue(RegionFileCache.class.getDeclaredField("a"), null, new ConcurrentHashMap() { - @Override - public RegionFile get(Object key) { - RegionFile existing = super.get(key); - if (existing != null) { - return existing; - } - File file = (File) key; - if (!file.exists()) { - file.getParentFile().mkdirs(); - } - if (size() >= 256) { - RegionFileCache.a(); - } - RegionFile regionFile = new RegionFile(file) { - @Override - public DataOutputStream b(final int i, final int j) { - if (i < 0 || i >= 32 || j < 0 || j >= 32) { - return null; - } - return new DataOutputStream(new BufferedOutputStream(new DeflaterOutputStream(new ByteArrayOutputStream() { - @Override - public void close() throws IOException { - a(i, j, this.buf, this.count); - } - }, new Deflater(Settings.EXPERIMENTAL.WORLD_COMPRESSION)))); + @Override + public void onEnable() { + super.onEnable(); + if (Settings.EXPERIMENTAL.WORLD_COMPRESSION != -1) { + try { + ReflectionUtils.setFailsafeFieldValue(RegionFileCache.class.getDeclaredField("a"), null, new ConcurrentHashMap(8, 0.9f, 1) { + @Override + public RegionFile get(Object key) { + RegionFile existing = super.get(key); + if (existing != null) { + return existing; } - }; - put(file, regionFile); - return regionFile; - } - }); - ; - } catch (Throwable e) { - e.printStackTrace(); + try { + File file = (File) key; + if (!file.exists()) { + file.getParentFile().mkdirs(); + } + if (size() >= 256) { + RegionFileCache.a(); + } + RegionFile regionFile = new RegionFile(file) { + + private int[] d = ReflectionUtils.getField(RegionFile.class.getDeclaredField("d"), this); + private int[] e = ReflectionUtils.getField(RegionFile.class.getDeclaredField("e"), this); + private List f = ReflectionUtils.getField(RegionFile.class.getDeclaredField("f"), this); + public RandomAccessFile c = null; + + @Override + public DataOutputStream b(final int i, final int j) { + if (i < 0 || i >= 32 || j < 0 || j >= 32) { + return null; + } +// if (Settings.EXPERIMENTAL.FAST_WORLD_COMPRESSION) { +// try { +// return new DataOutputStream(new AsyncBufferedOutputStream(new LZ4OutputStream(new FastByteArrayOutputStream() { +// @Override +// public void close() { +// try { +// super.close(); +// } catch (IOException e1) { +// e1.printStackTrace(); +// } +// a(i, j, array, length); +// } +// }, 16000))); +// } catch (Throwable e) { +// e.printStackTrace(); +// } +// } + return new DataOutputStream(new BufferedOutputStream(new DeflaterOutputStream(new FastByteArrayOutputStream() { + @Override + public void close() throws IOException { + super.close(); + a(i, j, this.array, length); + } + }, new Deflater(Settings.EXPERIMENTAL.WORLD_COMPRESSION)))); + } + + @Override + public synchronized DataInputStream a(int i, int j) { + if ((i < 0) || (i >= 32) || (j < 0) || (j >= 32)) { + return null; + } else { + try { + int k = d[(i + j * 32)]; + if (k == 0) { + return null; + } else { + int l = k >> 8; + int i1 = k & 255; + if (l + i1 > f.size()) { + return null; + } else { + c.seek((long) (l * 4096)); + int j1 = this.c.readInt(); + if (j1 > 4096 * i1) { + return null; + } else if (j1 <= 0) { + return null; + } else { + byte b0 = c.readByte(); + byte[] abyte; + if (b0 == 1) { + abyte = new byte[j1 - 1]; + c.read(abyte); + return new DataInputStream(new BufferedInputStream(new GZIPInputStream(new FastByteArrayInputStream(abyte)))); + } else if (b0 == 2) { + abyte = new byte[j1 - 1]; + c.read(abyte); +// if (Settings.EXPERIMENTAL.FAST_WORLD_COMPRESSION) { +// return new DataInputStream(new LZ4InputStream(new FastByteArrayInputStream(abyte))); +// } + return new DataInputStream(new BufferedInputStream(new InflaterInputStream(new FastByteArrayInputStream(abyte)))); + } else { + return null; + } + } + } + } + } catch (IOException var9) { + var9.printStackTrace(); + return null; + } + } + } + }; + Field field = RegionFile.class.getDeclaredField("c"); + field.setAccessible(true); + RandomAccessFile raf2 = (RandomAccessFile) field.get(regionFile); + raf2.close(); + final BufferedRandomAccessFile raf = new BufferedRandomAccessFile(file, "rw"); + ReflectionUtils.setFailsafeFieldValue(field, regionFile, raf); + put(file, regionFile); + regionFile.getClass().getDeclaredField("c").set(regionFile, raf); + return regionFile; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + }); + ; + } catch (Throwable e) { + e.printStackTrace(); + } } } - } \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/Fawe.java b/core/src/main/java/com/boydti/fawe/Fawe.java index f482088a..a3759e39 100644 --- a/core/src/main/java/com/boydti/fawe/Fawe.java +++ b/core/src/main/java/com/boydti/fawe/Fawe.java @@ -474,7 +474,7 @@ public class Fawe { return this.thread = Thread.currentThread(); } - private ConcurrentHashMap players = new ConcurrentHashMap<>(); + private ConcurrentHashMap players = new ConcurrentHashMap<>(8, 0.9f, 1); public void register(FawePlayer player) { players.put(player.getName(), player); diff --git a/core/src/main/java/com/boydti/fawe/command/Reload.java b/core/src/main/java/com/boydti/fawe/command/Reload.java index ba449a41..50d32ea0 100644 --- a/core/src/main/java/com/boydti/fawe/command/Reload.java +++ b/core/src/main/java/com/boydti/fawe/command/Reload.java @@ -5,7 +5,10 @@ import com.boydti.fawe.FaweVersion; import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.FaweCommand; import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.util.HastebinUtility; import com.boydti.fawe.util.MainUtil; +import java.io.File; +import java.io.IOException; import java.util.Date; public class Reload extends FaweCommand { @@ -32,6 +35,49 @@ public class Reload extends FaweCommand { MainUtil.sendMessage(player, "Version Build: #" + version.build); return true; } + case "debugpaste": + case "paste": { + try { + String settingsYML = HastebinUtility.upload(new File(Fawe.imp().getDirectory(), "config.yml")); + String messagesYML = HastebinUtility.upload(new File(Fawe.imp().getDirectory(), "message.yml")); + String commandsYML = HastebinUtility.upload(new File(Fawe.imp().getDirectory(), "commands.yml")); + String latestLOG; + try { + latestLOG = HastebinUtility.upload(new File(Fawe.imp().getDirectory(), "../../logs/latest.log")); + } catch (IOException ignored) { + MainUtil.sendMessage(player, "&clatest.log is too big to be pasted, will ignore"); + latestLOG = "too big :("; + } + StringBuilder b = new StringBuilder(); + b.append( + "# Welcome to this paste\n# It is meant to provide us at IntellectualSites with better information about your " + + "problem\n\n# We will start with some informational files\n"); + b.append("links.config_yml: ").append(settingsYML).append('\n'); + b.append("links.messages_yml: ").append(messagesYML).append('\n'); + b.append("links.commands_yml: ").append(commandsYML).append('\n'); + b.append("links.latest_log: ").append(latestLOG).append('\n'); + b.append("\n# Server Information\n"); + b.append("version.server: ").append(Fawe.imp().getPlatform()).append('\n'); + b.append("\n\n# YAY! Now, let's see what we can find in your JVM\n"); + Runtime runtime = Runtime.getRuntime(); + b.append("memory.free: ").append(runtime.freeMemory()).append('\n'); + b.append("memory.max: ").append(runtime.maxMemory()).append('\n'); + b.append("java.specification.version: '").append(System.getProperty("java.specification.version")).append("'\n"); + b.append("java.vendor: '").append(System.getProperty("java.vendor")).append("'\n"); + b.append("java.version: '").append(System.getProperty("java.version")).append("'\n"); + b.append("os.arch: '").append(System.getProperty("os.arch")).append("'\n"); + b.append("os.name: '").append(System.getProperty("os.name")).append("'\n"); + b.append("os.version: '").append(System.getProperty("os.version")).append("'\n\n"); + b.append("# Okay :D Great. You are now ready to create your bug report!"); + b.append("\n# You can do so at https://github.com/boy0001/FastAsyncWorldedit/issues"); + + String link = HastebinUtility.upload(b.toString()); + BBC.DOWNLOAD_LINK.send(player, link); + return true; + } catch (IOException e) { + e.printStackTrace(); + } + } case "reload": { Fawe.get().setupConfigs(); MainUtil.sendMessage(player, "Reloaded (" + Fawe.get().getVersion() + ")."); diff --git a/core/src/main/java/com/boydti/fawe/database/DBHandler.java b/core/src/main/java/com/boydti/fawe/database/DBHandler.java index 19f89552..c638d6bc 100644 --- a/core/src/main/java/com/boydti/fawe/database/DBHandler.java +++ b/core/src/main/java/com/boydti/fawe/database/DBHandler.java @@ -6,7 +6,7 @@ import java.util.concurrent.ConcurrentHashMap; public class DBHandler { public final static DBHandler IMP = new DBHandler(); - private Map databases = new ConcurrentHashMap<>(); + private Map databases = new ConcurrentHashMap<>(8, 0.9f, 1); public RollbackDatabase getDatabase(String world) { RollbackDatabase database = databases.get(world); diff --git a/core/src/main/java/com/boydti/fawe/database/RollbackDatabase.java b/core/src/main/java/com/boydti/fawe/database/RollbackDatabase.java index ae9b33dd..c25b9eda 100644 --- a/core/src/main/java/com/boydti/fawe/database/RollbackDatabase.java +++ b/core/src/main/java/com/boydti/fawe/database/RollbackDatabase.java @@ -47,7 +47,7 @@ public class RollbackDatabase { this.dbLocation = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.PATHS.HISTORY + File.separator + world + File.separator + "summary.db"); connection = openConnection(); CREATE_TABLE = "CREATE TABLE IF NOT EXISTS `" + prefix + "edits` (`player` BLOB(16) NOT NULL,`id` INT NOT NULL,`x1` INT NOT NULL,`y1` INT NOT NULL,`z1` INT NOT NULL,`x2` INT NOT NULL,`y2` INT NOT NULL,`z2` INT NOT NULL,`time` INT NOT NULL, PRIMARY KEY (player, id))"; - INSERT_EDIT = "INSERT INTO `" + prefix + "edits` (`player`,`id`,`x1`,`y1`,`z1`,`x2`,`y2`,`z2`,`time`) VALUES(?,?,?,?,?,?,?,?,?)"; + INSERT_EDIT = "INSERT OR REPLACE INTO `" + prefix + "edits` (`player`,`id`,`x1`,`y1`,`z1`,`x2`,`y2`,`z2`,`time`) VALUES(?,?,?,?,?,?,?,?,?)"; PURGE = "DELETE FROM `" + prefix + "edits` WHERE `time`=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=?"; GET_EDITS = "SELECT `player`,`id` FROM `" + prefix + "edits` WHERE `x2`>=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=? AND `time`>? ORDER BY `time` DESC, `id` DESC"; diff --git a/core/src/main/java/com/boydti/fawe/example/DefaultFaweQueueMap.java b/core/src/main/java/com/boydti/fawe/example/DefaultFaweQueueMap.java index 74568ff9..8763f6d8 100644 --- a/core/src/main/java/com/boydti/fawe/example/DefaultFaweQueueMap.java +++ b/core/src/main/java/com/boydti/fawe/example/DefaultFaweQueueMap.java @@ -21,7 +21,7 @@ public class DefaultFaweQueueMap implements IFaweQueueMap { /** * Map of chunks in the queue */ - public ConcurrentHashMap blocks = new ConcurrentHashMap<>(); + public ConcurrentHashMap blocks = new ConcurrentHashMap<>(8, 0.9f, 1); public ConcurrentLinkedDeque chunks = new ConcurrentLinkedDeque() { @Override public boolean add(FaweChunk o) { diff --git a/core/src/main/java/com/boydti/fawe/example/NMSMappedFaweQueue.java b/core/src/main/java/com/boydti/fawe/example/NMSMappedFaweQueue.java index 2cc9f33a..412c73f1 100644 --- a/core/src/main/java/com/boydti/fawe/example/NMSMappedFaweQueue.java +++ b/core/src/main/java/com/boydti/fawe/example/NMSMappedFaweQueue.java @@ -14,10 +14,23 @@ public abstract class NMSMappedFaweQueue ex public NMSMappedFaweQueue(String world) { super(world); + addRelightTask(); } public NMSMappedFaweQueue(String world, IFaweQueueMap map) { super(world, map); + addRelightTask(); + } + + private void addRelightTask() { + tasks.add(new Runnable() { + @Override + public void run() { + if (relighter != null) { + relighter.fixLightingSafe(hasSky()); + } + } + }); } private NMSRelighter relighter; @@ -55,14 +68,6 @@ public abstract class NMSMappedFaweQueue ex } } - @Override - public void runTasks() { - super.runTasks(); - if (relighter != null) { - relighter.fixLightingSafe(hasSky()); - } - } - @Override public void sendChunk(final FaweChunk fc) { refreshChunk(fc); diff --git a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAFile.java b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAFile.java index 894bb4b2..6bfda27f 100644 --- a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAFile.java +++ b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAFile.java @@ -6,13 +6,14 @@ import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.RunnableVal4; import com.boydti.fawe.object.io.BufferedRandomAccessFile; +import com.boydti.fawe.object.io.FastByteArrayInputStream; +import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.boydti.fawe.util.MathMan; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTOutputStream; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; @@ -163,7 +164,7 @@ public class MCAFile { private NBTInputStream getChunkIS(int offset) throws IOException { try { byte[] data = getChunkCompressedBytes(offset); - ByteArrayInputStream bais = new ByteArrayInputStream(data); + FastByteArrayInputStream bais = new FastByteArrayInputStream(data); InflaterInputStream iis = new InflaterInputStream(bais, new Inflater(), 1); fieldBuf2.set(iis, buffer2); BufferedInputStream bis = new BufferedInputStream(iis, 1); @@ -221,7 +222,7 @@ public class MCAFile { if (tag == null) { return null; } - ByteArrayOutputStream baos = new ByteArrayOutputStream(0); + FastByteArrayOutputStream baos = new FastByteArrayOutputStream(0); fieldBuf4.set(baos, buffer3); DeflaterOutputStream deflater = new DeflaterOutputStream(baos, new Deflater(Settings.EXPERIMENTAL.WORLD_COMPRESSION), 1, true); fieldBuf5.set(deflater, buffer2); diff --git a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAQueueMap.java b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAQueueMap.java index 68677cc2..fd3be3a5 100644 --- a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAQueueMap.java +++ b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAQueueMap.java @@ -19,7 +19,7 @@ public class MCAQueueMap implements IFaweQueueMap { private FaweQueue queue; - private Map mcaFileMap = new ConcurrentHashMap<>(); + private Map mcaFileMap = new ConcurrentHashMap<>(8, 0.9f, 1); private NullFaweChunk nullChunk; private boolean isHybridQueue; diff --git a/core/src/main/java/com/boydti/fawe/logging/rollback/RollbackOptimizedHistory.java b/core/src/main/java/com/boydti/fawe/logging/rollback/RollbackOptimizedHistory.java index a3b58794..075899b1 100644 --- a/core/src/main/java/com/boydti/fawe/logging/rollback/RollbackOptimizedHistory.java +++ b/core/src/main/java/com/boydti/fawe/logging/rollback/RollbackOptimizedHistory.java @@ -4,12 +4,13 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.database.DBHandler; import com.boydti.fawe.database.RollbackDatabase; import com.boydti.fawe.object.changeset.DiskStorageHistory; +import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.world.World; import java.io.IOException; import java.util.UUID; public class RollbackOptimizedHistory extends DiskStorageHistory { - private final long time; + private long time; private int minX; private int maxX; @@ -56,6 +57,19 @@ public class RollbackOptimizedHistory extends DiskStorageHistory { return maxZ; } + public void setDimensions(Vector pos1, Vector pos2) { + this.minX = pos1.getBlockX(); + this.minY = pos1.getBlockY(); + this.minZ = pos1.getBlockZ(); + this.maxX = pos2.getBlockX(); + this.maxY = pos2.getBlockY(); + this.maxZ = pos2.getBlockZ(); + } + + public void setTime(long time) { + this.time = time; + } + @Override public boolean flush() { if (super.flush()) { diff --git a/core/src/main/java/com/boydti/fawe/object/FawePlayer.java b/core/src/main/java/com/boydti/fawe/object/FawePlayer.java index fdce4d6e..95cf3494 100644 --- a/core/src/main/java/com/boydti/fawe/object/FawePlayer.java +++ b/core/src/main/java/com/boydti/fawe/object/FawePlayer.java @@ -315,7 +315,7 @@ public abstract class FawePlayer { */ public void setMeta(String key, Object value) { if (this.meta == null) { - this.meta = new ConcurrentHashMap<>(); + this.meta = new ConcurrentHashMap<>(8, 0.9f, 1); } this.meta.put(key, value); } @@ -441,7 +441,7 @@ public abstract class FawePlayer { * @return */ public Map getTrackedSessions(SetQueue.QueueStage requiredStage) { - Map map = new ConcurrentHashMap<>(); + Map map = new ConcurrentHashMap<>(8, 0.9f, 1); if (requiredStage == null || requiredStage == SetQueue.QueueStage.ACTIVE) { for (FaweQueue queue : SetQueue.IMP.getActiveQueues()) { Set sessions = queue.getEditSessions(); diff --git a/core/src/main/java/com/boydti/fawe/object/brush/InspectBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/InspectBrush.java index c54ac908..3a2da2f0 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/InspectBrush.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/InspectBrush.java @@ -63,7 +63,7 @@ public class InspectBrush extends BrushTool implements DoubleActionTraceTool { return false; } if (!Settings.HISTORY.USE_DATABASE) { - BBC.SETTING_DISABLE.send(player, "history.use-disk"); + BBC.SETTING_DISABLE.send(player, "history.use-database"); return false; } WorldVector target = getTarget(player, rightClick); diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java b/core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java index d4649aa6..198ccac2 100644 --- a/core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java +++ b/core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java @@ -348,6 +348,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet { int combined2 = buffer[8] & 0xFF; summary.add(x, z, ((combined2 << 4) + (combined1 >> 4))); } + return summary; } catch (IOException e) { MainUtil.handleError(e); } diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java b/core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java index d93d2214..b566074d 100644 --- a/core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java +++ b/core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java @@ -3,12 +3,12 @@ package com.boydti.fawe.object.changeset; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FaweInputStream; import com.boydti.fawe.object.FaweOutputStream; +import com.boydti.fawe.object.io.FastByteArrayInputStream; +import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.boydti.fawe.util.MainUtil; import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTOutputStream; import com.sk89q.worldedit.world.World; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -22,23 +22,23 @@ import java.io.OutputStream; public class MemoryOptimizedHistory extends FaweStreamChangeSet { private byte[] ids; - private ByteArrayOutputStream idsStream; + private FastByteArrayOutputStream idsStream; private FaweOutputStream idsStreamZip; private byte[] entC; - private ByteArrayOutputStream entCStream; + private FastByteArrayOutputStream entCStream; private NBTOutputStream entCStreamZip; private byte[] entR; - private ByteArrayOutputStream entRStream; + private FastByteArrayOutputStream entRStream; private NBTOutputStream entRStreamZip; private byte[] tileC; - private ByteArrayOutputStream tileCStream; + private FastByteArrayOutputStream tileCStream; private NBTOutputStream tileCStreamZip; private byte[] tileR; - private ByteArrayOutputStream tileRStream; + private FastByteArrayOutputStream tileRStream; private NBTOutputStream tileRStreamZip; public MemoryOptimizedHistory(World world) { @@ -102,7 +102,7 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet { return idsStreamZip; } setOrigin(x, z); - idsStream = new ByteArrayOutputStream(Settings.HISTORY.BUFFER_SIZE); + idsStream = new FastByteArrayOutputStream(Settings.HISTORY.BUFFER_SIZE); idsStreamZip = getCompressedOS(idsStream); idsStreamZip.write(FaweStreamChangeSet.MODE); idsStreamZip.writeInt(x); @@ -115,7 +115,7 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet { if (ids == null) { return null; } - FaweInputStream result = MainUtil.getCompressedIS(new ByteArrayInputStream(ids)); + FaweInputStream result = MainUtil.getCompressedIS(new FastByteArrayInputStream(ids)); result.skip(FaweStreamChangeSet.HEADER_SIZE); return result; } @@ -125,7 +125,7 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet { if (entCStreamZip != null) { return entCStreamZip; } - entCStream = new ByteArrayOutputStream(Settings.HISTORY.BUFFER_SIZE); + entCStream = new FastByteArrayOutputStream(Settings.HISTORY.BUFFER_SIZE); return entCStreamZip = new NBTOutputStream(getCompressedOS(entCStream)); } @@ -134,7 +134,7 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet { if (entRStreamZip != null) { return entRStreamZip; } - entRStream = new ByteArrayOutputStream(Settings.HISTORY.BUFFER_SIZE); + entRStream = new FastByteArrayOutputStream(Settings.HISTORY.BUFFER_SIZE); return entRStreamZip = new NBTOutputStream(getCompressedOS(entRStream)); } @@ -143,7 +143,7 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet { if (tileCStreamZip != null) { return tileCStreamZip; } - tileCStream = new ByteArrayOutputStream(Settings.HISTORY.BUFFER_SIZE); + tileCStream = new FastByteArrayOutputStream(Settings.HISTORY.BUFFER_SIZE); return tileCStreamZip = new NBTOutputStream(getCompressedOS(tileCStream)); } @@ -152,27 +152,27 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet { if (tileRStreamZip != null) { return tileRStreamZip; } - tileRStream = new ByteArrayOutputStream(Settings.HISTORY.BUFFER_SIZE); + tileRStream = new FastByteArrayOutputStream(Settings.HISTORY.BUFFER_SIZE); return tileRStreamZip = new NBTOutputStream(getCompressedOS(tileRStream)); } @Override public NBTInputStream getEntityCreateIS() throws IOException { - return entC == null ? null : new NBTInputStream(MainUtil.getCompressedIS(new ByteArrayInputStream(entC))); + return entC == null ? null : new NBTInputStream(MainUtil.getCompressedIS(new FastByteArrayInputStream(entC))); } @Override public NBTInputStream getEntityRemoveIS() throws IOException { - return entR == null ? null : new NBTInputStream(MainUtil.getCompressedIS(new ByteArrayInputStream(entR))); + return entR == null ? null : new NBTInputStream(MainUtil.getCompressedIS(new FastByteArrayInputStream(entR))); } @Override public NBTInputStream getTileCreateIS() throws IOException { - return tileC == null ? null : new NBTInputStream(MainUtil.getCompressedIS(new ByteArrayInputStream(tileC))); + return tileC == null ? null : new NBTInputStream(MainUtil.getCompressedIS(new FastByteArrayInputStream(tileC))); } @Override public NBTInputStream getTileRemoveIS() throws IOException { - return tileR == null ? null : new NBTInputStream(MainUtil.getCompressedIS(new ByteArrayInputStream(tileR))); + return tileR == null ? null : new NBTInputStream(MainUtil.getCompressedIS(new FastByteArrayInputStream(tileR))); } } diff --git a/core/src/main/java/com/boydti/fawe/object/io/AsyncBufferedOutputStream.java b/core/src/main/java/com/boydti/fawe/object/io/AsyncBufferedOutputStream.java new file mode 100644 index 00000000..72143fba --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/io/AsyncBufferedOutputStream.java @@ -0,0 +1,183 @@ +package com.boydti.fawe.object.io; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.concurrent.ConcurrentLinkedDeque; + +/** + * BufferedOutputStream that asynchronously flushes to disk, so callers don't + * have to wait until the flush happens. Buffers are put into a queue that is + * written asynchronously to disk once it is really available. + * + * This class is thread-safe. + * + * The error handling (as all stream ops are done asynchronously) is done during + * write and close. Exceptions on the asynchronous thread will be thrown to the + * caller either while writing or closing this stream. + * + * @author thomas.jungblut + * + */ +public final class AsyncBufferedOutputStream extends FilterOutputStream { + + private final FlushThread flusher = new FlushThread(); + private final Thread flusherThread = new Thread(flusher, "FlushThread"); + private final ConcurrentLinkedDeque buffers; + + private final byte[] buf; + private int count = 0; + + /** + * Creates an asynchronous buffered output stream with 8K buffer and 5 maximal + * buffers. + */ + public AsyncBufferedOutputStream(OutputStream out) { + this(out, 8 * 1024, 5); + } + + /** + * Creates an asynchronous buffered output stream with defined buffersize and + * 5 maximal buffers. + */ + public AsyncBufferedOutputStream(OutputStream out, int bufSize) { + this(out, bufSize, 5); + } + + /** + * Creates an asynchronous buffered output stream. + * + * @param out the outputstream to layer on. + * @param bufSize the buffer size. + * @param maxBuffers the number of buffers to keep in parallel. + */ + public AsyncBufferedOutputStream(OutputStream out, int bufSize, int maxBuffers) { + super(out); + buffers = new ConcurrentLinkedDeque<>(); + buf = new byte[bufSize]; + flusherThread.start(); + } + + /** + * Writes the specified byte to this buffered output stream. + * + * @param b the byte to be written. + * @exception IOException if an I/O error occurs. + */ + @Override + public synchronized void write(int b) throws IOException { + flushBufferIfSizeLimitReached(); + throwOnFlusherError(); + buf[count++] = (byte) b; + } + + @Override + public void write(byte[] b) throws IOException { + write(b, 0, b.length); + } + + /** + * Writes len bytes from the specified byte array starting at + * offset off to this buffered output stream. + * + * @param b the data. + * @param off the start offset in the data. + * @param len the number of bytes to write. + * @exception IOException if an I/O error occurs. + */ + @Override + public synchronized void write(byte[] b, int off, int len) throws IOException { + if ((off | len | (b.length - (len + off)) | (off + len)) < 0) { + throw new IndexOutOfBoundsException(); + } + + int bytesWritten = 0; + while (bytesWritten < len) { + throwOnFlusherError(); + flushBufferIfSizeLimitReached(); + + int bytesToWrite = Math.min(len - bytesWritten, buf.length - count); + System.arraycopy(b, off + bytesWritten, buf, count, bytesToWrite); + count += bytesToWrite; + bytesWritten += bytesToWrite; + } + } + + /** + * Flushes this buffered output stream. It will enforce that the current + * buffer will be queue for asynchronous flushing no matter what size it has. + * + * @exception IOException if an I/O error occurs. + */ + @Override + public synchronized void flush() throws IOException { + forceFlush(); + } + + private void flushBufferIfSizeLimitReached() throws IOException { + if (count >= buf.length) { + forceFlush(); + } + } + + private void forceFlush() throws IOException { + if (count > 0) { + final byte[] copy = new byte[count]; + System.arraycopy(buf, 0, copy, 0, copy.length); + buffers.add(copy); + count = 0; + } + } + + @Override + public synchronized void close() throws IOException { + throwOnFlusherError(); + + forceFlush(); + flusher.closed = true; + + try { + flusherThread.interrupt(); + flusherThread.join(); + + throwOnFlusherError(); + } catch (InterruptedException e) { + // this is expected to happen + } finally { + out.close(); + } + } + + private void throwOnFlusherError() throws IOException { + if (flusher != null && flusher.errorHappened) { + throw new IOException("caught flusher to fail writing asynchronously!", + flusher.caughtException); + } + } + + class FlushThread implements Runnable { + + volatile boolean closed = false; + volatile boolean errorHappened = false; + volatile Exception caughtException; + + @Override + public void run() { + // run the real flushing action to the underlying stream + try { + while (!closed) { + byte[] take = buffers.poll(); + if (take != null) { + out.write(take); + } + } + } catch (Exception e) { + caughtException = e; + errorHappened = true; + // yield this thread, an error happened + return; + } + } + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/object/io/FastByteArrayInputStream.java b/core/src/main/java/com/boydti/fawe/object/io/FastByteArrayInputStream.java new file mode 100644 index 00000000..8ff92fcd --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/io/FastByteArrayInputStream.java @@ -0,0 +1,79 @@ +package com.boydti.fawe.object.io; + +import java.io.InputStream; + +public class FastByteArrayInputStream extends InputStream { + public byte[] array; + public int offset; + public int length; + private int position; + private int mark; + + public FastByteArrayInputStream(byte[] array, int offset, int length) { + this.array = array; + this.offset = offset; + this.length = length; + } + + public FastByteArrayInputStream(byte[] array) { + this(array, 0, array.length); + } + + public boolean markSupported() { + return true; + } + + public void reset() { + this.position = this.mark; + } + + public void close() { + } + + public void mark(int dummy) { + this.mark = this.position; + } + + public int available() { + return this.length - this.position; + } + + public long skip(long n) { + if (n <= this.length - this.position) { + this.position += (int) n; + return n; + } + n = this.length - this.position; + this.position = this.length; + return n; + } + + public int read() { + if (this.length == this.position) { + return -1; + } + return this.array[(this.offset + this.position++)] & 0xFF; + } + + public int read(byte[] b, int offset, int length) { + if (this.length == this.position) { + return length == 0 ? 0 : -1; + } + int n = Math.min(length, this.length - this.position); + System.arraycopy(this.array, this.offset + this.position, b, offset, n); + this.position += n; + return n; + } + + public long position() { + return this.position; + } + + public void position(long newPosition) { + this.position = ((int) Math.min(newPosition, this.length)); + } + + public long length() { + return this.length; + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/io/FastByteArrayOutputStream.java b/core/src/main/java/com/boydti/fawe/object/io/FastByteArrayOutputStream.java index 1831d359..9ca3da83 100644 --- a/core/src/main/java/com/boydti/fawe/object/io/FastByteArrayOutputStream.java +++ b/core/src/main/java/com/boydti/fawe/object/io/FastByteArrayOutputStream.java @@ -1,282 +1,96 @@ package com.boydti.fawe.object.io; -/* - * #%L - * ch-commons-util - * %% - * Copyright (C) 2012 Cloudhopper by Twitter - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - +import com.boydti.fawe.util.ByteArrays; import java.io.IOException; import java.io.OutputStream; -import java.io.RandomAccessFile; -import java.io.Writer; -import java.util.Iterator; -import java.util.LinkedList; -/** - * Originally found here: http://www.java2s.com/Code/Java/File-Input-Output/AspeedyimplementationofByteArrayOutputStream.htm - * - * A speedy implementation of ByteArrayOutputStream. It's not synchronized, and it - * does not copy buffers when it's expanded. There's also no copying of the internal buffer - * if it's contents is extracted with the writeTo(stream) method. - * - * @author Rickard berg - * @author Brat Baker (Atlassian) - * @author Alexey - * @version $Date: 2008-01-19 10:09:56 +0800 (Sat, 19 Jan 2008) $ $Id: FastByteArrayOutputStream.java 3000 2008-01-19 02:09:56Z tm_jee $ - */ public class FastByteArrayOutputStream extends OutputStream { - // Static -------------------------------------------------------- - private static final int DEFAULT_BLOCK_SIZE = 16384; - private LinkedList buffers; - // Attributes ---------------------------------------------------- - // internal buffer - private byte[] buffer; - // is the stream closed? - private boolean closed; - private int blockSize; - private int index; - private int size; + /** + * The array backing the output stream. + */ + public final static int DEFAULT_INITIAL_CAPACITY = 16; - // Constructors -------------------------------------------------- + /** + * The array backing the output stream. + */ + public byte[] array; + + /** + * The number of valid bytes in {@link #array}. + */ + public int length; + + /** + * The current writing position. + */ + private int position; + + /** + * Creates a new array output stream with an initial capacity of {@link #DEFAULT_INITIAL_CAPACITY} bytes. + */ public FastByteArrayOutputStream() { - this(DEFAULT_BLOCK_SIZE); + this(DEFAULT_INITIAL_CAPACITY); } - public FastByteArrayOutputStream(int aSize) { - blockSize = aSize; - buffer = new byte[blockSize]; + /** + * Creates a new array output stream with a given initial capacity. + * + * @param initialCapacity the initial length of the backing array. + */ + public FastByteArrayOutputStream(final int initialCapacity) { + array = new byte[initialCapacity]; } - public int getSize() { - return size + index; + /** + * Creates a new array output stream wrapping a given byte array. + * + * @param a the byte array to wrap. + */ + public FastByteArrayOutputStream(final byte[] a) { + array = a; } - @Override - public void close() { - closed = true; + /** + * Marks this array output stream as empty. + */ + public void reset() { + length = 0; + position = 0; + } + + public void trim() { + this.array = ByteArrays.trim(this.array, this.length); } public byte[] toByteArray() { - return toByteArray(false); + trim(); + return array; } - public byte[] toByteArray(boolean delete) { - byte[] data = new byte[getSize()]; - - // Check if we have a list of buffers - int pos = 0; - buffer = null; - if (buffers != null) { - Iterator iter = buffers.iterator(); - - while (iter.hasNext()) { - byte[] bytes = (byte[])iter.next(); - if (delete) { - iter.remove(); - } - System.arraycopy(bytes, 0, data, pos, blockSize); - pos += blockSize; - } - } - - // write the internal buffer directly - System.arraycopy(buffer, 0, data, pos, index); - - return data; + public void write(final int b) { + if (position >= array.length) array = ByteArrays.grow(array, position + 1, length); + array[position++] = (byte) b; + if (length < position) length = position; } - @Override - public String toString() { - return new String(toByteArray()); + public void write(final byte[] b, final int off, final int len) throws IOException { + ByteArrays.ensureOffsetLength(b, off, len); + if (position + len > array.length) array = ByteArrays.grow(array, position + len, position); + System.arraycopy(b, off, array, position, len); + if (position + len > length) length = position += len; } - // OutputStream overrides ---------------------------------------- - public void write(int datum) throws IOException { - if (closed) { - throw new IOException("Stream closed"); - } else { - if (index == blockSize) { - addBuffer(); - } - - // store the byte - buffer[index++] = (byte) datum; - } + public void position(long newPosition) { + if (position > Integer.MAX_VALUE) throw new IllegalArgumentException("Position too large: " + newPosition); + position = (int) newPosition; } - @Override - public void write(byte[] data, int offset, int length) throws IOException { - if (data == null) { - throw new NullPointerException(); - } else if ((offset < 0) || ((offset + length) > data.length) || (length < 0)) { - throw new IndexOutOfBoundsException(); - } else if (closed) { - throw new IOException("Stream closed"); - } else { - if ((index + length) > blockSize) { - int copyLength; - - do { - if (index == blockSize) { - addBuffer(); - } - - copyLength = blockSize - index; - - if (length < copyLength) { - copyLength = length; - } - - System.arraycopy(data, offset, buffer, index, copyLength); - offset += copyLength; - index += copyLength; - length -= copyLength; - } while (length > 0); - } else { - // Copy in the subarray - System.arraycopy(data, offset, buffer, index, length); - index += length; - } - } + public long position() { + return position; } - // Public - public void writeTo(OutputStream out) throws IOException { - // Check if we have a list of buffers - if (buffers != null) { - Iterator iter = buffers.iterator(); - - while (iter.hasNext()) { - byte[] bytes = (byte[]) iter.next(); - out.write(bytes, 0, blockSize); - } - } - - // write the internal buffer directly - out.write(buffer, 0, index); - } - - public void writeTo(RandomAccessFile out) throws IOException { - // Check if we have a list of buffers - if (buffers != null) { - Iterator iter = buffers.iterator(); - - while (iter.hasNext()) { - byte[] bytes = (byte[]) iter.next(); - out.write(bytes, 0, blockSize); - } - } - - // write the internal buffer directly - out.write(buffer, 0, index); - } - - public void writeTo(Writer out, String encoding) throws IOException { - /* - There is design tradeoff between being fast, correct and using too much memory when decoding bytes to strings. - - The rules are thus : - - 1. if there is only one buffer then its a simple String conversion - - REASON : Fast!!! - - 2. uses full buffer allocation annd System.arrayCopy() to smooosh together the bytes - and then use String conversion - - REASON : Fast at the expense of a known amount of memory (eg the used memory * 2) - */ - if (buffers != null) { - // RULE 2 : a balance between using some memory and speed - writeToViaSmoosh(out, encoding); - } else { - // RULE 1 : fastest! - writeToViaString(out, encoding); - } - } - - /** - * This can ONLY be called if there is only a single buffer to write, instead - * use {@link #writeTo(java.io.Writer, String)}, which auto detects if - * {@link #writeToViaString(java.io.Writer, String)} is to be used or - * {@link #writeToViaSmoosh(java.io.Writer, String)}. - * - * @param out the JspWriter - * @param encoding the encoding - * @throws IOException - */ - void writeToViaString(Writer out, String encoding) throws IOException { - byte[] bufferToWrite = buffer; // this is always the last buffer to write - int bufferToWriteLen = index; // index points to our place in the last buffer - writeToImpl(out, encoding, bufferToWrite, bufferToWriteLen); - } - - /** - * This is recommended to be used where there's more than 1 buffer to write, instead - * use {@link #writeTo(java.io.Writer, String)} which auto detects if - * {@link #writeToViaString(java.io.Writer, String)} is to be used or - * {@link #writeToViaSmoosh(java.io.Writer, String)}. - * - * @param out - * @param encoding - * @throws IOException - */ - void writeToViaSmoosh(Writer out, String encoding) throws IOException { - byte[] bufferToWrite = toByteArray(); - int bufferToWriteLen = bufferToWrite.length; - writeToImpl(out, encoding, bufferToWrite, bufferToWriteLen); - } - - /** - * Write bufferToWriteLen of bytes from bufferToWrite to - * out encoding it at the same time. - * - * @param out - * @param encoding - * @param bufferToWrite - * @param bufferToWriteLen - * @throws IOException - */ - private void writeToImpl(Writer out, String encoding, byte[] bufferToWrite, int bufferToWriteLen) - throws IOException { - String writeStr; - if (encoding != null) { - writeStr = new String(bufferToWrite, 0, bufferToWriteLen, encoding); - } else { - writeStr = new String(bufferToWrite, 0, bufferToWriteLen); - } - out.write(writeStr); - } - - /** - * Create a new buffer and store the - * current one in linked list - */ - protected void addBuffer() { - if (buffers == null) { - buffers = new LinkedList(); - } - - buffers.addLast(buffer); - - buffer = new byte[blockSize]; - size += index; - index = 0; + public long length() throws IOException { + return length; } } \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/object/schematic/FaweFormat.java b/core/src/main/java/com/boydti/fawe/object/schematic/FaweFormat.java index 9bd9ed77..bf805ffd 100644 --- a/core/src/main/java/com/boydti/fawe/object/schematic/FaweFormat.java +++ b/core/src/main/java/com/boydti/fawe/object/schematic/FaweFormat.java @@ -4,6 +4,8 @@ import com.boydti.fawe.FaweCache; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FaweInputStream; import com.boydti.fawe.object.FaweOutputStream; +import com.boydti.fawe.object.io.FastByteArrayInputStream; +import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.ReflectionUtils; import com.sk89q.jnbt.CompoundTag; @@ -29,7 +31,6 @@ import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.registry.WorldData; import com.sk89q.worldedit.world.storage.NBTConversions; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayDeque; @@ -96,7 +97,7 @@ public class FaweFormat implements ClipboardReader, ClipboardWriter { case 1: { // Unknown size ox = in.readInt(); oz = in.readInt(); - FaweOutputStream tmp = new FaweOutputStream(new ByteArrayOutputStream(Settings.HISTORY.BUFFER_SIZE)); + FaweOutputStream tmp = new FaweOutputStream(new FastByteArrayOutputStream(Settings.HISTORY.BUFFER_SIZE)); int width = 0; int height = 0; int length = 0; @@ -136,7 +137,7 @@ public class FaweFormat implements ClipboardReader, ClipboardWriter { height++; length++; byte[] array = ((ByteArrayOutputStream) tmp.getParent()).toByteArray(); - FaweInputStream part = new FaweInputStream(new ByteArrayInputStream(array)); + FaweInputStream part = new FaweInputStream(new FastByteArrayInputStream(array)); try { for (int i = 0; i< array.length; i+= 9) { int x, y, z; diff --git a/core/src/main/java/com/boydti/fawe/util/ByteArrays.java b/core/src/main/java/com/boydti/fawe/util/ByteArrays.java new file mode 100644 index 00000000..85d314eb --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/util/ByteArrays.java @@ -0,0 +1,47 @@ +package com.boydti.fawe.util; + +public class ByteArrays { + + public static final byte[] EMPTY_ARRAY = new byte[0]; + + public static void ensureOffsetLength(byte[] a, int offset, int length) + { + ensureOffsetLength(a.length, offset, length); + } + + public static void ensureOffsetLength(int arrayLength, int offset, int length) + { + if (offset < 0) { + throw new ArrayIndexOutOfBoundsException("Offset (" + offset + ") is negative"); + } + if (length < 0) { + throw new IllegalArgumentException("Length (" + length + ") is negative"); + } + if (offset + length > arrayLength) { + throw new ArrayIndexOutOfBoundsException("Last index (" + (offset + length) + ") is greater than array length (" + arrayLength + ")"); + } + } + + public static byte[] grow(byte[] array, int length, int preserve) + { + if (length > array.length) + { + int newLength = (int)Math.max( + Math.min(2L * array.length, 2147483639L), length); + byte[] t = new byte[newLength]; + System.arraycopy(array, 0, t, 0, preserve); + return t; + } + return array; + } + + public static byte[] trim(byte[] array, int length) + { + if (length >= array.length) { + return array; + } + byte[] t = length == 0 ? EMPTY_ARRAY : new byte[length]; + System.arraycopy(array, 0, t, 0, length); + return t; + } +} diff --git a/core/src/main/java/com/boydti/fawe/util/ImgurUtility.java b/core/src/main/java/com/boydti/fawe/util/ImgurUtility.java index e20d12e9..c7d09bfa 100644 --- a/core/src/main/java/com/boydti/fawe/util/ImgurUtility.java +++ b/core/src/main/java/com/boydti/fawe/util/ImgurUtility.java @@ -1,10 +1,10 @@ package com.boydti.fawe.util; +import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.google.gson.Gson; import com.google.gson.JsonObject; import java.io.BufferedInputStream; import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -25,7 +25,7 @@ public class ImgurUtility { public static URL uploadImage(InputStream is) throws IOException { is = new BufferedInputStream(is); - ByteArrayOutputStream baos = new ByteArrayOutputStream(Short.MAX_VALUE); + FastByteArrayOutputStream baos = new FastByteArrayOutputStream(Short.MAX_VALUE); int d; while ((d = is.read()) != -1) { baos.write(d); diff --git a/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java index b1e4efff..c8ce2b89 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -23,6 +23,7 @@ import com.boydti.fawe.FaweAPI; import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.RunnableVal2; import com.boydti.fawe.object.clipboard.ReadOnlyClipboard; +import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.boydti.fawe.util.ImgurUtility; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandException; @@ -60,7 +61,6 @@ import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.command.binding.Switch; import com.sk89q.worldedit.util.command.parametric.Optional; -import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.net.URL; @@ -229,7 +229,7 @@ public class ClipboardCommands { switch (format) { case PNG: try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(Short.MAX_VALUE); + FastByteArrayOutputStream baos = new FastByteArrayOutputStream(Short.MAX_VALUE); ClipboardWriter writer = format.getWriter(baos); writer.write(clipboard, null); baos.flush(); diff --git a/core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java b/core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java index c0d605f6..4fca6c59 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java @@ -20,10 +20,13 @@ package com.sk89q.worldedit.command; import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweAPI; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.database.DBHandler; import com.boydti.fawe.database.RollbackDatabase; +import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory; +import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.changeset.DiskStorageHistory; import com.boydti.fawe.util.MainUtil; @@ -38,6 +41,7 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldVector; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.world.World; +import java.io.File; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; @@ -74,6 +78,53 @@ public class HistoryCommands { BBC.SETTING_DISABLE.send(player, "history.use-database"); return; } + if (user.equals("#import")) { + if (!player.hasPermission("fawe.rollback.import")) { + BBC.NO_PERM.send(player, "fawe.rollback.import"); + return; + } + File folder = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.PATHS.HISTORY); + if (!folder.exists()) { + return; + } + for (File worldFolder : folder.listFiles()) { + if (!worldFolder.isDirectory()) { + continue; + } + String worldName = worldFolder.getName(); + World world = FaweAPI.getWorld(worldName); + if (world != null) { + for (File userFolder : worldFolder.listFiles()) { + if (!userFolder.isDirectory()) { + continue; + } + String userUUID = userFolder.getName(); + try { + UUID uuid = UUID.fromString(userUUID); + for (File historyFile : userFolder.listFiles()) { + String name = historyFile.getName(); + if (!name.endsWith(".bd")) { + continue; + } + RollbackOptimizedHistory rollback = new RollbackOptimizedHistory(world, uuid, Integer.parseInt(name.substring(0, name.length() - 3))); + DiskStorageHistory.DiskStorageSummary summary = rollback.summarize(RegionWrapper.GLOBAL(), true); + if (summary != null) { + rollback.setDimensions(new Vector(summary.minX, 0, summary.minZ), new Vector(summary.maxX, 255, summary.maxZ)); + rollback.setTime(historyFile.lastModified()); + RollbackDatabase db = DBHandler.IMP.getDatabase(worldName); + db.logEdit(rollback); + player.print("Logging: " + historyFile); + } + } + } catch (IllegalArgumentException e) { + continue; + } + } + } + } + player.print("Done import!"); + return; + } UUID other = Fawe.imp().getUUID(user); if (other == null) { BBC.PLAYER_NOT_FOUND.send(player, user); diff --git a/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java b/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java index 1f28bbdb..856717e8 100644 --- a/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java +++ b/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java @@ -227,7 +227,7 @@ public enum ClipboardFormat { private static final Map aliasMap; static { - aliasMap = new ConcurrentHashMap<>(); + aliasMap = new ConcurrentHashMap<>(8, 0.9f, 1); for (ClipboardFormat emum : ClipboardFormat.values()) { for (String alias : emum.getAliases()) { aliasMap.put(alias, emum); diff --git a/core/src/main/java/com/sk89q/worldedit/session/SessionManager.java b/core/src/main/java/com/sk89q/worldedit/session/SessionManager.java index d087c6aa..a66174b7 100644 --- a/core/src/main/java/com/sk89q/worldedit/session/SessionManager.java +++ b/core/src/main/java/com/sk89q/worldedit/session/SessionManager.java @@ -60,7 +60,7 @@ public class SessionManager { private static final Logger log = Logger.getLogger(SessionManager.class.getCanonicalName()); private final Timer timer = new Timer(); private final WorldEdit worldEdit; - private final Map sessions = new ConcurrentHashMap(); + private final Map sessions = new ConcurrentHashMap(8, 0.9f, 1); private SessionStore store = new VoidStore(); private File path; diff --git a/core/src/main/java/net/jpountz/lz4/LZ4InputStream.java b/core/src/main/java/net/jpountz/lz4/LZ4InputStream.java index 0acc8424..b4e3e4dc 100644 --- a/core/src/main/java/net/jpountz/lz4/LZ4InputStream.java +++ b/core/src/main/java/net/jpountz/lz4/LZ4InputStream.java @@ -10,14 +10,20 @@ public class LZ4InputStream extends InputStream { private final InputStream inputStream; private final LZ4Decompressor decompressor; - private byte compressedBuffer[] = new byte[1048576]; - private byte decompressedBuffer[] = new byte[1048576]; + private byte compressedBuffer[]; + private byte decompressedBuffer[]; private int decompressedBufferPosition = 0; private int decompressedBufferLength = 0; public LZ4InputStream(InputStream stream) { + this(stream, 1048576); + } + + public LZ4InputStream(InputStream stream, int size) { this.decompressor = factory.decompressor(); this.inputStream = stream; + compressedBuffer = new byte[size]; + decompressedBuffer = new byte[size]; } @Override diff --git a/core/src/main/java/net/jpountz/lz4/LZ4OutputStream.java b/core/src/main/java/net/jpountz/lz4/LZ4OutputStream.java index cd7813fe..769a0b18 100644 --- a/core/src/main/java/net/jpountz/lz4/LZ4OutputStream.java +++ b/core/src/main/java/net/jpountz/lz4/LZ4OutputStream.java @@ -17,6 +17,10 @@ public class LZ4OutputStream extends OutputStream { this(os, ONE_MEGABYTE, lz4Factory.fastCompressor()); } + public LZ4OutputStream(OutputStream os, int size) throws IOException { + this(os, size, lz4Factory.fastCompressor()); + } + public LZ4OutputStream(OutputStream underlyingOutputStream, int blocksize, LZ4Compressor compressor) throws IOException { compressionInputBuffer = new byte[blocksize]; this.compressor = compressor; diff --git a/core/src/main/java/net/jpountz/lz4/LZ4StreamTest.java b/core/src/main/java/net/jpountz/lz4/LZ4StreamTest.java index eac75e2b..5bc47412 100644 --- a/core/src/main/java/net/jpountz/lz4/LZ4StreamTest.java +++ b/core/src/main/java/net/jpountz/lz4/LZ4StreamTest.java @@ -1,8 +1,8 @@ package net.jpountz.lz4; +import com.boydti.fawe.object.io.FastByteArrayInputStream; +import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.boydti.fawe.util.MainUtil; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Random; @@ -35,7 +35,7 @@ public class LZ4StreamTest { } private void compressContent() throws IOException { - ByteArrayOutputStream compressedOutputStream = new ByteArrayOutputStream(); + FastByteArrayOutputStream compressedOutputStream = new FastByteArrayOutputStream(); LZ4OutputStream os = new LZ4OutputStream(compressedOutputStream); int currentContentPosition = 0; @@ -75,7 +75,7 @@ public class LZ4StreamTest { @Test public void randomizedTest() throws IOException { try { - InputStream is = new LZ4InputStream(new ByteArrayInputStream(compressedOutput)); + InputStream is = new LZ4InputStream(new FastByteArrayInputStream(compressedOutput)); int currentContentPosition = 0; diff --git a/forge110/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java b/forge110/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java index 7115b1a3..62dd9544 100644 --- a/forge110/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java +++ b/forge110/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java @@ -30,9 +30,9 @@ package com.boydti.fawe.forge; +import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.boydti.fawe.util.MainUtil; import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; @@ -356,7 +356,7 @@ public class ForgeMetrics { * @return */ public static byte[] gzip(String input) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); + FastByteArrayOutputStream baos = new FastByteArrayOutputStream(); GZIPOutputStream gzos = null; try { diff --git a/forge110/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java b/forge110/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java index d1940818..39eb1155 100644 --- a/forge110/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java +++ b/forge110/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java @@ -18,7 +18,7 @@ public class ForgeTaskMan extends TaskManager { private final ConcurrentLinkedDeque syncTasks = new ConcurrentLinkedDeque<>(); private final ConcurrentLinkedDeque asyncTasks = new ConcurrentLinkedDeque<>(); - private final ConcurrentHashMap taskIdMap = new ConcurrentHashMap<>(); + private final ConcurrentHashMap taskIdMap = new ConcurrentHashMap<>(8, 0.9f, 1); private final AtomicInteger taskId = new AtomicInteger(); diff --git a/forge1710/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java b/forge1710/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java index d45902c2..0e984057 100644 --- a/forge1710/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java +++ b/forge1710/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java @@ -30,6 +30,7 @@ package com.boydti.fawe.forge; +import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.boydti.fawe.util.MainUtil; import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.FMLLog; @@ -37,7 +38,6 @@ import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import cpw.mods.fml.common.gameevent.TickEvent; import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; @@ -355,7 +355,7 @@ public class ForgeMetrics { * @return */ public static byte[] gzip(String input) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); + FastByteArrayOutputStream baos = new FastByteArrayOutputStream(); GZIPOutputStream gzos = null; try { diff --git a/forge1710/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java b/forge1710/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java index c780e74b..d573cfbf 100644 --- a/forge1710/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java +++ b/forge1710/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java @@ -18,7 +18,7 @@ public class ForgeTaskMan extends TaskManager { private final ConcurrentLinkedDeque syncTasks = new ConcurrentLinkedDeque<>(); private final ConcurrentLinkedDeque asyncTasks = new ConcurrentLinkedDeque<>(); - private final ConcurrentHashMap taskIdMap = new ConcurrentHashMap<>(); + private final ConcurrentHashMap taskIdMap = new ConcurrentHashMap<>(8, 0.9f, 1); private final AtomicInteger taskId = new AtomicInteger(); diff --git a/forge189/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java b/forge189/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java index 49f752ca..b4b59f95 100644 --- a/forge189/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java +++ b/forge189/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java @@ -30,9 +30,9 @@ package com.boydti.fawe.forge; +import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.boydti.fawe.util.MainUtil; import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; @@ -355,7 +355,7 @@ public class ForgeMetrics { * @return */ public static byte[] gzip(String input) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); + FastByteArrayOutputStream baos = new FastByteArrayOutputStream(); GZIPOutputStream gzos = null; try { diff --git a/forge189/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java b/forge189/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java index d1940818..39eb1155 100644 --- a/forge189/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java +++ b/forge189/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java @@ -18,7 +18,7 @@ public class ForgeTaskMan extends TaskManager { private final ConcurrentLinkedDeque syncTasks = new ConcurrentLinkedDeque<>(); private final ConcurrentLinkedDeque asyncTasks = new ConcurrentLinkedDeque<>(); - private final ConcurrentHashMap taskIdMap = new ConcurrentHashMap<>(); + private final ConcurrentHashMap taskIdMap = new ConcurrentHashMap<>(8, 0.9f, 1); private final AtomicInteger taskId = new AtomicInteger(); diff --git a/forge194/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java b/forge194/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java index 7115b1a3..62dd9544 100644 --- a/forge194/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java +++ b/forge194/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java @@ -30,9 +30,9 @@ package com.boydti.fawe.forge; +import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.boydti.fawe.util.MainUtil; import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; @@ -356,7 +356,7 @@ public class ForgeMetrics { * @return */ public static byte[] gzip(String input) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); + FastByteArrayOutputStream baos = new FastByteArrayOutputStream(); GZIPOutputStream gzos = null; try { diff --git a/forge194/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java b/forge194/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java index d1940818..39eb1155 100644 --- a/forge194/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java +++ b/forge194/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java @@ -18,7 +18,7 @@ public class ForgeTaskMan extends TaskManager { private final ConcurrentLinkedDeque syncTasks = new ConcurrentLinkedDeque<>(); private final ConcurrentLinkedDeque asyncTasks = new ConcurrentLinkedDeque<>(); - private final ConcurrentHashMap taskIdMap = new ConcurrentHashMap<>(); + private final ConcurrentHashMap taskIdMap = new ConcurrentHashMap<>(8, 0.9f, 1); private final AtomicInteger taskId = new AtomicInteger(); diff --git a/sponge/src/main/java/com/boydti/fawe/sponge/SpongeMetrics.java b/sponge/src/main/java/com/boydti/fawe/sponge/SpongeMetrics.java index 5e93c32f..c4d75106 100644 --- a/sponge/src/main/java/com/boydti/fawe/sponge/SpongeMetrics.java +++ b/sponge/src/main/java/com/boydti/fawe/sponge/SpongeMetrics.java @@ -131,7 +131,7 @@ public class SpongeMetrics { * @return */ public static byte[] gzip(final String input) { - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final ByteArrayOutputStream baos = new FastByteArrayOutputStream(); GZIPOutputStream gzos = null; try {