diff --git a/.gitignore b/.gitignore index ad190d2ae..6c2e94e65 100644 --- a/.gitignore +++ b/.gitignore @@ -30,9 +30,7 @@ test_out/ # project root /src/ -#native libraries -/natives/**/*.so -/natives/**/*.dll -/natives/**/*.o -/natives/src/main/native/*/lib-*/ -/natives/src/main/native/*/build*/ +# native libraries +/**/*.so +/**/*.dll +/**/*.o diff --git a/binary/src/main/java/net/daporkchop/lib/binary/oio/StreamUtil.java b/binary/src/main/java/net/daporkchop/lib/binary/oio/StreamUtil.java new file mode 100644 index 000000000..92f2d36aa --- /dev/null +++ b/binary/src/main/java/net/daporkchop/lib/binary/oio/StreamUtil.java @@ -0,0 +1,98 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.binary.oio; + +import lombok.NonNull; +import lombok.experimental.UtilityClass; +import net.daporkchop.lib.binary.stream.DataIn; +import net.daporkchop.lib.common.util.PorkUtil; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; + +/** + * Helper methods for dealing with OIO {@link InputStream}s and {@link OutputStream}s. + * + * @author DaPorkchop_ + */ +@UtilityClass +public class StreamUtil { + /** + * Reads the entire contents of the given {@link InputStream} into a {@code byte[]}. + * + * @param in the {@link InputStream} to read + * @return the contents of the given {@link InputStream} as a {@code byte[]} + * @throws IOException if an IO exception occurs you dummy + */ + public byte[] toByteArray(@NonNull InputStream in) throws IOException { + if (in instanceof DataIn) { + //DataIn implementations might apply their own optimizations here + return ((DataIn) in).toByteArray(); + } else { + byte[] arr = new byte[4096]; + int pos = 0; + for (int i; (i = in.read(arr, pos, arr.length - pos)) != -1; pos += i) { + if (pos + i == arr.length) { + //grow array + byte[] old = arr; + System.arraycopy(old, 0, arr = new byte[arr.length << 1], 0, old.length); + } + } + return pos == arr.length ? arr : Arrays.copyOf(arr, pos); //don't copy if the size is exactly the size of the array already + } + } + + /** + * Fills the given {@code byte[]} with data read from the given {@link InputStream}. + * + * @param in the {@link InputStream} to read from + * @param dst the {@code byte[]} to read to + * @return the {@link byte[]} + * @throws EOFException if the given {@link InputStream} reaches EOF before the given {@code byte[]} could be filled + * @throws IOException if an IO exception occurs you dummy + */ + public byte[] readFully(@NonNull InputStream in, @NonNull byte[] dst) throws EOFException, IOException { + return readFully(in, dst, 0, dst.length); + } + + /** + * Fills the given region of the given {@code byte[]} with data read from the given {@link InputStream}. + * + * @param in the {@link InputStream} to read from + * @param dst the {@code byte[]} to read to + * @param start the first index (inclusive) in the {@code byte[]} to start writing to + * @param length the number of bytes to read into the {@code byte[]} + * @return the {@link byte[]} + * @throws EOFException if the given {@link InputStream} reaches EOF before the given number of bytes could be read + * @throws IOException if an IO exception occurs you dummy + */ + public byte[] readFully(@NonNull InputStream in, @NonNull byte[] dst, int start, int length) throws EOFException, IOException { + if (in instanceof DataIn) { + //DataIn implementations might apply their own optimizations here + ((DataIn) in).readFully(dst, start, length); + } else { + PorkUtil.assertInRangeLen(dst.length, start, length); + for (int i; length > 0 && (i = in.read(dst, start, length)) != -1; start += i, length -= i) ; + if (length != 0) { + throw new EOFException(); + } + } + return dst; + } +} diff --git a/binary/src/main/java/net/daporkchop/lib/binary/stream/DataIn.java b/binary/src/main/java/net/daporkchop/lib/binary/stream/DataIn.java index 371208aaf..4fb829770 100644 --- a/binary/src/main/java/net/daporkchop/lib/binary/stream/DataIn.java +++ b/binary/src/main/java/net/daporkchop/lib/binary/stream/DataIn.java @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -20,9 +20,11 @@ import net.daporkchop.lib.binary.stream.netty.NettyByteBufIn; import net.daporkchop.lib.binary.stream.nio.BufferIn; import net.daporkchop.lib.binary.stream.stream.StreamIn; +import net.daporkchop.lib.common.util.PorkUtil; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; +import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -30,6 +32,7 @@ import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.function.Function; /** @@ -459,48 +462,51 @@ public CharSequence readText(long size, @NonNull Charset charset) throws IOExcep } /** - * Attempts to fill a byte array with data. - *

- * Functionally equivalent to: - * {@code return readFully(b, 0, b.length);} + * Fills the given {@code byte[]} with data. * - * @param b the byte array to read into - * @return the {@code byte[]} that the data was read into - * @throws IOException if end of stream is reached before the required number required bytes are read + * @param dst the {@code byte[]} to read to + * @throws EOFException if EOF is reached before the given {@code byte[]} could be filled + * @throws IOException if an IO exception occurs you dummy */ - public byte[] readFully(@NonNull byte[] b) throws IOException { - return this.readFully(b, 0, b.length); + public byte[] readFully(@NonNull byte[] dst) throws EOFException, IOException { + return this.readFully(dst, 0, dst.length); } /** - * Attempts to fill a given region of a byte array with data. + * Fills the given region of the given {@code byte[]} with data. * - * @param b the byte array to read into - * @param off the offset in the array to write data to - * @param len the number of bytes to read - * @return the {@code byte[]} that the data was read into - * @throws IOException if end of stream is reached before the required number required bytes are read + * @param dst the {@code byte[]} to read to + * @param start the first index (inclusive) in the {@code byte[]} to start writing to + * @param length the number of bytes to read into the {@code byte[]} + * @return the {@code byte[]} + * @throws EOFException if EOF is reached before the given number of bytes could be read + * @throws IOException if an IO exception occurs you dummy */ - public byte[] readFully(@NonNull byte[] b, int off, int len) throws IOException { - int i = 0; - while (len > 0 && (i = this.read(b, off + i, len)) != -1) { - len -= i; - } - if (i == -1) { - throw new IOException("Reached end of stream!"); + public byte[] readFully(@NonNull byte[] dst, int start, int length) throws EOFException, IOException { + PorkUtil.assertInRangeLen(dst.length, start, length); + for (int i; length > 0 && (i = this.read(dst, start, length)) != -1; start += i, length -= i) ; + if (length != 0) { + throw new EOFException(); } - return b; + return dst; } /** - * Reads all available bytes from this stream, as returned by {@link #available()}. + * Reads the entire contents of this {@link DataIn} into a {@code byte[]}. * - * @return all available bytes from this stream + * @return the contents of this {@link DataIn} as a {@code byte[]} */ - public byte[] readAllAvailableBytes() throws IOException { - byte[] b = new byte[this.available()]; - this.readFully(b); - return b; + public byte[] toByteArray() throws IOException { + byte[] arr = new byte[4096]; + int pos = 0; + for (int i; (i = this.read(arr, pos, arr.length - pos)) != -1; pos += i) { + if (pos + i == arr.length) { + //grow array + byte[] old = arr; + System.arraycopy(old, 0, arr = new byte[arr.length << 1], 0, old.length); + } + } + return pos == arr.length ? arr : Arrays.copyOf(arr, pos); //don't copy if the size is exactly the size of the array already } /** diff --git a/binary/src/main/java/net/daporkchop/lib/binary/stream/netty/NettyByteBufIn.java b/binary/src/main/java/net/daporkchop/lib/binary/stream/netty/NettyByteBufIn.java index c4be4afd2..a19016fd4 100644 --- a/binary/src/main/java/net/daporkchop/lib/binary/stream/netty/NettyByteBufIn.java +++ b/binary/src/main/java/net/daporkchop/lib/binary/stream/netty/NettyByteBufIn.java @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -21,7 +21,9 @@ import lombok.RequiredArgsConstructor; import lombok.experimental.Accessors; import net.daporkchop.lib.binary.stream.DataIn; +import net.daporkchop.lib.common.util.PorkUtil; +import java.io.EOFException; import java.io.IOException; import java.nio.charset.Charset; @@ -177,22 +179,34 @@ public double readDoubleLE() throws IOException { @Override public CharSequence readText(long size, @NonNull Charset charset) throws IOException { - if (size > Integer.MAX_VALUE) { + if (size > Integer.MAX_VALUE) { throw new IllegalArgumentException("size parameter too large!"); } return this.buf.readCharSequence((int) size, charset); } @Override - public byte[] readFully(@NonNull byte[] b) throws IOException { - this.buf.readBytes(b); - return b; + public byte[] readFully(@NonNull byte[] dst) throws IOException { + this.buf.readBytes(dst); + return dst; } @Override - public byte[] readFully(@NonNull byte[] b, int off, int len) throws IOException { - this.buf.readBytes(b, off, len); - return b; + public byte[] readFully(@NonNull byte[] dst, int start, int length) throws EOFException, IOException { + PorkUtil.assertInRangeLen(dst.length, start, length); + if (this.buf.isReadable(length)) { + this.buf.readBytes(dst, start, length); + return dst; + } else { + throw new EOFException(); + } + } + + @Override + public byte[] toByteArray() throws IOException { + byte[] arr = new byte[this.buf.readableBytes()]; + this.buf.readBytes(arr); + return arr; } @Override diff --git a/binary/src/main/java/net/daporkchop/lib/binary/stream/nio/BufferIn.java b/binary/src/main/java/net/daporkchop/lib/binary/stream/nio/BufferIn.java index ea415a432..84e187d45 100644 --- a/binary/src/main/java/net/daporkchop/lib/binary/stream/nio/BufferIn.java +++ b/binary/src/main/java/net/daporkchop/lib/binary/stream/nio/BufferIn.java @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -19,7 +19,9 @@ import lombok.NonNull; import lombok.RequiredArgsConstructor; import net.daporkchop.lib.binary.stream.DataIn; +import net.daporkchop.lib.common.util.PorkUtil; +import java.io.EOFException; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -51,9 +53,21 @@ public int read(@NonNull byte[] b, int off, int len) throws IOException { } @Override - public byte[] readFully(@NonNull byte[] b, int off, int len) throws IOException { - this.buffer.get(b, off, len); - return b; + public byte[] readFully(@NonNull byte[] dst, int start, int length) throws EOFException, IOException { + PorkUtil.assertInRangeLen(dst.length, start, length); + if (this.buffer.remaining() >= length) { + this.buffer.get(dst, start, length); + return dst; + } else { + throw new EOFException(); + } + } + + @Override + public byte[] toByteArray() throws IOException { + byte[] arr = new byte[this.buffer.remaining()]; + this.buffer.get(arr); + return arr; } @Override @@ -76,12 +90,12 @@ public long skip(long cnt) throws IOException { } @Override - public synchronized void mark(int readlimit) { + public void mark(int readlimit) { this.buffer.mark(); } @Override - public synchronized void reset() throws IOException { + public void reset() throws IOException { this.buffer.reset(); } diff --git a/build.gradle b/build.gradle index 472ee6c93..e284add9b 100644 --- a/build.gradle +++ b/build.gradle @@ -29,19 +29,14 @@ allprojects { porklibVersion = "0.5.1-SNAPSHOT" //dependency things - apacheCommonsCompressVersion = "1.16.1" bouncycastleVersion = "1.59" florianingerlRegexVersion = "1.1.9" - googleHttpClientVersion = "1.23.0" gsonVersion = "2.8.5" guavaVersion = "27.0-jre" - jnaVersion = "5.5.0" junitVersion = "4.12" lombokVersion = "1.16.20" nettyVersion = "4.1.36.Final" raknetVersion = "1.4.0" - sparseBitSetVersion = "1.1" - xzVersion = "1.8" } group "net.daporkchop" diff --git a/common/src/main/java/net/daporkchop/lib/common/util/PValidation.java b/common/src/main/java/net/daporkchop/lib/common/util/PValidation.java new file mode 100644 index 000000000..5de721498 --- /dev/null +++ b/common/src/main/java/net/daporkchop/lib/common/util/PValidation.java @@ -0,0 +1,106 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.common.util; + +import lombok.experimental.UtilityClass; +import net.daporkchop.lib.common.util.exception.ValueCannotFitException; +import net.daporkchop.lib.common.util.exception.ValueOutOfBoundsException; + +/** + * @author DaPorkchop_ + */ +@UtilityClass +public class PValidation { + public long ensurePositive(long value) throws ValueOutOfBoundsException { + if (value <= 0L) { + throw new ValueOutOfBoundsException(value + " <= 0"); + } + return value; + } + + public int ensurePositive(int value) throws ValueOutOfBoundsException { + if (value <= 0) { + throw new ValueOutOfBoundsException(value + " <= 0"); + } + return value; + } + + public long ensureNonPositive(long value) throws ValueOutOfBoundsException { + if (value > 0L) { + throw new ValueOutOfBoundsException(value + " > 0"); + } + return value; + } + + public int ensureNonPositive(int value) throws ValueOutOfBoundsException { + if (value > 0) { + throw new ValueOutOfBoundsException(value + " > 0"); + } + return value; + } + + public long ensureNegative(long value) throws ValueOutOfBoundsException { + if (value >= 0L) { + throw new ValueOutOfBoundsException(value + " >= 0"); + } + return value; + } + + public int ensureNegative(int value) throws ValueOutOfBoundsException { + if (value >= 0) { + throw new ValueOutOfBoundsException(value + " >= 0"); + } + return value; + } + + public long ensureNonNegative(long value) throws ValueOutOfBoundsException { + if (value < 0L) { + throw new ValueOutOfBoundsException(value + " < 0"); + } + return value; + } + + public int ensureNonNegative(int value) throws ValueOutOfBoundsException { + if (value < 0) { + throw new ValueOutOfBoundsException(value + " < 0"); + } + return value; + } + + public int toInt(long value) throws ValueCannotFitException { + int i = (int) value; + if (i != value) { + throw new ValueCannotFitException(value, 0); + } + return i; + } + + public int toPositiveIntSafe(long value) throws ValueCannotFitException, ValueOutOfBoundsException { + return ensurePositive(toInt(value)); + } + + public int toNonPositiveIntSafe(long value) throws ValueCannotFitException, ValueOutOfBoundsException { + return ensureNonPositive(toInt(value)); + } + + public int toNegativeIntSafe(long value) throws ValueCannotFitException, ValueOutOfBoundsException { + return ensureNegative(toInt(value)); + } + + public int toNonNegativeIntSafe(long value) throws ValueCannotFitException, ValueOutOfBoundsException { + return ensureNonNegative(toInt(value)); + } +} diff --git a/common/src/main/java/net/daporkchop/lib/common/util/PorkUtil.java b/common/src/main/java/net/daporkchop/lib/common/util/PorkUtil.java index 763cb7987..d342dc580 100644 --- a/common/src/main/java/net/daporkchop/lib/common/util/PorkUtil.java +++ b/common/src/main/java/net/daporkchop/lib/common/util/PorkUtil.java @@ -75,7 +75,7 @@ public class PorkUtil { ); public final DateFormat DATE_FORMAT = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); - public final String PORKLIB_VERSION = "0.5.0-SNAPSHOT"; + public final String PORKLIB_VERSION = "0.5.1-SNAPSHOT"; public final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); static { @@ -222,6 +222,24 @@ public Class classForName(@NonNull String name) { } } + @SuppressWarnings("unchecked") + public Class uninitializedClassForName(@NonNull String name) { + try { + return (Class) Class.forName(name, false, null); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + @SuppressWarnings("unchecked") + public Class uninitializedClassForName(@NonNull String name, ClassLoader loader) { + try { + return (Class) Class.forName(name, false, loader); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + public boolean classExistsWithName(@NonNull String name) { try { Class.forName(name); @@ -338,7 +356,7 @@ public void assertInRange(int size, int start, int end) throws IndexOutOfBoundsE } } - public void assertInRangeLen(int size, int start, int len) throws IndexOutOfBoundsException { - assertInRange(size, start, start + len); + public void assertInRangeLen(int size, int start, int length) throws IndexOutOfBoundsException { + assertInRange(size, start, start + length); } } diff --git a/common/src/main/java/net/daporkchop/lib/common/util/exception/ValueCannotFitException.java b/common/src/main/java/net/daporkchop/lib/common/util/exception/ValueCannotFitException.java new file mode 100644 index 000000000..d92f20b49 --- /dev/null +++ b/common/src/main/java/net/daporkchop/lib/common/util/exception/ValueCannotFitException.java @@ -0,0 +1,43 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.common.util.exception; + +/** + * Thrown when a value is too large to fit within some smaller data type. + * + * @author DaPorkchop_ + */ +public class ValueCannotFitException extends ValueOutOfBoundsException { + public ValueCannotFitException(String s) { + super(s); + } + + public ValueCannotFitException(long value, byte overload) { + super(value + " cannot fit in a byte"); + } + + public ValueCannotFitException(long value, char overload) { + super(value + " cannot fit in a char"); + } + + public ValueCannotFitException(long value, short overload) { + super(value + " cannot fit in a short"); + } + + public ValueCannotFitException(long value, int overload) { + super(value + " cannot fit in an int"); + } +} diff --git a/common/src/main/java/net/daporkchop/lib/common/util/exception/ValueOutOfBoundsException.java b/common/src/main/java/net/daporkchop/lib/common/util/exception/ValueOutOfBoundsException.java new file mode 100644 index 000000000..87adaffc6 --- /dev/null +++ b/common/src/main/java/net/daporkchop/lib/common/util/exception/ValueOutOfBoundsException.java @@ -0,0 +1,55 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.common.util.exception; + +/** + * Thrown when a value is too large to fit within some smaller data type. + * + * @author DaPorkchop_ + */ +public class ValueOutOfBoundsException extends IllegalArgumentException { + public ValueOutOfBoundsException(String s) { + super(s); + } + + public ValueOutOfBoundsException(byte value, byte expectedMin, byte expectedMax) { + super(String.format("%d is not within expected bounds (min: %d, max: %d)", value, expectedMin, expectedMax)); + } + + public ValueOutOfBoundsException(short value, short expectedMin, short expectedMax) { + super(String.format("%d is not within expected bounds (min: %d, max: %d)", value, expectedMin, expectedMax)); + } + + public ValueOutOfBoundsException(char value, char expectedMin, char expectedMax) { + super(String.format("%d is not within expected bounds (min: %d, max: %d)", value, expectedMin, expectedMax)); + } + + public ValueOutOfBoundsException(int value, int expectedMin, int expectedMax) { + super(String.format("%d is not within expected bounds (min: %d, max: %d)", value, expectedMin, expectedMax)); + } + + public ValueOutOfBoundsException(long value, long expectedMin, long expectedMax) { + super(String.format("%d is not within expected bounds (min: %d, max: %d)", value, expectedMin, expectedMax)); + } + + public ValueOutOfBoundsException(float value, float expectedMin, float expectedMax) { + super(String.format("%f is not within expected bounds (min: %f, max: %f)", value, expectedMin, expectedMax)); + } + + public ValueOutOfBoundsException(double value, double expectedMin, double expectedMax) { + super(String.format("%f is not within expected bounds (min: %f, max: %f)", value, expectedMin, expectedMax)); + } +} diff --git a/compression/build.gradle b/compression/build.gradle new file mode 100644 index 000000000..4171ad4a9 --- /dev/null +++ b/compression/build.gradle @@ -0,0 +1,20 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +dependencies { + compile project(":natives") + + compile "io.netty:netty-buffer:$nettyVersion" +} diff --git a/compression/src/main/java/net/daporkchop/lib/compression/CCtx.java b/compression/src/main/java/net/daporkchop/lib/compression/CCtx.java new file mode 100644 index 000000000..7ab36a959 --- /dev/null +++ b/compression/src/main/java/net/daporkchop/lib/compression/CCtx.java @@ -0,0 +1,58 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression; + +import io.netty.buffer.ByteBuf; +import lombok.NonNull; +import net.daporkchop.lib.compression.util.exception.DictionaryNotAllowedException; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; + +/** + * A context for doing repeated one-shot compression operations. + * + * @author DaPorkchop_ + */ +public interface CCtx extends Context { + /** + * Convenience method, equivalent to {@code compress(src, dst, null);}. + * + * @see #compress(ByteBuf, ByteBuf, ByteBuf) + */ + default boolean compress(@NonNull ByteBuf src, @NonNull ByteBuf dst) throws InvalidBufferTypeException { + return this.compress(src, dst, null); + } + + /** + * Compresses the given source data into the given destination buffer at the configured compression level. + *

+ * If the destination buffer does not have enough space writable for the compressed data, the operation will fail and both buffer's indices will remain + * unchanged, however the destination buffer's contents may be modified. + *

+ * In either case, the indices of the dictionary buffer remain unaffected. + * + * @param src the {@link ByteBuf} to read source data from + * @param dst the {@link ByteBuf} to write compressed data to + * @param dict the (possibly {@code null}) {@link ByteBuf} containing the dictionary to be used for compression + * @return whether or not compression was successful. If {@code false}, the destination buffer was too small for the compressed data + * @throws DictionaryNotAllowedException if the dictionary buffer is not {@code null} and this context does not allow use of a dictionary + */ + boolean compress(@NonNull ByteBuf src, @NonNull ByteBuf dst, ByteBuf dict) throws InvalidBufferTypeException, DictionaryNotAllowedException; + + /** + * @return the configured compression level + */ + int level(); +} diff --git a/compression/src/main/java/net/daporkchop/lib/compression/CompressionProvider.java b/compression/src/main/java/net/daporkchop/lib/compression/CompressionProvider.java new file mode 100644 index 000000000..4c00db3c1 --- /dev/null +++ b/compression/src/main/java/net/daporkchop/lib/compression/CompressionProvider.java @@ -0,0 +1,62 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression; + +import net.daporkchop.lib.common.util.PValidation; +import net.daporkchop.lib.common.util.exception.ValueCannotFitException; +import net.daporkchop.lib.compression.util.exception.InvalidCompressionLevelException; +import net.daporkchop.lib.natives.util.BufferTyped; + +/** + * An implementation of a compression algorithm. + * + * @author DaPorkchop_ + */ +public interface CompressionProvider extends BufferTyped { + @Override + boolean directAccepted(); + + /** + * @return the compression level with the worst compression ratio in exchange for the shortest compression times + */ + int levelFast(); + + /** + * @return the compression level used by default + */ + int levelDefault(); + + /** + * @return the compression level with the best compression ratio in exchange for the longest compression times + */ + int levelBest(); + + /** + * @see #compressBoundLong(long) + * @throws ValueCannotFitException if the returned value is too large to fit in an {@code int} + */ + default int compressBound(int srcSize) throws ValueCannotFitException { + return PValidation.toInt(this.compressBoundLong(srcSize)); + } + + /** + * Gets the maximum (worst-case) compressed size for input data of the given length. + * + * @param srcSize the size (in bytes) of the source data + * @return the worst-case size of the compressed data + */ + long compressBoundLong(long srcSize); +} diff --git a/compression/src/main/java/net/daporkchop/lib/compression/Context.java b/compression/src/main/java/net/daporkchop/lib/compression/Context.java new file mode 100644 index 000000000..375e0a364 --- /dev/null +++ b/compression/src/main/java/net/daporkchop/lib/compression/Context.java @@ -0,0 +1,41 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression; + +import io.netty.buffer.ByteBuf; +import lombok.NonNull; +import net.daporkchop.lib.natives.util.BufferTyped; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; +import net.daporkchop.lib.unsafe.capability.Releasable; + +/** + * Base interface for {@link PDeflater} and {@link PDeflater}. + *

+ * Unless explicitly specified, implementations of this class are not safe for use on multiple threads. + * + * @author DaPorkchop_ + */ +interface Context extends Releasable, BufferTyped { + /** + * @return the {@link CompressionProvider} that created this context + */ + CompressionProvider provider(); + + /** + * @return whether or not this implementation allows use of a dictionary + */ + boolean hasDict(); +} diff --git a/compression/src/main/java/net/daporkchop/lib/compression/DCtx.java b/compression/src/main/java/net/daporkchop/lib/compression/DCtx.java new file mode 100644 index 000000000..f6aee89b4 --- /dev/null +++ b/compression/src/main/java/net/daporkchop/lib/compression/DCtx.java @@ -0,0 +1,52 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression; + +import io.netty.buffer.ByteBuf; +import lombok.NonNull; +import net.daporkchop.lib.compression.util.exception.DictionaryNotAllowedException; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; + +/** + * A context for doing repeated one-shot compression operations. + * + * @author DaPorkchop_ + */ +public interface DCtx extends Context { + /** + * Convenience method, equivalent to {@code decompress(src, dst, null);}. + * + * @see #decompress(ByteBuf, ByteBuf, ByteBuf) + */ + default boolean decompress(@NonNull ByteBuf src, @NonNull ByteBuf dst) throws InvalidBufferTypeException { + return this.decompress(src, dst, null); + } + + /** + * Decompresses the given compressed data into the given destination buffer. + *

+ * If the destination buffer does not have enough space writable for the decompressed data, the operation will fail and both buffer's indices will remain + * unchanged, however the destination buffer's contents may be modified. + *

+ * In either case, the indices of the dictionary buffer remain unaffected. + * + * @param src the {@link ByteBuf} to read compressed data from + * @param dst the {@link ByteBuf} to write decompressed data to + * @param dict the (possibly {@code null}) {@link ByteBuf} containing the dictionary to be used for decompression + * @return whether or not decompression was successful. If {@code false}, the destination buffer was too small for the decompressed data + */ + boolean decompress(@NonNull ByteBuf src, @NonNull ByteBuf dst, ByteBuf dict) throws InvalidBufferTypeException, DictionaryNotAllowedException; +} diff --git a/compression/src/main/java/net/daporkchop/lib/compression/OneShotCompressionProvider.java b/compression/src/main/java/net/daporkchop/lib/compression/OneShotCompressionProvider.java new file mode 100644 index 000000000..a23e05592 --- /dev/null +++ b/compression/src/main/java/net/daporkchop/lib/compression/OneShotCompressionProvider.java @@ -0,0 +1,51 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression; + +import net.daporkchop.lib.common.util.PValidation; +import net.daporkchop.lib.common.util.exception.ValueCannotFitException; +import net.daporkchop.lib.compression.util.exception.InvalidCompressionLevelException; +import net.daporkchop.lib.natives.util.BufferTyped; + +/** + * An implementation of a compression algorithm that supports one-shot compression via {@link CCtx} and {@link DCtx}. + * + * @author DaPorkchop_ + */ +public interface OneShotCompressionProvider extends CompressionProvider { + /** + * Creates a new {@link CCtx} with the default compression level. + * + * @see #compressionContext(int) + */ + default CCtx compressionContext() { + return this.compressionContext(this.levelDefault()); + } + + /** + * Creates a new {@link CCtx} with the given compression level. + * + * @param level the compression level to use + * @return a new {@link CCtx} with the given compression level + * @throws InvalidCompressionLevelException if the given compression level is invalid + */ + CCtx compressionContext(int level) throws InvalidCompressionLevelException; + + /** + * @return a new {@link DCtx} + */ + DCtx decompressionContext(); +} diff --git a/compression/src/main/java/net/daporkchop/lib/compression/PDeflater.java b/compression/src/main/java/net/daporkchop/lib/compression/PDeflater.java new file mode 100644 index 000000000..8091ca98c --- /dev/null +++ b/compression/src/main/java/net/daporkchop/lib/compression/PDeflater.java @@ -0,0 +1,71 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression; + +import io.netty.buffer.ByteBuf; +import lombok.NonNull; +import net.daporkchop.lib.compression.util.exception.ContextFinishedException; +import net.daporkchop.lib.compression.util.exception.ContextFinishingException; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; + +/** + * Deflates (compresses) data. + * + * @author DaPorkchop_ + */ +public interface PDeflater extends StreamingContext { + /** + * Deflates the given source data into the given destination buffer. + *

+ * Rather than expanding the destination buffer if needed, this method will simply abort compression if not enough space is available. In such a case the + * reader/writer indices of both buffers will remain unaffected, however the contents of the destination buffer may be modified. + *

+ * This method will implicitly reset the context before the actual compression. Any previous state will be ignored. + * + * @param src the {@link ByteBuf} to read data from + * @param dst the {@link ByteBuf} to write data to + * @return whether or not there was enough space in the destination buffer for the compressed data + */ + boolean fullDeflate(@NonNull ByteBuf src, @NonNull ByteBuf dst) throws InvalidBufferTypeException; + + /** + * Deflates the given source data into the given destination buffer. + *

+ * This will continually grow the destination buffer until there is enough space for deflation to be finished successfully. + *

+ * This method will implicitly reset the context before the actual compression. Any previous state will be ignored. + * + * @param src the {@link ByteBuf} to read data from + * @param dst the {@link ByteBuf} to write data to + */ + default void fullDeflateGrowing(@NonNull ByteBuf src, @NonNull ByteBuf dst) throws InvalidBufferTypeException { + this.reset().src(src).dst(dst); + + do { + this.update(false); + } while (src.isReadable() && dst.ensureWritable(8192).isWritable()); + + while (!this.finish()) { + dst.ensureWritable(8192); + } + } + + @Override + PDeflater update(boolean flush) throws ContextFinishedException, ContextFinishingException; + + @Override + boolean finish() throws ContextFinishedException; +} diff --git a/compression/src/main/java/net/daporkchop/lib/compression/PInflater.java b/compression/src/main/java/net/daporkchop/lib/compression/PInflater.java new file mode 100644 index 000000000..70ec49e17 --- /dev/null +++ b/compression/src/main/java/net/daporkchop/lib/compression/PInflater.java @@ -0,0 +1,71 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression; + +import io.netty.buffer.ByteBuf; +import lombok.NonNull; +import net.daporkchop.lib.compression.util.exception.ContextFinishedException; +import net.daporkchop.lib.compression.util.exception.ContextFinishingException; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; + +/** + * Inflates (decompresses) data. + * + * @author DaPorkchop_ + */ +public interface PInflater extends StreamingContext { + /** + * Inflates the given source data into the given destination buffer. + *

+ * Rather than expanding the destination buffer if needed, this method will simply abort decompression if not enough space is available. In such a case the + * reader/writer indices of both buffers will remain unaffected, however the contents of the destination buffer may be modified. + *

+ * This method will implicitly reset the context before the actual decompression. Any previous state will be ignored. + * + * @param src the {@link ByteBuf} to read data from + * @param dst the {@link ByteBuf} to write data to + * @return whether or not there was enough space in the destination buffer for the decompressed data + */ + boolean fullInflate(@NonNull ByteBuf src, @NonNull ByteBuf dst) throws InvalidBufferTypeException; + + /** + * Inflates the given source data into the given destination buffer. + *

+ * This will continually grow the destination buffer until there is enough space for inflation to be finished successfully. + *

+ * This method will implicitly reset the context before the actual decompression. Any previous state will be ignored. + * + * @param src the {@link ByteBuf} to read data from + * @param dst the {@link ByteBuf} to write data to + */ + default void fullInflateGrowing(@NonNull ByteBuf src, @NonNull ByteBuf dst) throws InvalidBufferTypeException { + this.reset().src(src).dst(dst); + + do { + this.update(true); + } while (src.isReadable() && dst.ensureWritable(8192).isWritable()); + + if (!this.finish()) { + throw new IllegalStateException(); + } + } + + @Override + PInflater update(boolean flush) throws ContextFinishedException, ContextFinishingException; + + @Override + boolean finish() throws ContextFinishedException; +} diff --git a/compression/src/main/java/net/daporkchop/lib/compression/StreamingCompressionProvider.java b/compression/src/main/java/net/daporkchop/lib/compression/StreamingCompressionProvider.java new file mode 100644 index 000000000..63cbd7902 --- /dev/null +++ b/compression/src/main/java/net/daporkchop/lib/compression/StreamingCompressionProvider.java @@ -0,0 +1,51 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression; + +import net.daporkchop.lib.common.util.PValidation; +import net.daporkchop.lib.common.util.exception.ValueCannotFitException; +import net.daporkchop.lib.compression.util.exception.InvalidCompressionLevelException; +import net.daporkchop.lib.natives.util.BufferTyped; + +/** + * An implementation of a compression algorithm that supports streaming compression via {@link PDeflater} and {@link PInflater}. + * + * @author DaPorkchop_ + */ +public interface StreamingCompressionProvider extends CompressionProvider { + /** + * Creates a new {@link PDeflater} with the default compression level. + * + * @see #deflater(int) + */ + default PDeflater deflater() { + return this.deflater(this.levelDefault()); + } + + /** + * Creates a new {@link PDeflater} with the given compression level. + * + * @param level the compression level to use + * @return a new {@link PDeflater} with the given compression level + * @throws InvalidCompressionLevelException if the given compression level is invalid + */ + PDeflater deflater(int level) throws InvalidCompressionLevelException; + + /** + * @return a new {@link PInflater} + */ + PInflater inflater(); +} diff --git a/compression/src/main/java/net/daporkchop/lib/compression/StreamingContext.java b/compression/src/main/java/net/daporkchop/lib/compression/StreamingContext.java new file mode 100644 index 000000000..953769003 --- /dev/null +++ b/compression/src/main/java/net/daporkchop/lib/compression/StreamingContext.java @@ -0,0 +1,97 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression; + +import io.netty.buffer.ByteBuf; +import lombok.NonNull; +import net.daporkchop.lib.compression.util.exception.ContextFinishedException; +import net.daporkchop.lib.compression.util.exception.ContextFinishingException; +import net.daporkchop.lib.compression.util.exception.DictionaryNotAllowedException; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; + +/** + * Base interface for {@link PDeflater} and {@link PDeflater}. + * @author DaPorkchop_ + */ +interface StreamingContext> extends Context { + /** + * Sets the context's current source buffer when processing data in streaming mode. + * + * @param src the {@link ByteBuf} to read data from + * @return this context + * @throws ContextFinishingException if this context is already being finished, and as such the source buffer may not be updated any more + */ + I src(@NonNull ByteBuf src) throws InvalidBufferTypeException, ContextFinishingException; + + /** + * Sets the context's current destination buffer when processing data in streaming mode. + * + * @param dst the {@link ByteBuf} to write data to + * @return this context + */ + I dst(@NonNull ByteBuf dst) throws InvalidBufferTypeException; + + /** + * Updates this context, processing as much data as possible. + *

+ * This will read from the source buffer and write to the destination buffer until the source buffer runs dry or the destination buffer fills up. + *

+ * Implementations may buffer any amount of data internally. + * + * @param flush whether or not the internal buffer should be flushed. If {@code true}, an attempt will be made to flush as much buffered data as possible. Note + * that this can cause a negative impact on the compression ratio. + * @return this context + * @throws ContextFinishedException if this context is already finished and needs to be reset before being used again + * @throws ContextFinishingException if this context is already being finished (but is not yet completely finished) + */ + I update(boolean flush) throws ContextFinishedException, ContextFinishingException; + + /** + * Finishes this context. + *

+ * This will read from the source buffer and write to the destination buffer until the source buffer runs dry or the destination buffer fills up, and then + * attempt to flush any internally buffered data and finish the (de)compression process. + * + * @return whether or not the context could be completed. If {@code false}, there is not enough space in the destination buffer for the context to finish + * @throws ContextFinishedException if this context is already finished and needs to be reset before being used again + */ + boolean finish() throws ContextFinishedException; + + /** + * Resets this context. + *

+ * This will discard any internal buffers and reset the source, destination and dictionary buffers to {@code null}. + * + * @return this context + */ + I reset(); + + /** + * Sets the dictionary to be used by this context. + *

+ * Must be called immediately after being initialized or reset. + *

+ * The dictionary will remain referenced until the context is reset. + * + * @param dict the new dictionary to use. The currently readable region of the buffer will be used as the dictionary. + * @return this context + * @throws DictionaryNotAllowedException if this context does not allow use of a dictionary + * @see #hasDict() + */ + default I dict(@NonNull ByteBuf dict) throws InvalidBufferTypeException, DictionaryNotAllowedException { + throw new DictionaryNotAllowedException(); + } +} diff --git a/compression/src/main/java/net/daporkchop/lib/compression/util/StreamingWrapperCCtx.java b/compression/src/main/java/net/daporkchop/lib/compression/util/StreamingWrapperCCtx.java new file mode 100644 index 000000000..171d9408f --- /dev/null +++ b/compression/src/main/java/net/daporkchop/lib/compression/util/StreamingWrapperCCtx.java @@ -0,0 +1,100 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.util; + +import io.netty.buffer.ByteBuf; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import net.daporkchop.lib.compression.CCtx; +import net.daporkchop.lib.compression.CompressionProvider; +import net.daporkchop.lib.compression.PDeflater; +import net.daporkchop.lib.compression.StreamingCompressionProvider; +import net.daporkchop.lib.compression.util.exception.DictionaryNotAllowedException; +import net.daporkchop.lib.compression.util.exception.InvalidCompressionLevelException; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; +import net.daporkchop.lib.unsafe.util.AbstractReleasable; + +/** + * An implementation of {@link CCtx} that wraps a {@link PDeflater}. + * + * @author DaPorkchop_ + */ +@RequiredArgsConstructor(access = AccessLevel.PROTECTED) +@Accessors(fluent = true) +public class StreamingWrapperCCtx extends AbstractReleasable implements CCtx { + @Getter + @NonNull + protected final StreamingCompressionProvider provider; + @NonNull + protected final PDeflater deflater; + @Getter + protected final int level; + + public StreamingWrapperCCtx(@NonNull StreamingCompressionProvider provider, int level) throws InvalidCompressionLevelException { + this(provider, provider.deflater(level), level); + } + + @Override + public boolean compress(@NonNull ByteBuf src, @NonNull ByteBuf dst) throws InvalidBufferTypeException { + return this.deflater.reset().fullDeflate(src, dst); + } + + @Override + public boolean compress(@NonNull ByteBuf src, @NonNull ByteBuf dst, ByteBuf dict) throws InvalidBufferTypeException, DictionaryNotAllowedException { + if (dict != null && !this.hasDict()) { + throw new DictionaryNotAllowedException(); + } + + this.deflater.reset(); + if (dict != null) { + this.deflater.dict(dict); + } + return this.deflater.fullDeflate(src, dst); + } + + @Override + public boolean hasDict() { + return this.deflater.hasDict(); + } + + @Override + public boolean directAccepted() { + return this.deflater.directAccepted(); + } + + @Override + public boolean heapAccepted() { + return this.deflater.heapAccepted(); + } + + @Override + public boolean isAcceptable(@NonNull ByteBuf buf) { + return this.deflater.isAcceptable(buf); + } + + @Override + public ByteBuf assertAcceptable(@NonNull ByteBuf buf) throws InvalidBufferTypeException { + return this.deflater.assertAcceptable(buf); + } + + @Override + protected void doRelease() { + this.deflater.release(); + } +} diff --git a/compression/src/main/java/net/daporkchop/lib/compression/util/StreamingWrapperDCtx.java b/compression/src/main/java/net/daporkchop/lib/compression/util/StreamingWrapperDCtx.java new file mode 100644 index 000000000..cf49d4e64 --- /dev/null +++ b/compression/src/main/java/net/daporkchop/lib/compression/util/StreamingWrapperDCtx.java @@ -0,0 +1,98 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.util; + +import io.netty.buffer.ByteBuf; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import net.daporkchop.lib.compression.CCtx; +import net.daporkchop.lib.compression.CompressionProvider; +import net.daporkchop.lib.compression.DCtx; +import net.daporkchop.lib.compression.PInflater; +import net.daporkchop.lib.compression.StreamingCompressionProvider; +import net.daporkchop.lib.compression.util.exception.DictionaryNotAllowedException; +import net.daporkchop.lib.compression.util.exception.InvalidCompressionLevelException; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; +import net.daporkchop.lib.unsafe.util.AbstractReleasable; + +/** + * An implementation of {@link DCtx} that wraps a {@link PInflater}. + * + * @author DaPorkchop_ + */ +@RequiredArgsConstructor(access = AccessLevel.PROTECTED) +@Accessors(fluent = true) +public class StreamingWrapperDCtx extends AbstractReleasable implements DCtx { + @Getter + protected final StreamingCompressionProvider provider; + @NonNull + protected final PInflater inflater; + + public StreamingWrapperDCtx(@NonNull StreamingCompressionProvider provider) { + this(provider, provider.inflater()); + } + + @Override + public boolean decompress(@NonNull ByteBuf src, @NonNull ByteBuf dst) throws InvalidBufferTypeException { + return this.inflater.reset().fullInflate(src, dst); + } + + @Override + public boolean decompress(@NonNull ByteBuf src, @NonNull ByteBuf dst, ByteBuf dict) throws InvalidBufferTypeException, DictionaryNotAllowedException { + if (dict != null && !this.hasDict()) { + throw new DictionaryNotAllowedException(); + } + + this.inflater.reset(); + if (dict != null) { + this.inflater.dict(dict); + } + return this.inflater.fullInflate(src, dst); + } + + @Override + public boolean hasDict() { + return this.inflater.hasDict(); + } + + @Override + public boolean directAccepted() { + return this.inflater.directAccepted(); + } + + @Override + public boolean heapAccepted() { + return this.inflater.heapAccepted(); + } + + @Override + public boolean isAcceptable(@NonNull ByteBuf buf) { + return this.inflater.isAcceptable(buf); + } + + @Override + public ByteBuf assertAcceptable(@NonNull ByteBuf buf) throws InvalidBufferTypeException { + return this.inflater.assertAcceptable(buf); + } + + @Override + protected void doRelease() { + this.inflater.release(); + } +} diff --git a/compression/src/main/java/net/daporkchop/lib/compression/util/exception/ContextFinishedException.java b/compression/src/main/java/net/daporkchop/lib/compression/util/exception/ContextFinishedException.java new file mode 100644 index 000000000..066ebb5e2 --- /dev/null +++ b/compression/src/main/java/net/daporkchop/lib/compression/util/exception/ContextFinishedException.java @@ -0,0 +1,27 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.util.exception; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +/** + * Thrown when a {@link net.daporkchop.lib.compression.Context} is used that is already finished, and was not reset. + * + * @author DaPorkchop_ + */ +public class ContextFinishedException extends RuntimeException { +} diff --git a/compression/src/main/java/net/daporkchop/lib/compression/util/exception/ContextFinishingException.java b/compression/src/main/java/net/daporkchop/lib/compression/util/exception/ContextFinishingException.java new file mode 100644 index 000000000..f9446f053 --- /dev/null +++ b/compression/src/main/java/net/daporkchop/lib/compression/util/exception/ContextFinishingException.java @@ -0,0 +1,24 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.util.exception; + +/** + * Thrown when a {@link net.daporkchop.lib.compression.Context} is updated that is already being finished (but was not completely finished), and was not reset. + * + * @author DaPorkchop_ + */ +public class ContextFinishingException extends ContextFinishedException { +} diff --git a/compression/src/main/java/net/daporkchop/lib/compression/util/exception/DictionaryNotAllowedException.java b/compression/src/main/java/net/daporkchop/lib/compression/util/exception/DictionaryNotAllowedException.java new file mode 100644 index 000000000..ec01af72d --- /dev/null +++ b/compression/src/main/java/net/daporkchop/lib/compression/util/exception/DictionaryNotAllowedException.java @@ -0,0 +1,24 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.util.exception; + +/** + * Thrown when a {@link net.daporkchop.lib.compression.Context} that does not allow use of a dictionary is given one anyway. + * + * @author DaPorkchop_ + */ +public class DictionaryNotAllowedException extends UnsupportedOperationException { +} diff --git a/compression/src/main/java/net/daporkchop/lib/compression/util/exception/InvalidCompressionLevelException.java b/compression/src/main/java/net/daporkchop/lib/compression/util/exception/InvalidCompressionLevelException.java new file mode 100644 index 000000000..989adc0e2 --- /dev/null +++ b/compression/src/main/java/net/daporkchop/lib/compression/util/exception/InvalidCompressionLevelException.java @@ -0,0 +1,38 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.util.exception; + +import lombok.NonNull; +import net.daporkchop.lib.compression.CompressionProvider; + +/** + * Thrown when an invalid compression level is given. + * + * @author DaPorkchop_ + */ +public class InvalidCompressionLevelException extends IllegalArgumentException { + public static int validate(int level, @NonNull CompressionProvider provider) throws InvalidCompressionLevelException { + if (level < provider.levelFast() || level > provider.levelBest()) { + throw new InvalidCompressionLevelException(level, provider); + } + + return level; + } + + public InvalidCompressionLevelException(int level, @NonNull CompressionProvider provider) { + super(String.format("Invalid level %d (must be in range %d-%d)", level, provider.levelFast(), provider.levelBest())); + } +} diff --git a/compression/zlib/Makefile b/compression/zlib/Makefile new file mode 100644 index 000000000..1ae6d5ef6 --- /dev/null +++ b/compression/zlib/Makefile @@ -0,0 +1,56 @@ +ifneq ($(BUILD),$(notdir $(CURDIR))) +.PHONY: $(BUILD) clean + +export OUTDIR := $(CURDIR)/src/main/resources +BUILDDIR := build/native/$(BUILD_TYPE)/$(BUILD) + +clean: + @if [ -d build/native/ ]; then rm -rf build/native/; fi + @rm -rf $(foreach arch,$(ARCHS),$(OUTDIR)/$(arch)) + +$(BUILD): + @[ -d $(BUILDDIR) ] || mkdir -p $(BUILDDIR) + @$(MAKE) --no-print-directory -C $(BUILDDIR) -f $(CURDIR)/Makefile BUILD=$(BUILD) build + +else +.PHONY: build + +include $(TOOLCHAINS)/$(BUILD) + +SOURCES := $(PROJDIR)/src/main/native/common $(PROJDIR)/src/main/native/$(BUILD) $(COMMONSRC)/common $(COMMONSRC)/$(BUILD) +SOURCES := $(abspath $(SOURCES)) + +CFILES := $(foreach dir,$(SOURCES),$(wildcard $(dir)/*.c)) +CPPFILES := $(foreach dir,$(SOURCES),$(wildcard $(dir)/*.cpp)) +OFILES := $(addsuffix .o,$(subst /,__,$(CFILES)) $(subst /,__,$(CPPFILES))) + +INCLUDE := $(addprefix -I,$(INCLUDES) $(CURDIR)) + +build: libzlib.$(EXT) + @echo "Copying libzlib.$(EXT) to $(OUTDIR)/$(BUILD)/..." + @[ -d $(OUTDIR)/$(BUILD) ] || mkdir -p $(OUTDIR)/$(BUILD) + @cp libzlib.$(EXT) $(OUTDIR)/$(BUILD)/libzlib.$(EXT) + +libzlib.$(EXT): $(CFILES) $(CPPFILES) $(OFILES) $(CURDIR)/lib-zlib/libz-ng.a + @echo "Linking $@..." + @$(LD) $(LDFLAGS) $(INCLUDE) -o $@ $(OFILES) $(CURDIR)/lib-zlib/libz-ng.a + @echo "Stripping $@..." + @$(STRIP) $@ + +%.c.o: $(CFILES) $(CURDIR)/lib-zlib/libz-ng.a + @echo "Building $(subst .o,,$(subst __,/,$@))..." + @$(CC) $(CFLAGS) $(INCLUDE) -c $(subst .o,,$(subst __,/,$@)) -o $@ + +%.cpp.o: $(CPPFILES) $(CURDIR)/lib-zlib/libz-ng.a + @echo "Building $(subst .o,,$(subst __,/,$@))..." + $(CXX) $(CXXFLAGS) $(INCLUDE) -c $(subst .o,,$(subst __,/,$@)) -o $@ + +$(CURDIR)/lib-zlib/libz-ng.a: $(TOPDIR)/zlib-ng-1.2.11.tar.gz + @[ ! -d lib-zlib ] || rm -rf lib-zlib/ + @tar zxf $(TOPDIR)/zlib-ng-1.2.11.tar.gz + @mv zlib-ng-1.2.11/ lib-zlib/ + @cd lib-zlib/ && ./configure --static && cd .. +# @cd lib-zlib/ && ./configure --static --with-gzfileops && cd .. + @$(MAKE) -C $(CURDIR)/lib-zlib/ -f $(CURDIR)/lib-zlib/Makefile libz-ng.a + +endif diff --git a/compression/zlib/build.gradle b/compression/zlib/build.gradle new file mode 100644 index 000000000..5f38939a3 --- /dev/null +++ b/compression/zlib/build.gradle @@ -0,0 +1,19 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +dependencies { + compile project(":compression") + compile project(":natives") +} diff --git a/compression/zlib/src/example/java/ZlibTest.java b/compression/zlib/src/example/java/ZlibTest.java new file mode 100644 index 000000000..c283dbc9f --- /dev/null +++ b/compression/zlib/src/example/java/ZlibTest.java @@ -0,0 +1,237 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.NonNull; +import net.daporkchop.lib.compression.PDeflater; +import net.daporkchop.lib.compression.PInflater; +import net.daporkchop.lib.compression.zlib.Zlib; + +import java.util.concurrent.ThreadLocalRandom; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; + +/** + * @author DaPorkchop_ + */ +public class ZlibTest { + private static final int SIZE = 1 << 26; // 64 MiB + private static final int DICT_SIZE = 256 * 8;//32768; + + public static void main(String... args) throws DataFormatException { + ByteBuf original = Unpooled.directBuffer(SIZE, SIZE).clear().ensureWritable(SIZE).writerIndex(SIZE); + for (int i = 0; i < SIZE; i++) { + original.setByte(i, i & 0xFF); + } + for (int i = 0; i < 256; i += 8) { + original.setLongLE(i, ThreadLocalRandom.current().nextLong()); + } + + { + ByteBuf compressedNative = Unpooled.directBuffer(SIZE >>> 4, SIZE >>> 4); + + try (PDeflater deflater = Zlib.PROVIDER.deflater()) { + if (!deflater.fullDeflate(original.slice(), compressedNative)) { + throw new IllegalStateException("Couldn't deflate data!"); + } + } + + System.out.printf("original: %d, compressed: %d\n", SIZE, compressedNative.readableBytes()); + + byte[] compressedHeap = new byte[compressedNative.readableBytes()]; + compressedNative.getBytes(0, compressedHeap); + + byte[] uncompressedHeap = new byte[SIZE]; + + { + Inflater inflater = new Inflater(); + inflater.setInput(compressedHeap); + int cnt = inflater.inflate(uncompressedHeap); + if (cnt != SIZE) { + throw new IllegalStateException(String.format("Only inflated %d/%d bytes!", cnt, SIZE)); + } + inflater.end(); + } + + validateEqual(original, Unpooled.wrappedBuffer(uncompressedHeap), 0, SIZE); + + ByteBuf uncompressedNative = Unpooled.directBuffer(SIZE, SIZE); + try (PInflater inflater = Zlib.PROVIDER.inflater()) { + if (!inflater.fullInflate(compressedNative, uncompressedNative)) { + throw new IllegalStateException("Couldn't inflate data!"); + } + validateEqual(original, uncompressedNative, 0, SIZE); + + uncompressedNative.clear(); + inflater.reset().src(compressedNative.resetReaderIndex()); + for (int i = 0; i < 16; i++) { + ByteBuf dst = uncompressedNative.slice((SIZE >>> 4) * i, SIZE >>> 4).clear(); + inflater.dst(dst).update(false); + if (dst.isWritable()) { + throw new IllegalStateException(); + } + } + validateEqual(original, uncompressedNative, 0, SIZE); + } + } + + { + ByteBuf compressedNative = Unpooled.directBuffer(16, SIZE >>> 4); + + try (PDeflater deflater = Zlib.PROVIDER.deflater()) { + deflater.dict(original.slice(0, DICT_SIZE)); + if (false) { + deflater.dst(compressedNative); + for (int i = 0; i < 16; i++) { + ByteBuf src = original.slice((SIZE >>> 4) * i, SIZE >>> 4); + deflater.src(src).update(false); + if (src.isReadable()) { + throw new IllegalStateException(); + } + } + + if (!deflater.finish()) { + throw new IllegalStateException("Couldn't deflate data!"); + } + } else if (true) { + deflater.fullDeflateGrowing(original.slice(), compressedNative); + } else { + if (!deflater.fullDeflate(original.slice(), compressedNative)) { + throw new IllegalStateException("Couldn't deflate data!"); + } + } + } + + System.out.printf("original: %d, compressed: %d\n", SIZE, compressedNative.readableBytes()); + + byte[] compressedHeap = new byte[compressedNative.readableBytes()]; + compressedNative.getBytes(0, compressedHeap); + + byte[] uncompressedHeap = new byte[SIZE]; + + { + Inflater inflater = new Inflater(); + + inflater.setInput(compressedHeap); + int cnt = inflater.inflate(uncompressedHeap); + if (cnt != 0) { + throw new IllegalStateException(String.format("Inflated %d bytes without asking for a dictionary?!?", cnt)); + } else if (!inflater.needsDictionary()) { + throw new IllegalStateException("Not requiring dictionary!"); + } + + byte[] dict = new byte[DICT_SIZE]; + original.getBytes(0, dict); + inflater.setDictionary(dict); + + cnt = inflater.inflate(uncompressedHeap); + if (cnt != SIZE) { + throw new IllegalStateException(String.format("Only inflated %d/%d bytes!", cnt, SIZE)); + } + inflater.end(); + } + + validateEqual(original, Unpooled.wrappedBuffer(uncompressedHeap), 0, SIZE); + + ByteBuf uncompressedNative = Unpooled.directBuffer(SIZE, SIZE); + try (PInflater inflater = Zlib.PROVIDER.inflater()) { + if (!inflater.dict(original.slice(0, DICT_SIZE)) + .fullInflate(compressedNative, uncompressedNative)) { + throw new IllegalStateException("Couldn't inflate data!"); + } + validateEqual(original, uncompressedNative, 0, SIZE); + + uncompressedNative.clear(); + inflater.reset() + .dict(original.slice(0, DICT_SIZE)) + .src(compressedNative.resetReaderIndex()); + for (int i = 0; i < 16; i++) { + ByteBuf dst = uncompressedNative.slice((SIZE >>> 4) * i, SIZE >>> 4).clear(); + inflater.dst(dst).update(false); + if (dst.isWritable()) { + throw new IllegalStateException(); + } + } + validateEqual(original, uncompressedNative, 0, SIZE); + } + } + + { + ByteBuf compressedNative = Unpooled.directBuffer(SIZE >>> 4, SIZE >>> 4); + + try (PDeflater deflater = Zlib.PROVIDER.deflater()) { + deflater.dst(compressedNative); + for (int i = 0; i < 16; i++) { + ByteBuf src = original.slice((SIZE >>> 4) * i, SIZE >>> 4); + deflater.src(src).update(true); + if (src.isReadable()) { + throw new IllegalStateException(); + } + } + + if (!deflater.finish()) { + throw new IllegalStateException("Couldn't deflate data!"); + } + } + + System.out.printf("original: %d, compressed: %d\n", SIZE, compressedNative.readableBytes()); + + byte[] compressedHeap = new byte[compressedNative.readableBytes()]; + compressedNative.getBytes(0, compressedHeap); + + byte[] uncompressedHeap = new byte[SIZE]; + + { + Inflater inflater = new Inflater(); + inflater.setInput(compressedHeap); + int cnt = inflater.inflate(uncompressedHeap); + if (cnt != SIZE) { + throw new IllegalStateException(String.format("Only inflated %d/%d bytes!", cnt, SIZE)); + } + inflater.end(); + } + + validateEqual(original, Unpooled.wrappedBuffer(uncompressedHeap), 0, SIZE); + + ByteBuf uncompressedNative = Unpooled.directBuffer(SIZE, SIZE); + try (PInflater inflater = Zlib.PROVIDER.inflater()) { + if (!inflater.fullInflate(compressedNative, uncompressedNative)) { + throw new IllegalStateException("Couldn't inflate data!"); + } + validateEqual(original, uncompressedNative, 0, SIZE); + + uncompressedNative.clear(); + inflater.reset().src(compressedNative.resetReaderIndex()); + for (int i = 0; i < 16; i++) { + ByteBuf dst = uncompressedNative.slice((SIZE >>> 4) * i, SIZE >>> 4).clear(); + inflater.dst(dst).update(false); + if (dst.isWritable()) { + throw new IllegalStateException(); + } + } + validateEqual(original, uncompressedNative, 0, SIZE); + } + } + } + + private static void validateEqual(@NonNull ByteBuf a, @NonNull ByteBuf b, int start, int size) { + for (int i = 0; i < size; i++) { + if (a.getByte(start + i) != b.getByte(start + i)) { + throw new IllegalStateException(); + } + } + } +} diff --git a/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/Zlib.java b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/Zlib.java new file mode 100644 index 000000000..b468ad7ad --- /dev/null +++ b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/Zlib.java @@ -0,0 +1,48 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zlib; + +import lombok.experimental.UtilityClass; +import net.daporkchop.lib.compression.zlib.java.JavaZlib; +import net.daporkchop.lib.compression.zlib.natives.NativeZlib; +import net.daporkchop.lib.natives.FeatureBuilder; + +/** + * @author DaPorkchop_ + */ +@UtilityClass +public class Zlib { + public final ZlibProvider PROVIDER = FeatureBuilder.create(Zlib.class) + .addNative("net.daporkchop.lib.compression.zlib.natives.NativeZlib", "zlib") + .addJava("net.daporkchop.lib.compression.zlib.java.JavaZlib") + .build(); + + public final int LEVEL_NONE = 0; //no compression at all + public final int LEVEL_FASTEST = 1; //fastest compression, worst ratio + public final int LEVEL_BEST = 9; //best ratio, slowest compression + public final int LEVEL_DEFAULT = -1; //uses the library default level + + public final int STRATEGY_DEFAULT = 0; + public final int STRATEGY_FILTERED = 1; + public final int STRATEGY_HUFFMAN = 2; + public final int STRATEGY_RLE = 3; //only supported by native impl + public final int STRATEGY_FIXED = 4; //only supported by native impl + + public final int MODE_ZLIB = 0; //DEFLATE with zlib headers + public final int MODE_GZIP = 1; //DEFLATE with gzip headers + public final int MODE_RAW = 2; //raw DEFLATE output + public final int MODE_AUTO = 3; //automatically detects zlib or gzip (inflater only) +} diff --git a/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/ZlibCCtx.java b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/ZlibCCtx.java new file mode 100644 index 000000000..4fd5e3e6d --- /dev/null +++ b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/ZlibCCtx.java @@ -0,0 +1,43 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zlib; + +import io.netty.buffer.ByteBuf; +import lombok.NonNull; +import net.daporkchop.lib.compression.CCtx; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; + +/** + * An extension of {@link CCtx} for {@link Zlib}. + * + * @author DaPorkchop_ + */ +public interface ZlibCCtx extends CCtx { + /** + * @return the configured strategy + */ + int strategy(); + + /** + * @return the configured wrapping mode + */ + int mode(); + + @Override + default boolean hasDict() { + return true; + } +} diff --git a/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/ZlibDCtx.java b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/ZlibDCtx.java new file mode 100644 index 000000000..f692ffe18 --- /dev/null +++ b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/ZlibDCtx.java @@ -0,0 +1,39 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zlib; + +import io.netty.buffer.ByteBuf; +import lombok.NonNull; +import net.daporkchop.lib.compression.CCtx; +import net.daporkchop.lib.compression.DCtx; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; + +/** + * An extension of {@link DCtx} for {@link Zlib}. + * + * @author DaPorkchop_ + */ +public interface ZlibDCtx extends DCtx { + /** + * @return the configured wrapping mode + */ + int mode(); + + @Override + default boolean hasDict() { + return true; + } +} diff --git a/natives/src/main/java/net/daporkchop/lib/natives/zlib/JavaZlib.java b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/ZlibDeflater.java similarity index 70% rename from natives/src/main/java/net/daporkchop/lib/natives/zlib/JavaZlib.java rename to compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/ZlibDeflater.java index 39bc12357..b423473c6 100644 --- a/natives/src/main/java/net/daporkchop/lib/natives/zlib/JavaZlib.java +++ b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/ZlibDeflater.java @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -13,33 +13,24 @@ * */ -package net.daporkchop.lib.natives.zlib; +package net.daporkchop.lib.compression.zlib; -import net.daporkchop.lib.natives.NativeCode; +import io.netty.buffer.ByteBuf; +import lombok.NonNull; +import net.daporkchop.lib.compression.PDeflater; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; /** - * Pure Java implementation of {@link Zlib}. + * Extension of {@link PDeflater} for {@link Zlib}. * * @author DaPorkchop_ */ -public final class JavaZlib extends NativeCode.Impl implements Zlib { +public interface ZlibDeflater extends PDeflater { @Override - protected Zlib _get() { - return this; - } - - @Override - protected boolean _available() { + default boolean hasDict() { return true; } @Override - public PDeflater deflater(int level, int mode) { - return new JavaDeflater(level, mode); - } - - @Override - public PInflater inflater(int mode) { - return new JavaInflater(mode); - } + PDeflater dict(@NonNull ByteBuf dict) throws InvalidBufferTypeException; } diff --git a/natives/src/main/java/net/daporkchop/lib/natives/zlib/NativeZlib.java b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/ZlibInflater.java similarity index 65% rename from natives/src/main/java/net/daporkchop/lib/natives/zlib/NativeZlib.java rename to compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/ZlibInflater.java index ebf0cc03b..993e7a76a 100644 --- a/natives/src/main/java/net/daporkchop/lib/natives/zlib/NativeZlib.java +++ b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/ZlibInflater.java @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -13,37 +13,24 @@ * */ -package net.daporkchop.lib.natives.zlib; +package net.daporkchop.lib.compression.zlib; -import net.daporkchop.lib.natives.NativeCode; +import io.netty.buffer.ByteBuf; +import lombok.NonNull; +import net.daporkchop.lib.compression.PInflater; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; /** - * Native code implementation of {@link Zlib}. + * Extension of {@link PInflater} for {@link Zlib}. * * @author DaPorkchop_ */ -public final class NativeZlib extends NativeCode.NativeImpl implements Zlib { - static { - if (NativeCode.NativeImpl.AVAILABLE) { - NativeCode.loadNativeLibrary("zlib"); - - NativeDeflater.load(); - NativeInflater.load(); - } - } - +public interface ZlibInflater extends PInflater { @Override - protected Zlib _get() { - return this; + default boolean hasDict() { + return true; } @Override - public PDeflater deflater(int level, int mode) { - return new NativeDeflater(level, mode); - } - - @Override - public PInflater inflater(int mode) { - return new NativeInflater(mode); - } + PInflater dict(@NonNull ByteBuf dict) throws InvalidBufferTypeException; } diff --git a/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/ZlibProvider.java b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/ZlibProvider.java new file mode 100644 index 000000000..f0735c3b4 --- /dev/null +++ b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/ZlibProvider.java @@ -0,0 +1,274 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zlib; + + +import net.daporkchop.lib.common.util.PValidation; +import net.daporkchop.lib.common.util.exception.ValueCannotFitException; +import net.daporkchop.lib.compression.CompressionProvider; +import net.daporkchop.lib.compression.OneShotCompressionProvider; +import net.daporkchop.lib.compression.StreamingCompressionProvider; +import net.daporkchop.lib.compression.util.exception.InvalidCompressionLevelException; +import net.daporkchop.lib.natives.impl.Feature; + +/** + * Representation of a Zlib implementation. + * + * @author DaPorkchop_ + */ +public interface ZlibProvider extends StreamingCompressionProvider, OneShotCompressionProvider, Feature { + @Override + default int levelFast() { + return Zlib.LEVEL_FASTEST; + } + + @Override + default int levelDefault() { + return Zlib.LEVEL_DEFAULT; + } + + @Override + default int levelBest() { + return Zlib.LEVEL_BEST; + } + + @Override + default int compressBound(int srcSize) throws ValueCannotFitException { + return PValidation.toInt(this.compressBoundLong(srcSize, Zlib.MODE_ZLIB)); + } + + /** + * Gets the maximum (worst-case) compressed size for input data of the given length using the Gzip format. + * + * @see #compressBound(int) + */ + default int compressBoundGzip(int srcSize) throws ValueCannotFitException { + return PValidation.toInt(this.compressBoundLong(srcSize, Zlib.MODE_GZIP)); + } + + /** + * Gets the maximum (worst-case) compressed size for input data of the given length using the given mode. + * + * @param srcSize the size (in bytes) of the source data + * @param mode the {@link Zlib} mode to use. Must be one of {@link Zlib#MODE_ZLIB}, {@link Zlib#MODE_GZIP} or {@link Zlib#MODE_RAW} + * @return the worst-case size of the compressed data + * @see #compressBound(int) + */ + default int compressBound(long srcSize, int mode) throws ValueCannotFitException { + return PValidation.toInt(this.compressBoundLong(srcSize, mode)); + } + + @Override + default long compressBoundLong(long srcSize) { + return this.compressBoundLong(srcSize, Zlib.MODE_ZLIB); + } + + /** + * Gets the maximum (worst-case) compressed size for input data of the given length using the Gzip format. + * + * @see #compressBoundLong(long) + */ + default long compressBoundGzipLong(long srcSize) { + return this.compressBoundLong(srcSize, Zlib.MODE_GZIP); + } + + /** + * Gets the maximum (worst-case) compressed size for input data of the given length using the given mode. + * + * @param srcSize the size (in bytes) of the source data + * @param mode the {@link Zlib} mode to use. Must be one of {@link Zlib#MODE_ZLIB}, {@link Zlib#MODE_GZIP} or {@link Zlib#MODE_RAW} + * @return the worst-case size of the compressed data + */ + long compressBoundLong(long srcSize, int mode); + + /** + * @return a new {@link ZlibDeflater} + */ + @Override + default ZlibDeflater deflater() { + return this.deflater(Zlib.LEVEL_DEFAULT, Zlib.STRATEGY_DEFAULT, Zlib.MODE_ZLIB); + } + + /** + * Creates a new {@link ZlibDeflater}. + * + * @param level the {@link Zlib} level to use + * @return a new {@link ZlibDeflater} with the given level + */ + @Override + default ZlibDeflater deflater(int level) throws InvalidCompressionLevelException { + return this.deflater(level, Zlib.STRATEGY_DEFAULT, Zlib.MODE_ZLIB); + } + + /** + * Creates a new {@link ZlibDeflater}. + * + * @param level the {@link Zlib} level to use + * @param strategy the {@link Zlib} strategy to use + * @return a new {@link ZlibDeflater} with the given level and strategy + */ + default ZlibDeflater deflater(int level, int strategy) throws InvalidCompressionLevelException { + return this.deflater(level, strategy, Zlib.MODE_ZLIB); + } + + /** + * @return a new {@link ZlibDeflater} using the Gzip format + */ + default ZlibDeflater deflaterGzip() { + return this.deflater(Zlib.LEVEL_DEFAULT, Zlib.STRATEGY_DEFAULT, Zlib.MODE_GZIP); + } + + /** + * Creates a new {@link ZlibDeflater} using the Gzip format. + * + * @param level the {@link Zlib} level to use + * @return a new {@link ZlibDeflater} using the Gzip format with the given level + */ + default ZlibDeflater deflaterGzip(int level) throws InvalidCompressionLevelException { + return this.deflater(level, Zlib.STRATEGY_DEFAULT, Zlib.MODE_GZIP); + } + + /** + * Creates a new {@link ZlibDeflater} using the Gzip format. + * + * @param level the {@link Zlib} level to use + * @param strategy the {@link Zlib} strategy to use + * @return a new {@link ZlibDeflater} using the Gzip format with the given level and strategy + */ + default ZlibDeflater deflaterGzip(int level, int strategy) throws InvalidCompressionLevelException { + return this.deflater(level, strategy, Zlib.MODE_GZIP); + } + + /** + * Creates a new {@link ZlibDeflater}. + * + * @param level the {@link Zlib} level to use. Must be in range {@link Zlib#LEVEL_NONE} to {@link Zlib#LEVEL_BEST} (inclusive), or {@link Zlib#LEVEL_DEFAULT} + * @param strategy the {@link Zlib} strategy to use. Must be one of {@link Zlib#STRATEGY_DEFAULT}, {@link Zlib#STRATEGY_FILTERED}, {@link Zlib#STRATEGY_HUFFMAN}, + * {@link Zlib#STRATEGY_RLE} or {@link Zlib#STRATEGY_FIXED} + * @param mode the {@link Zlib} mode to use. Must be one of {@link Zlib#MODE_ZLIB}, {@link Zlib#MODE_GZIP} or {@link Zlib#MODE_RAW} + * @return a new {@link ZlibDeflater} with the given level, strategy and mode + * @throws InvalidCompressionLevelException if the given compression level is invalid + */ + ZlibDeflater deflater(int level, int strategy, int mode) throws InvalidCompressionLevelException; + + /** + * @return a new {@link ZlibInflater} + */ + @Override + default ZlibInflater inflater() { + return this.inflater(Zlib.MODE_ZLIB); + } + + /** + * @return a new {@link ZlibInflater} using the Gzip format + */ + default ZlibInflater inflaterGzip() { + return this.inflater(Zlib.MODE_GZIP); + } + + /** + * @return a new {@link ZlibInflater} that will automatically detect whether the compressed data is in Zlib or Gzip format + */ + default ZlibInflater inflaterAuto() { + return this.inflater(Zlib.MODE_AUTO); + } + + /** + * Creates a new {@link ZlibInflater}. + * + * @param mode the {@link Zlib} mode to use. Must be one of {@link Zlib#MODE_ZLIB}, {@link Zlib#MODE_GZIP}, {@link Zlib#MODE_RAW} or {@link Zlib#MODE_AUTO} + * @return a new {@link ZlibInflater} + */ + ZlibInflater inflater(int mode); + + /** + * @return a new {@link ZlibCCtx} + */ + @Override + default ZlibCCtx compressionContext() { + return this.compressionContext(Zlib.LEVEL_BEST, Zlib.STRATEGY_DEFAULT, Zlib.MODE_ZLIB); + } + + /** + * @return a new {@link ZlibCCtx} with the given level + */ + @Override + default ZlibCCtx compressionContext(int level) throws InvalidCompressionLevelException { + return this.compressionContext(level, Zlib.STRATEGY_DEFAULT, Zlib.MODE_ZLIB); + } + + /** + * @return a new {@link ZlibCCtx} with the given level and strategy + */ + default ZlibCCtx compressionContext(int level, int strategy) throws InvalidCompressionLevelException { + return this.compressionContext(level, strategy, Zlib.MODE_ZLIB); + } + + /** + * @return a new {@link ZlibCCtx} using the Gzip format + */ + default ZlibCCtx compressionContextGzip() { + return this.compressionContext(Zlib.LEVEL_BEST, Zlib.STRATEGY_DEFAULT, Zlib.MODE_GZIP); + } + + /** + * @return a new {@link ZlibCCtx} using the Gzip format with the given level + */ + default ZlibCCtx compressionContextGzip(int level) throws InvalidCompressionLevelException { + return this.compressionContext(level, Zlib.STRATEGY_DEFAULT, Zlib.MODE_GZIP); + } + + /** + * @return a new {@link ZlibCCtx} using the Gzip format with the given level and strategy + */ + default ZlibCCtx compressionContextGzip(int level, int strategy) throws InvalidCompressionLevelException { + return this.compressionContext(level, strategy, Zlib.MODE_GZIP); + } + + /** + * @return a new {@link ZlibCCtx} with the given level, strategy and mode + * @see #deflater(int, int, int) + */ + ZlibCCtx compressionContext(int level, int strategy, int mode); + + /** + * @return a new {@link ZlibDCtx} + */ + @Override + default ZlibDCtx decompressionContext() { + return this.decompressionContext(Zlib.MODE_ZLIB); + } + + /** + * @return a new {@link ZlibDCtx} using the Gzip format + */ + default ZlibDCtx decompressionContextGzip() { + return this.decompressionContext(Zlib.MODE_GZIP); + } + + /** + * @return a new {@link ZlibDCtx} that will automatically detect whether the compressed data is in Zlib or Gzip format + */ + default ZlibDCtx decompressionContextAuto() { + return this.decompressionContext(Zlib.MODE_AUTO); + } + + /** + * @return a new {@link ZlibDCtx} with the given mode + * @see #inflater(int) + */ + ZlibDCtx decompressionContext(int mode); +} diff --git a/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/java/JavaZlib.java b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/java/JavaZlib.java new file mode 100644 index 000000000..416de5527 --- /dev/null +++ b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/java/JavaZlib.java @@ -0,0 +1,73 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zlib.java; + +import net.daporkchop.lib.common.util.PValidation; +import net.daporkchop.lib.compression.zlib.Zlib; +import net.daporkchop.lib.compression.zlib.ZlibCCtx; +import net.daporkchop.lib.compression.zlib.ZlibDCtx; +import net.daporkchop.lib.compression.zlib.ZlibDeflater; +import net.daporkchop.lib.compression.zlib.ZlibInflater; +import net.daporkchop.lib.compression.zlib.ZlibProvider; +import net.daporkchop.lib.natives.FeatureBuilder; +import net.daporkchop.lib.natives.impl.NativeFeature; + +/** + * @author DaPorkchop_ + */ +public final class JavaZlib extends NativeFeature implements ZlibProvider { + @Override + public boolean directAccepted() { + return false; + } + + @Override + public long compressBoundLong(long srcSize, int mode) { + //extracted from deflate.c, i'm assuming that the java implementation has the same limits + PValidation.ensureNonNegative(srcSize); + long conservativeUpperBound = srcSize + ((srcSize + 7L) >> 3L) + ((srcSize + 63L) >> 6L) + 5L; + switch (mode) { + case Zlib.MODE_ZLIB: + return conservativeUpperBound + 6L + 4L; //additional +4 in case `strstart`? whatever that means + case Zlib.MODE_GZIP: + return conservativeUpperBound + 18L; //assume there is no gzip message + case Zlib.MODE_RAW: + return conservativeUpperBound; + default: + throw new IllegalArgumentException("Invalid Zlib compression mode: " + mode); + } + } + + @Override + public ZlibDeflater deflater(int level, int strategy, int mode) { + throw new UnsupportedOperationException(); //TODO + } + + @Override + public ZlibInflater inflater(int mode) { + throw new UnsupportedOperationException(); //TODO + } + + @Override + public ZlibCCtx compressionContext(int level, int strategy, int mode) { + throw new UnsupportedOperationException(); //TODO + } + + @Override + public ZlibDCtx decompressionContext(int mode) { + throw new UnsupportedOperationException(); //TODO + } +} diff --git a/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/natives/NativeZlib.java b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/natives/NativeZlib.java new file mode 100644 index 000000000..214aed326 --- /dev/null +++ b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/natives/NativeZlib.java @@ -0,0 +1,63 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zlib.natives; + +import net.daporkchop.lib.common.util.PValidation; +import net.daporkchop.lib.compression.zlib.Zlib; +import net.daporkchop.lib.compression.zlib.ZlibCCtx; +import net.daporkchop.lib.compression.zlib.ZlibDCtx; +import net.daporkchop.lib.compression.zlib.ZlibDeflater; +import net.daporkchop.lib.compression.zlib.ZlibInflater; +import net.daporkchop.lib.compression.zlib.ZlibProvider; +import net.daporkchop.lib.natives.impl.NativeFeature; + +/** + * @author DaPorkchop_ + */ +public final class NativeZlib extends NativeFeature implements ZlibProvider { + static { + NativeZlibDeflater.load(); + NativeZlibInflater.load(); + } + + @Override + public boolean directAccepted() { + return true; + } + + @Override + public native long compressBoundLong(long srcSize, int mode); + + @Override + public ZlibCCtx compressionContext(int level, int strategy, int mode) { + return new NativeZlibCCtx(this, level, strategy, mode); + } + + @Override + public ZlibDCtx decompressionContext(int mode) { + return new NativeZlibDCtx(this, mode); + } + + @Override + public ZlibDeflater deflater(int level, int strategy, int mode) { + return new NativeZlibDeflater(this, level, strategy, mode); + } + + @Override + public ZlibInflater inflater(int mode) { + return new NativeZlibInflater(this, mode); + } +} diff --git a/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/natives/NativeZlibCCtx.java b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/natives/NativeZlibCCtx.java new file mode 100644 index 000000000..f92995612 --- /dev/null +++ b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/natives/NativeZlibCCtx.java @@ -0,0 +1,47 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zlib.natives; + +import io.netty.buffer.ByteBuf; +import lombok.Getter; +import lombok.NonNull; +import lombok.experimental.Accessors; +import net.daporkchop.lib.compression.CCtx; +import net.daporkchop.lib.compression.util.StreamingWrapperCCtx; +import net.daporkchop.lib.compression.zlib.ZlibCCtx; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; + +/** + * @author DaPorkchop_ + */ +@Getter +@Accessors(fluent = true) +final class NativeZlibCCtx extends StreamingWrapperCCtx implements ZlibCCtx { + private final int strategy; + private final int mode; + + NativeZlibCCtx(@NonNull NativeZlib provider, int level, int strategy, int mode) { + super(provider, provider.deflater(level, strategy, mode), level); + + this.strategy = strategy; + this.mode = mode; + } + + @Override + public boolean hasDict() { + return true; + } +} diff --git a/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/natives/NativeZlibDCtx.java b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/natives/NativeZlibDCtx.java new file mode 100644 index 000000000..0a3a9ded0 --- /dev/null +++ b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/natives/NativeZlibDCtx.java @@ -0,0 +1,46 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zlib.natives; + +import io.netty.buffer.ByteBuf; +import lombok.Getter; +import lombok.NonNull; +import lombok.experimental.Accessors; +import net.daporkchop.lib.compression.PDeflater; +import net.daporkchop.lib.compression.util.StreamingWrapperCCtx; +import net.daporkchop.lib.compression.util.StreamingWrapperDCtx; +import net.daporkchop.lib.compression.zlib.ZlibDCtx; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; + +/** + * @author DaPorkchop_ + */ +@Getter +@Accessors(fluent = true) +final class NativeZlibDCtx extends StreamingWrapperDCtx implements ZlibDCtx { + private final int mode; + + NativeZlibDCtx(@NonNull NativeZlib provider, int mode) { + super(provider, provider.inflater(mode)); + + this.mode = mode; + } + + @Override + public boolean hasDict() { + return true; + } +} diff --git a/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/natives/NativeZlibDeflater.java b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/natives/NativeZlibDeflater.java new file mode 100644 index 000000000..b946b530f --- /dev/null +++ b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/natives/NativeZlibDeflater.java @@ -0,0 +1,223 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zlib.natives; + +import io.netty.buffer.ByteBuf; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import net.daporkchop.lib.compression.PDeflater; +import net.daporkchop.lib.compression.util.exception.ContextFinishedException; +import net.daporkchop.lib.compression.util.exception.ContextFinishingException; +import net.daporkchop.lib.compression.zlib.ZlibDeflater; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; +import net.daporkchop.lib.unsafe.PCleaner; +import net.daporkchop.lib.unsafe.util.AbstractReleasable; + +/** + * @author DaPorkchop_ + */ +@Accessors(fluent = true) +final class NativeZlibDeflater extends AbstractReleasable implements ZlibDeflater { + static native void load(); + + private static native long allocateCtx(int level, int strategy, int mode); + + private static native void releaseCtx(long ctx); + + private final long ctx; + + @Getter + private final NativeZlib provider; + private final PCleaner cleaner; + + private ByteBuf src; + private ByteBuf dst; + private ByteBuf dict; + + private int readBytes; + private int writtenBytes; + + private boolean reset; + private boolean started; + private boolean finishing; + private boolean finished; + + NativeZlibDeflater(@NonNull NativeZlib provider, int level, int strategy, int mode) { + this.provider = provider; + + this.ctx = allocateCtx(level, strategy, mode); + this.cleaner = PCleaner.cleaner(this, new Releaser(this.ctx)); + this.reset = true; + } + + @Override + public boolean fullDeflate(@NonNull ByteBuf src, @NonNull ByteBuf dst) throws InvalidBufferTypeException { + if (!src.hasMemoryAddress() || !dst.hasMemoryAddress()) { + throw InvalidBufferTypeException.direct(); + } + + this.reset(); //this will do nothing if we're already reset + this.reset = false; + + if (this.doFullDeflate(src.memoryAddress() + src.readerIndex(), src.readableBytes(), + dst.memoryAddress() + dst.writerIndex(), dst.writableBytes())) { + //increase indices if successful + src.skipBytes(this.readBytes); + dst.writerIndex(dst.writerIndex() + this.writtenBytes); + return true; + } else { + return false; + } + } + + private native boolean doFullDeflate(long srcAddr, int srcSize, long dstAddr, int dstSize); + + @Override + public PDeflater update(boolean flush) throws ContextFinishedException, ContextFinishingException { + this.update(this.src, this.dst, flush); + return this; + } + + private void update(@NonNull ByteBuf src, @NonNull ByteBuf dst, boolean flush) { + if (this.finished) { + throw new ContextFinishedException(); + } else if (this.finishing) { + throw new ContextFinishingException(); + } + + this.reset = false; + this.started = true; + + this.doUpdate(src.memoryAddress() + src.readerIndex(), src.readableBytes(), + dst.memoryAddress() + dst.writerIndex(), dst.writableBytes(), + flush); + + //increase indices + src.skipBytes(this.readBytes); + dst.writerIndex(dst.writerIndex() + this.writtenBytes); + } + + private native void doUpdate(long srcAddr, int srcSize, long dstAddr, int dstSize, boolean flush); + + @Override + public boolean finish() throws ContextFinishedException { + return this.finish(this.src, this.dst); + } + + private boolean finish(@NonNull ByteBuf src, @NonNull ByteBuf dst) { + if (this.finished) { + throw new ContextFinishedException(); + } + + this.reset = false; + this.started = true; + this.finishing = true; + + if (this.doFinish(src.memoryAddress() + src.readerIndex(), src.readableBytes(), + dst.memoryAddress() + dst.writerIndex(), dst.writableBytes())) { + //increase indices if successful + src.skipBytes(this.readBytes); + dst.writerIndex(dst.writerIndex() + this.writtenBytes); + return true; + } else { + return false; + } + } + + private native boolean doFinish(long srcAddr, int srcSize, long dstAddr, int dstSize); + + @Override + public PDeflater reset() { + if (!this.reset) { + this.src = null; + this.dst = null; + if (this.dict != null) { + this.dict.release(); + this.dict = null; + } + + this.readBytes = 0; + this.writtenBytes = 0; + + this.started = false; + this.finishing = false; + this.finished = false; + + this.doReset(); + } + return this; + } + + private native void doReset(); + + @Override + public PDeflater dict(@NonNull ByteBuf dict) throws InvalidBufferTypeException { + if (!dict.hasMemoryAddress()) { + throw InvalidBufferTypeException.direct(); + } else if (this.started) { + throw new IllegalStateException("Cannot set dictionary after compression has started!"); + } else if (this.dict != null) { + throw new IllegalStateException("Dictionary has already been set!"); + } + + this.dict = dict = dict.retainedSlice(); + this.doDict(dict.memoryAddress(), dict.readableBytes()); + + return this; + } + + private native void doDict(long dictAddr, int dictSize); + + @Override + public PDeflater src(@NonNull ByteBuf src) throws InvalidBufferTypeException { + if (!src.hasMemoryAddress()) { + throw InvalidBufferTypeException.direct(); + } + this.src = src; + return this; + } + + @Override + public PDeflater dst(@NonNull ByteBuf dst) throws InvalidBufferTypeException { + if (!dst.hasMemoryAddress()) { + throw InvalidBufferTypeException.direct(); + } + this.dst = dst; + return this; + } + + @Override + public boolean directAccepted() { + return true; + } + + @Override + protected void doRelease() { + this.cleaner.clean(); + } + + @RequiredArgsConstructor + private static final class Releaser implements Runnable { + private final long ctx; + + @Override + public void run() { + releaseCtx(this.ctx); + } + } +} diff --git a/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/natives/NativeZlibInflater.java b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/natives/NativeZlibInflater.java new file mode 100644 index 000000000..474365c3d --- /dev/null +++ b/compression/zlib/src/main/java/net/daporkchop/lib/compression/zlib/natives/NativeZlibInflater.java @@ -0,0 +1,224 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zlib.natives; + +import io.netty.buffer.ByteBuf; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import net.daporkchop.lib.compression.PInflater; +import net.daporkchop.lib.compression.util.exception.ContextFinishedException; +import net.daporkchop.lib.compression.util.exception.ContextFinishingException; +import net.daporkchop.lib.compression.zlib.ZlibInflater; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; +import net.daporkchop.lib.unsafe.PCleaner; +import net.daporkchop.lib.unsafe.util.AbstractReleasable; + +/** + * @author DaPorkchop_ + */ +@Accessors(fluent = true) +final class NativeZlibInflater extends AbstractReleasable implements ZlibInflater { + static native void load(); + + private static native long allocateCtx(int mode); + + private static native void releaseCtx(long ctx); + + private final long ctx; + + @Getter + private final NativeZlib provider; + private final PCleaner cleaner; + + private ByteBuf src; + private ByteBuf dst; + private ByteBuf dict; + + private int readBytes; + private int writtenBytes; + + private boolean reset; + private boolean started; + private boolean finishing; + private boolean finished; + + NativeZlibInflater(@NonNull NativeZlib provider, int mode) { + this.provider = provider; + + this.ctx = allocateCtx(mode); + this.cleaner = PCleaner.cleaner(this, new Releaser(this.ctx)); + this.reset = true; + } + + @Override + public boolean fullInflate(@NonNull ByteBuf src, @NonNull ByteBuf dst) throws InvalidBufferTypeException { + if (!src.hasMemoryAddress() || !dst.hasMemoryAddress()) { + throw InvalidBufferTypeException.direct(); + } + + this.reset(); //this will do nothing if we're already reset + this.reset = false; + + ByteBuf dict = this.dict; + if (this.doFullInflate(src.memoryAddress() + src.readerIndex(), src.readableBytes(), + dst.memoryAddress() + dst.writerIndex(), dst.writableBytes(), + dict == null ? 0L : dict.memoryAddress(), dict == null ? 0 : dict.readableBytes())) { + //increase indices if successful + src.skipBytes(this.readBytes); + dst.writerIndex(dst.writerIndex() + this.writtenBytes); + return true; + } else { + return false; + } + } + + private native boolean doFullInflate(long srcAddr, int srcSize, long dstAddr, int dstSize, long dictAddr, int dictSize); + + @Override + public PInflater update(boolean flush) throws ContextFinishedException, ContextFinishingException { + this.update(this.src, this.dst, this.dict, flush); + return this; + } + + private void update(@NonNull ByteBuf src, @NonNull ByteBuf dst, ByteBuf dict, boolean flush) { + if (this.finished) { + throw new ContextFinishedException(); + } else if (this.finishing) { + throw new ContextFinishingException(); + } + + this.reset = false; + this.started = true; + + this.doUpdate(src.memoryAddress() + src.readerIndex(), src.readableBytes(), + dst.memoryAddress() + dst.writerIndex(), dst.writableBytes(), + dict == null ? 0L : dict.memoryAddress(), dict == null ? 0 : dict.readableBytes(), + flush); + + //increase indices + src.skipBytes(this.readBytes); + dst.writerIndex(dst.writerIndex() + this.writtenBytes); + } + + private native void doUpdate(long srcAddr, int srcSize, long dstAddr, int dstSize, long dictAddr, int dictSize, boolean flush); + + @Override + public boolean finish() throws ContextFinishedException { + return this.finish(this.src, this.dst, this.dict); + } + + private boolean finish(@NonNull ByteBuf src, @NonNull ByteBuf dst, ByteBuf dict) { + if (this.finished) { + return true; + } + + this.reset = false; + this.started = true; + this.finishing = true; + + if (this.doFinish(src.memoryAddress() + src.readerIndex(), src.readableBytes(), + dst.memoryAddress() + dst.writerIndex(), dst.writableBytes(), + dict == null ? 0L : dict.memoryAddress(), dict == null ? 0 : dict.readableBytes())) { + //increase indices if successful + src.skipBytes(this.readBytes); + dst.writerIndex(dst.writerIndex() + this.writtenBytes); + return true; + } else { + return false; + } + } + + private native boolean doFinish(long srcAddr, int srcSize, long dstAddr, int dstSize, long dictAddr, int dictSize); + + @Override + public PInflater reset() { + if (!this.reset) { + this.src = null; + this.dst = null; + if (this.dict != null) { + this.dict.release(); + this.dict = null; + } + + this.readBytes = 0; + this.writtenBytes = 0; + + this.started = false; + this.finishing = false; + this.finished = false; + + this.doReset(); + } + return this; + } + + private native void doReset(); + + @Override + public PInflater dict(@NonNull ByteBuf dict) throws InvalidBufferTypeException { + if (!dict.hasMemoryAddress()) { + throw InvalidBufferTypeException.direct(); + } else if (this.started) { + throw new IllegalStateException("Cannot set dictionary after decompression has started!"); + } else if (this.dict != null) { + throw new IllegalStateException("Dictionary has already been set!"); + } + + this.dict = dict.retainedSlice(); + + return this; + } + + @Override + public PInflater src(@NonNull ByteBuf src) throws InvalidBufferTypeException { + if (!src.hasMemoryAddress()) { + throw InvalidBufferTypeException.direct(); + } + this.src = src; + return this; + } + + @Override + public PInflater dst(@NonNull ByteBuf dst) throws InvalidBufferTypeException { + if (!dst.hasMemoryAddress()) { + throw InvalidBufferTypeException.direct(); + } + this.dst = dst; + return this; + } + + @Override + public boolean directAccepted() { + return true; + } + + @Override + protected void doRelease() { + this.cleaner.clean(); + } + + @RequiredArgsConstructor + private static final class Releaser implements Runnable { + private final long ctx; + + @Override + public void run() { + releaseCtx(this.ctx); + } + } +} diff --git a/compression/zlib/src/main/native/common/NativeZlib.cpp b/compression/zlib/src/main/native/common/NativeZlib.cpp new file mode 100644 index 000000000..c32032e84 --- /dev/null +++ b/compression/zlib/src/main/native/common/NativeZlib.cpp @@ -0,0 +1,23 @@ +#include +#include "NativeZlib.h" + +#include + +__attribute__((visibility("default"))) jlong JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlib_compressBoundLong + (JNIEnv* env, jobject obj, jlong srcSize, jint mode) { + if (srcSize < 0) { + return throwException(env, "srcSize may not be negative!", srcSize); + } + + long conservativeUpperBound = zng_deflateBound(nullptr, srcSize); + switch (mode) { + case 0: //zlib + return conservativeUpperBound + 6 + 4; //additional +4 in case `strstart`? whatever that means + case 1: //gzip + return conservativeUpperBound + 18; //assume there is no gzip message + case 2: //raw + return conservativeUpperBound; + default: + return throwException(env, "Invalid zlib mode!", mode); + } +} diff --git a/compression/zlib/src/main/native/common/NativeZlib.h b/compression/zlib/src/main/native/common/NativeZlib.h new file mode 100644 index 000000000..1bec65b3e --- /dev/null +++ b/compression/zlib/src/main/native/common/NativeZlib.h @@ -0,0 +1,25 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +//actually it's not, it was initially though +//easier to make this by hand lol +#include + +#ifndef _Included_net_daporkchop_lib_compression_zlib_natives_NativeZlib +#define _Included_net_daporkchop_lib_compression_zlib_natives_NativeZlib + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Class: net_daporkchop_lib_compression_zlib_natives_NativeZlib + * Method: compressBoundLong + * Signature: (JI)J + */ +JNIEXPORT jlong JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlib_compressBoundLong + (JNIEnv *, jobject, jlong, jint); + +#ifdef __cplusplus +} +#endif + +#endif //_Included_net_daporkchop_lib_compression_zlib_natives_NativeZlib diff --git a/compression/zlib/src/main/native/common/NativeZlibDeflater.cpp b/compression/zlib/src/main/native/common/NativeZlibDeflater.cpp new file mode 100644 index 000000000..552d4943e --- /dev/null +++ b/compression/zlib/src/main/native/common/NativeZlibDeflater.cpp @@ -0,0 +1,173 @@ +#include +#include "NativeZlibDeflater.h" + +#include + +static jfieldID ctxID; +static jfieldID readBytesID; +static jfieldID writtenBytesID; +static jfieldID resetID; +static jfieldID finishedID; + +__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater_load + (JNIEnv* env, jclass cla) { + ctxID = env->GetFieldID(cla, "ctx", "J"); + readBytesID = env->GetFieldID(cla, "readBytes", "I"); + writtenBytesID = env->GetFieldID(cla, "writtenBytes", "I"); + resetID = env->GetFieldID(cla, "reset", "Z"); + finishedID = env->GetFieldID(cla, "finished", "Z"); +} + +__attribute__((visibility("default"))) jlong JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater_allocateCtx + (JNIEnv* env, jclass cla, jint level, jint strategy, jint mode) { + if (level < -1 || level > 9) { + throwException(env, "Invalid level!", level); + return 0; + } else if (strategy < 0 || strategy > 4) { + throwException(env, "Invalid strategy!", strategy); + return 0; + } + + int windowBits; + switch (mode) { + case 0: //zlib + windowBits = 15; + break; + case 1: //gzip + windowBits = 15 + 16; + break; + case 2: //raw + windowBits = -15; + break; + default: + return throwException(env, "Invalid deflater mode!", mode); + } + + zng_stream* stream = (zng_stream*) new char[sizeof(zng_stream)](); + + int ret = zng_deflateInit2(stream, level, Z_DEFLATED, windowBits, 8, strategy); + + if (ret != Z_OK) { + const char* msg = stream->msg; + delete stream; + throwException(env, msg == nullptr ? "Couldn't init deflater!" : msg, ret); + return 0; + } + + return (jlong) stream; +} + +__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater_releaseCtx + (JNIEnv* env, jclass cla, jlong ctx) { + zng_stream* stream = (zng_stream*) ctx; + int ret = zng_deflateReset(stream); + + if (ret != Z_OK) { + throwException(env, stream->msg == nullptr ? "Couldn't reset deflater!" : stream->msg, ret); + return; + } + + ret = zng_deflateEnd(stream); + const char* msg = stream->msg; + delete stream; + + if (ret != Z_OK) { + throwException(env, msg == nullptr ? "Couldn't end deflater!" : msg, ret); + } +} + +__attribute__((visibility("default"))) jboolean JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater_doFullDeflate + (JNIEnv* env, jobject obj, jlong srcAddr, jint srcSize, jlong dstAddr, jint dstSize) { + zng_stream* stream = (zng_stream*) env->GetLongField(obj, ctxID); + + //set stream buffers + stream->next_in = (unsigned char*) srcAddr; + stream->avail_in = srcSize; + + stream->next_out = (unsigned char*) dstAddr; + stream->avail_out = dstSize; + + int ret = zng_deflate(stream, Z_FINISH); + if (ret == Z_STREAM_END) { + env->SetIntField(obj, readBytesID, srcSize - stream->avail_in); + env->SetIntField(obj, writtenBytesID, dstSize - stream->avail_out); + return true; + } else if (ret != Z_OK) { + throwException(env, stream->msg == nullptr ? "Invalid return value from deflate()!" : stream->msg, ret); + } + + return false; +} + +__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater_doUpdate + (JNIEnv* env, jobject obj, jlong srcAddr, jint srcSize, jlong dstAddr, jint dstSize, jboolean flush) { + zng_stream* stream = (zng_stream*) env->GetLongField(obj, ctxID); + + //set stream buffers + stream->next_in = (unsigned char*) srcAddr; + stream->avail_in = srcSize; + + stream->next_out = (unsigned char*) dstAddr; + stream->avail_out = dstSize; + + int ret = zng_deflate(stream, flush ? Z_SYNC_FLUSH : Z_NO_FLUSH); + if (ret != Z_OK) { + throwException(env, stream->msg == nullptr ? "Invalid return value from deflate()!" : stream->msg, ret); + return; + } + + env->SetIntField(obj, readBytesID, srcSize - stream->avail_in); + env->SetIntField(obj, writtenBytesID, dstSize - stream->avail_out); +} + +__attribute__((visibility("default"))) jboolean JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater_doFinish + (JNIEnv* env, jobject obj, jlong srcAddr, jint srcSize, jlong dstAddr, jint dstSize) { + zng_stream* stream = (zng_stream*) env->GetLongField(obj, ctxID); + + //set stream buffers + stream->next_in = (unsigned char*) srcAddr; + stream->avail_in = srcSize; + + stream->next_out = (unsigned char*) dstAddr; + stream->avail_out = dstSize; + + int ret = zng_deflate(stream, Z_FINISH); + if (ret != Z_STREAM_END && ret != Z_OK) { + throwException(env, stream->msg == nullptr ? "Invalid return value from deflate()!" : stream->msg, ret); + return false; + } + + env->SetIntField(obj, readBytesID, srcSize - stream->avail_in); + env->SetIntField(obj, writtenBytesID, dstSize - stream->avail_out); + + if (ret == Z_STREAM_END) { + env->SetBooleanField(obj, finishedID, true); + return true; + } else { + return false; + } +} + +__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater_doReset + (JNIEnv* env, jobject obj) { + zng_stream* stream = (zng_stream*) env->GetLongField(obj, ctxID); + int ret = zng_deflateReset(stream); + + if (ret != Z_OK) { + throwException(env, stream->msg == nullptr ? "Couldn't reset deflater!" : stream->msg, ret); + return; + } + + env->SetBooleanField(obj, resetID, true); +} + +__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater_doDict + (JNIEnv* env, jobject obj, jlong dictAddr, jint dictSize) { + zng_stream* stream = (zng_stream*) env->GetLongField(obj, ctxID); + int ret = zng_deflateSetDictionary(stream, (unsigned char*) dictAddr, dictSize); + + if (ret != Z_OK) { + throwException(env, stream->msg == nullptr ? "Couldn't set deflater dictionary!" : stream->msg, ret); + return; + } +} diff --git a/compression/zlib/src/main/native/common/NativeZlibDeflater.h b/compression/zlib/src/main/native/common/NativeZlibDeflater.h new file mode 100644 index 000000000..7718d4327 --- /dev/null +++ b/compression/zlib/src/main/native/common/NativeZlibDeflater.h @@ -0,0 +1,81 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +//actually it's not, it was initially though +//easier to make this by hand lol +#include + +#ifndef _Included_net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater +#define _Included_net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Class: net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater + * Method: load + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater_load + (JNIEnv *, jclass); + +/* + * Class: net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater + * Method: allocateCtx + * Signature: (III)J + */ +JNIEXPORT jlong JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater_allocateCtx + (JNIEnv *, jclass, jint, jint, jint); + +/* + * Class: net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater + * Method: releaseCtx + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater_releaseCtx + (JNIEnv *, jclass, jlong); + +/* + * Class: net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater + * Method: doFullDeflate + * Signature: (JIJI)Z + */ +JNIEXPORT jboolean JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater_doFullDeflate + (JNIEnv *, jobject, jlong, jint, jlong, jint); + +/* + * Class: net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater + * Method: doUpdate + * Signature: (JIJIZ)V + */ +JNIEXPORT void JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater_doUpdate + (JNIEnv *, jobject, jlong, jint, jlong, jint, jboolean); + +/* + * Class: net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater + * Method: doFinish + * Signature: (JIJI)Z + */ +JNIEXPORT jboolean JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater_doFinish + (JNIEnv *, jobject, jlong, jint, jlong, jint); + +/* + * Class: net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater + * Method: doReset + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater_doReset + (JNIEnv *, jobject); + +/* + * Class: net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater + * Method: doDict + * Signature: (JI)V + */ +JNIEXPORT void JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater_doDict + (JNIEnv *, jobject, jlong, jint); + +#ifdef __cplusplus +} +#endif + +#endif //_Included_net_daporkchop_lib_compression_zlib_natives_NativeZlibDeflater diff --git a/compression/zlib/src/main/native/common/NativeZlibInflater.cpp b/compression/zlib/src/main/native/common/NativeZlibInflater.cpp new file mode 100644 index 000000000..ed736eb6d --- /dev/null +++ b/compression/zlib/src/main/native/common/NativeZlibInflater.cpp @@ -0,0 +1,218 @@ +#include +#include "NativeZlibInflater.h" + +#include + +static jfieldID ctxID; +static jfieldID readBytesID; +static jfieldID writtenBytesID; +static jfieldID resetID; +static jfieldID finishedID; + +__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater_load + (JNIEnv* env, jclass cla) { + ctxID = env->GetFieldID(cla, "ctx", "J"); + readBytesID = env->GetFieldID(cla, "readBytes", "I"); + writtenBytesID = env->GetFieldID(cla, "writtenBytes", "I"); + resetID = env->GetFieldID(cla, "reset", "Z"); + finishedID = env->GetFieldID(cla, "finished", "Z"); +} + +__attribute__((visibility("default"))) jlong JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater_allocateCtx + (JNIEnv* env, jclass cla, jint mode) { + int windowBits; + switch (mode) { + case 0: //zlib + windowBits = 15; + break; + case 1: //gzip + windowBits = 15 + 16; + break; + case 2: //raw + windowBits = -15; + break; + case 3: //auto + windowBits = 15 + 32; + break; + default: + throwException(env, "Invalid inflater mode!", mode); + return 0; + } + + zng_stream* stream = (zng_stream*) new char[sizeof(zng_stream)](); + + int ret = zng_inflateInit2(stream, windowBits); + + if (ret != Z_OK) { + const char* msg = stream->msg; + delete stream; + throwException(env, msg == nullptr ? "Couldn't init inflater!" : msg, ret); + return 0; + } + + return (jlong) stream; +} + +__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater_releaseCtx + (JNIEnv* env, jclass cla, jlong ctx) { + zng_stream* stream = (zng_stream*) ctx; + + int ret = zng_inflateEnd(stream); + const char* msg = stream->msg; + delete stream; + + if (ret != Z_OK) { + throwException(env, msg == nullptr ? "Couldn't end inflater!" : msg, ret); + } +} + +__attribute__((visibility("default"))) jboolean JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater_doFullInflate + (JNIEnv* env, jobject obj, jlong srcAddr, jint srcSize, jlong dstAddr, jint dstSize, jlong dictAddr, jint dictSize) { + zng_stream* stream = (zng_stream*) env->GetLongField(obj, ctxID); + + //set stream buffers + stream->next_in = (unsigned char*) srcAddr; + stream->avail_in = srcSize; + + stream->next_out = (unsigned char*) dstAddr; + stream->avail_out = dstSize; + + int ret = zng_inflate(stream, Z_FINISH); + if (ret == Z_NEED_DICT) { + if (dictAddr) { + //set dictionary + ret = zng_inflateSetDictionary(stream, (unsigned char*) dictAddr, dictSize); + if (ret != Z_OK) { + throwException(env, stream->msg == nullptr ? "Couldn't set inflater dictionary!" : stream->msg, ret); + return false; + } + + //try again + ret = zng_inflate(stream, Z_FINISH); + } else { + throwException(env, "Dictionary needed, but none was given!", ret); + return false; + } + } + + if (ret == Z_STREAM_END) { + env->SetIntField(obj, readBytesID, srcSize - stream->avail_in); + env->SetIntField(obj, writtenBytesID, dstSize - stream->avail_out); + return true; + } else if (ret != Z_OK) { + throwException(env, stream->msg == nullptr ? "Invalid return value from inflate()!" : stream->msg, ret); + } + + return false; +} + +__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater_doUpdate + (JNIEnv* env, jobject obj, jlong srcAddr, jint srcSize, jlong dstAddr, jint dstSize, jlong dictAddr, jint dictSize, jboolean flush) { + zng_stream* stream = (zng_stream*) env->GetLongField(obj, ctxID); + + //set stream buffers + stream->next_in = (unsigned char*) srcAddr; + stream->avail_in = srcSize; + + stream->next_out = (unsigned char*) dstAddr; + stream->avail_out = dstSize; + + int ret = zng_inflate(stream, flush ? Z_SYNC_FLUSH : Z_NO_FLUSH); + if (ret == Z_NEED_DICT) { + if (dictAddr) { + //set dictionary + ret = zng_inflateSetDictionary(stream, (unsigned char*) dictAddr, dictSize); + if (ret != Z_OK) { + throwException(env, stream->msg == nullptr ? "Couldn't set inflater dictionary!" : stream->msg, ret); + return; + } + + //try again + ret = zng_inflate(stream, flush ? Z_SYNC_FLUSH : Z_NO_FLUSH); + } else { + printf("not setting dict\n"); + throwException(env, "Dictionary needed, but none was given!", ret); + return; + } + } + + if (ret == Z_STREAM_END) { + env->SetBooleanField(obj, finishedID, true); + } else if (ret != Z_OK) { + throwException(env, stream->msg == nullptr ? "Invalid return value from inflate()!" : stream->msg, ret); + return; + } + + env->SetIntField(obj, readBytesID, srcSize - stream->avail_in); + env->SetIntField(obj, writtenBytesID, dstSize - stream->avail_out); +} + +__attribute__((visibility("default"))) jboolean JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater_doFinish + (JNIEnv* env, jobject obj, jlong srcAddr, jint srcSize, jlong dstAddr, jint dstSize, jlong dictAddr, jint dictSize) { + zng_stream* stream = (zng_stream*) env->GetLongField(obj, ctxID); + + //set stream buffers + stream->next_in = (unsigned char*) srcAddr; + stream->avail_in = srcSize; + + stream->next_out = (unsigned char*) dstAddr; + stream->avail_out = dstSize; + + int ret = zng_inflate(stream, Z_FINISH); + if (ret == Z_NEED_DICT) { + if (dictAddr) { + //set dictionary + ret = zng_inflateSetDictionary(stream, (unsigned char*) dictAddr, dictSize); + if (ret != Z_OK) { + throwException(env, stream->msg == nullptr ? "Couldn't set inflater dictionary!" : stream->msg, ret); + return false; + } + + //try again + ret = zng_inflate(stream, Z_FINISH); + } else { + throwException(env, "Dictionary needed, but none was given!", ret); + return false; + } + } + + if (ret != Z_STREAM_END && ret != Z_OK && ret != Z_BUF_ERROR) { + throwException(env, stream->msg == nullptr ? "Invalid return value from inflate()!" : stream->msg, ret); + return false; + } + + env->SetIntField(obj, readBytesID, srcSize - stream->avail_in); + env->SetIntField(obj, writtenBytesID, dstSize - stream->avail_out); + + if (ret == Z_STREAM_END) { + env->SetBooleanField(obj, finishedID, true); + return true; + } else { + return false; + } +} + +__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater_doReset + (JNIEnv* env, jobject obj) { + zng_stream* stream = (zng_stream*) env->GetLongField(obj, ctxID); + int ret = zng_inflateReset(stream); + + if (ret != Z_OK) { + throwException(env, stream->msg == nullptr ? "Couldn't reset deflater!" : stream->msg, ret); + return; + } + + env->SetBooleanField(obj, resetID, true); +} + +__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater_doDict + (JNIEnv* env, jobject obj, jlong dictAddr, jint dictSize) { + zng_stream* stream = (zng_stream*) env->GetLongField(obj, ctxID); + int ret = zng_inflateSetDictionary(stream, (unsigned char*) dictAddr, dictSize); + + if (ret != Z_OK) { + throwException(env, stream->msg == nullptr ? "Couldn't set inflater dictionary!" : stream->msg, ret); + return; + } +} + diff --git a/compression/zlib/src/main/native/common/NativeZlibInflater.h b/compression/zlib/src/main/native/common/NativeZlibInflater.h new file mode 100644 index 000000000..9a251fd39 --- /dev/null +++ b/compression/zlib/src/main/native/common/NativeZlibInflater.h @@ -0,0 +1,73 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +//actually it's not, it was initially though +//easier to make this by hand lol +#include + +#ifndef _Included_net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater +#define _Included_net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Class: net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater + * Method: load + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater_load + (JNIEnv *, jclass); + +/* + * Class: net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater + * Method: allocateCtx + * Signature: (I)J + */ +JNIEXPORT jlong JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater_allocateCtx + (JNIEnv *, jclass, jint); + +/* + * Class: net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater + * Method: releaseCtx + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater_releaseCtx + (JNIEnv *, jclass, jlong); + +/* + * Class: net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater + * Method: doFullInflate + * Signature: (JIJIJI)Z + */ +JNIEXPORT jboolean JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater_doFullInflate + (JNIEnv *, jobject, jlong, jint, jlong, jint, jlong, jint); + +/* + * Class: net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater + * Method: doUpdate + * Signature: (JIJIJIZ)V + */ +JNIEXPORT void JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater_doUpdate + (JNIEnv *, jobject, jlong, jint, jlong, jint, jlong, jint, jboolean); + +/* + * Class: net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater + * Method: doFinish + * Signature: (JIJIJI)Z + */ +JNIEXPORT jboolean JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater_doFinish + (JNIEnv *, jobject, jlong, jint, jlong, jint, jlong, jint); + +/* + * Class: net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater + * Method: doReset + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater_doReset + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif + +#endif //_Included_net_daporkchop_lib_compression_zlib_natives_NativeZlibInflater diff --git a/compression/zstd/Makefile b/compression/zstd/Makefile new file mode 100644 index 000000000..dae8f2b37 --- /dev/null +++ b/compression/zstd/Makefile @@ -0,0 +1,54 @@ +ifneq ($(BUILD),$(notdir $(CURDIR))) +.PHONY: $(BUILD) clean + +export OUTDIR := $(CURDIR)/src/main/resources +BUILDDIR := build/native/$(BUILD_TYPE)/$(BUILD) + +clean: + @if [ -d build/native/ ]; then rm -rf build/native/; fi + @rm -rf $(foreach arch,$(ARCHS),$(OUTDIR)/$(arch)) + +$(BUILD): + @[ -d $(BUILDDIR) ] || mkdir -p $(BUILDDIR) + @$(MAKE) --no-print-directory -C $(BUILDDIR) -f $(CURDIR)/Makefile BUILD=$(BUILD) build + +else +.PHONY: build + +include $(TOOLCHAINS)/$(BUILD) + +SOURCES := $(PROJDIR)/src/main/native/common $(PROJDIR)/src/main/native/$(BUILD) $(COMMONSRC)/common $(COMMONSRC)/$(BUILD) +SOURCES := $(abspath $(SOURCES)) + +CFILES := $(foreach dir,$(SOURCES),$(wildcard $(dir)/*.c)) +CPPFILES := $(foreach dir,$(SOURCES),$(wildcard $(dir)/*.cpp)) +OFILES := $(addsuffix .o,$(subst /,__,$(CFILES)) $(subst /,__,$(CPPFILES))) + +INCLUDE := $(addprefix -I,$(INCLUDES) $(CURDIR)) + +build: libzstd.$(EXT) + @echo "Copying libzstd.$(EXT) to $(OUTDIR)/$(BUILD)/..." + @[ -d $(OUTDIR)/$(BUILD) ] || mkdir -p $(OUTDIR)/$(BUILD) + @cp libzstd.$(EXT) $(OUTDIR)/$(BUILD)/libzstd.$(EXT) + +libzstd.$(EXT): $(CFILES) $(CPPFILES) $(OFILES) $(CURDIR)/lib-zstd/lib/libzstd.a + @echo "Linking $@..." + @$(LD) $(LDFLAGS) $(INCLUDE) -o $@ $(OFILES) $(CURDIR)/lib-zstd/lib/libzstd.a + @echo "Stripping $@..." + @$(STRIP) $@ + +%.c.o: $(CFILES) $(CURDIR)/lib-zstd/lib/libzstd.a + @echo "Building $(subst .o,,$(subst __,/,$@))..." + @$(CC) $(CFLAGS) $(INCLUDE) -c $(subst .o,,$(subst __,/,$@)) -o $@ + +%.cpp.o: $(CPPFILES) $(CURDIR)/lib-zstd/lib/libzstd.a + @echo "Building $(subst .o,,$(subst __,/,$@))..." + $(CXX) $(CXXFLAGS) $(INCLUDE) -c $(subst .o,,$(subst __,/,$@)) -o $@ + +$(CURDIR)/lib-zstd/lib/libzstd.a: $(TOPDIR)/zstd-1.4.4.tar.gz + @[ ! -d lib-zstd ] || rm -rf lib-zstd/ + @tar zxf $(TOPDIR)/zstd-1.4.4.tar.gz + @mv zstd-1.4.4/ lib-zstd/ + @$(MAKE) -C $(CURDIR)/lib-zstd/ -f $(CURDIR)/lib-zstd/Makefile ZSTD_LEGACY_SUPPORT=0 ZSTD_LEGACY_MULTITHREADED_API=0 lib-release + +endif diff --git a/compression/zstd/build.gradle b/compression/zstd/build.gradle new file mode 100644 index 000000000..6d64c9271 --- /dev/null +++ b/compression/zstd/build.gradle @@ -0,0 +1,21 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +dependencies { + compile project(":compression") + compile project(":natives") + + exampleCompile project(":binary") +} diff --git a/compression/zstd/src/example/java/DictionaryTest.java b/compression/zstd/src/example/java/DictionaryTest.java new file mode 100644 index 000000000..bbe9c4900 --- /dev/null +++ b/compression/zstd/src/example/java/DictionaryTest.java @@ -0,0 +1,101 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.PooledByteBufAllocator; +import net.daporkchop.lib.binary.netty.PUnpooled; +import net.daporkchop.lib.common.misc.file.PFiles; +import net.daporkchop.lib.compression.zstd.Zstd; +import net.daporkchop.lib.compression.zstd.ZstdCCtx; +import net.daporkchop.lib.compression.zstd.ZstdCDict; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.file.StandardOpenOption; + +/** + * @author DaPorkchop_ + */ +public class DictionaryTest { + public static final File RAW_DIR = new File("/home/daporkchop/Desktop/raw"); + public static final File OUT_DIR_NORMAL = new File("/home/daporkchop/Desktop/compressed-normal"); + public static final File OUT_DIR_DICT = new File("/home/daporkchop/Desktop/compressed-dict"); + public static final File DICT_FILE = new File("/home/daporkchop/Desktop/dict"); + + public static void main(String... args) throws IOException { + PFiles.rm(OUT_DIR_NORMAL); + PFiles.rm(OUT_DIR_DICT); + + ByteBuf rawDict; + try (FileChannel channel = FileChannel.open(DICT_FILE.toPath(), StandardOpenOption.READ)) { + rawDict = PUnpooled.wrap(channel.map(FileChannel.MapMode.READ_ONLY, 0L, channel.size()), true); + } + + long normalSize = 0L; + long dictSize = 0L; + + try (ZstdCCtx cCtx = Zstd.PROVIDER.compressionContext(); + ZstdCDict cDict = Zstd.PROVIDER.compressionDictionary(rawDict, Zstd.LEVEL_MAX)) { + for (File srcFile : RAW_DIR.listFiles()) { + ByteBuf src; + try (FileChannel channel = FileChannel.open(srcFile.toPath(), StandardOpenOption.READ)) { + src = PUnpooled.wrap(channel.map(FileChannel.MapMode.READ_ONLY, 0L, channel.size()), true); + } + ByteBuf compressed = PooledByteBufAllocator.DEFAULT.directBuffer(Zstd.PROVIDER.compressBound(src.readableBytes()), Zstd.PROVIDER.compressBound(src.readableBytes())); + try { + if (!cCtx.compress(src, compressed, Zstd.LEVEL_MAX)) { + throw new IllegalStateException(); + } + normalSize += compressed.readableBytes(); + if (true) { + continue; + } + try (FileChannel channel = FileChannel.open(PFiles.ensureFileExists(new File(OUT_DIR_NORMAL, srcFile.getName())).toPath(), StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) { + compressed.readBytes(channel, compressed.readableBytes()); + } + } finally { + src.release(); + compressed.release(); + } + } + + for (File srcFile : RAW_DIR.listFiles()) { + ByteBuf src; + try (FileChannel channel = FileChannel.open(srcFile.toPath(), StandardOpenOption.READ)) { + src = PUnpooled.wrap(channel.map(FileChannel.MapMode.READ_ONLY, 0L, channel.size()), true); + } + ByteBuf compressed = PooledByteBufAllocator.DEFAULT.directBuffer(Zstd.PROVIDER.compressBound(src.readableBytes()), Zstd.PROVIDER.compressBound(src.readableBytes())); + try { + if (!cCtx.compress(src, compressed, cDict)) { + throw new IllegalStateException(); + } + dictSize += compressed.readableBytes(); + if (true) { + continue; + } + try (FileChannel channel = FileChannel.open(PFiles.ensureFileExists(new File(OUT_DIR_DICT, srcFile.getName())).toPath(), StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) { + compressed.readBytes(channel, compressed.readableBytes()); + } + } finally { + src.release(); + compressed.release(); + } + } + } + + System.out.printf("Normal: %.2f\nDictionary: %.2f\n", normalSize / (1024.0d * 1024.0d), dictSize / (1024.0d * 1024.0d)); + } +} diff --git a/compression/zstd/src/example/java/ZstdTest.java b/compression/zstd/src/example/java/ZstdTest.java new file mode 100644 index 000000000..6fe9a15e7 --- /dev/null +++ b/compression/zstd/src/example/java/ZstdTest.java @@ -0,0 +1,117 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.NonNull; +import net.daporkchop.lib.common.util.PValidation; +import net.daporkchop.lib.compression.zstd.Zstd; +import net.daporkchop.lib.compression.zstd.ZstdCCtx; +import net.daporkchop.lib.compression.zstd.ZstdDCtx; + +import java.util.concurrent.ThreadLocalRandom; + +/** + * @author DaPorkchop_ + */ +public class ZstdTest { + private static final int SIZE = 1 << 26; // 64 MiB + + private static final int BOUND_SIZE = PValidation.toPositiveIntSafe(Zstd.PROVIDER.compressBound(SIZE)); + + public static void main(String... args) { + System.out.printf("original: %d, worst-case compressed: %d\n", 1 << 16L, Zstd.PROVIDER.compressBound(1 << 16L)); + + ByteBuf original = Unpooled.directBuffer(SIZE, SIZE).clear().ensureWritable(SIZE).writerIndex(SIZE).markWriterIndex(); + for (int i = 0; i < SIZE; i++) { + original.setByte(i, i & 0xFF); + } + for (int i = 0; i < 256; i += 8) { + original.setLongLE(i, ThreadLocalRandom.current().nextLong()); + } + + ByteBuf compressed = Unpooled.directBuffer(BOUND_SIZE, BOUND_SIZE); + + if (false) { //benchmark simple vs context compression + long start = System.currentTimeMillis(); + for (int i = 0; i < 256; i++) { + if (!Zstd.PROVIDER.compress(original.clear().writerIndex(SIZE), compressed.clear(), Zstd.LEVEL_MAX)) { + throw new IllegalStateException(); + } + } + System.out.printf("simple: %.2fs\n", (System.currentTimeMillis() - start) / 1000.0d); + + start = System.currentTimeMillis(); + try (ZstdCCtx ctx = Zstd.PROVIDER.compressionContext()) { + for (int i = 0; i < 256; i++) { + if (!ctx.compress(original.clear().writerIndex(SIZE), compressed.clear(), Zstd.LEVEL_MAX)) { + throw new IllegalStateException(); + } + } + } + System.out.printf("context: %.2fs\n", (System.currentTimeMillis() - start) / 1000.0d); + } else if (true) { //benchmark simple vs context decompression + Zstd.PROVIDER.compress(original.clear().writerIndex(SIZE), compressed.clear(), Zstd.LEVEL_MAX); + compressed.markWriterIndex(); + + int uncompressedSize = Zstd.PROVIDER.frameContentSize(compressed); + ByteBuf uncompressed = Unpooled.directBuffer(uncompressedSize, uncompressedSize); + + long start = System.currentTimeMillis(); + for (int i = 0; i < 256; i++) { + if (!Zstd.PROVIDER.decompress(compressed.clear().resetWriterIndex(), uncompressed.clear())) { + throw new IllegalStateException(); + } + } + System.out.printf("simple: %.2fs\n", (System.currentTimeMillis() - start) / 1000.0d); + + start = System.currentTimeMillis(); + try (ZstdDCtx ctx = Zstd.PROVIDER.decompressionContext()) { + for (int i = 0; i < 256; i++) { + if (!ctx.decompress(compressed.clear().resetWriterIndex(), uncompressed.clear())) { + throw new IllegalStateException(); + } + } + } + System.out.printf("context: %.2fs\n", (System.currentTimeMillis() - start) / 1000.0d); + + try (ZstdDCtx ctx = Zstd.PROVIDER.decompressionContext()) { + if (!ctx.decompress(compressed.clear().resetWriterIndex(), uncompressed.clear())) { + throw new IllegalStateException(); + } + } + validateEqual(original, uncompressed, 0, SIZE); + } else if (false) { + Zstd.PROVIDER.compress(original.slice(), compressed, Zstd.LEVEL_DEFAULT); + System.out.printf("original: %d, compressed: %d\n", original.readableBytes(), compressed.readableBytes()); + + int uncompressedSize = PValidation.toPositiveIntSafe(Zstd.PROVIDER.frameContentSizeLong(compressed)); + System.out.printf("original size: %d, frame content size: %d\n", SIZE, uncompressedSize); + ByteBuf uncompressed = Unpooled.directBuffer(uncompressedSize, uncompressedSize); + + Zstd.PROVIDER.decompress(compressed, uncompressed); + + validateEqual(original, uncompressed, 0, SIZE); + } + } + + private static void validateEqual(@NonNull ByteBuf a, @NonNull ByteBuf b, int start, int size) { + for (int i = 0; i < size; i++) { + if (a.getByte(start + i) != b.getByte(start + i)) { + throw new IllegalStateException(); + } + } + } +} diff --git a/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/Zstd.java b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/Zstd.java new file mode 100644 index 000000000..0c0dacc68 --- /dev/null +++ b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/Zstd.java @@ -0,0 +1,34 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zstd; + +import lombok.experimental.UtilityClass; +import net.daporkchop.lib.compression.zstd.natives.NativeZstd; +import net.daporkchop.lib.natives.FeatureBuilder; + +/** + * @author DaPorkchop_ + */ +@UtilityClass +public class Zstd { + public final ZstdProvider PROVIDER = FeatureBuilder.create(Zstd.class) + .addNative("net.daporkchop.lib.compression.zstd.natives.NativeZstd", "zstd") + .build(); + + public final int LEVEL_DEFAULT = 3; + public final int LEVEL_MIN = -999; + public final int LEVEL_MAX = 22; +} diff --git a/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/ZstdCCtx.java b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/ZstdCCtx.java new file mode 100644 index 000000000..5ed6c8a00 --- /dev/null +++ b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/ZstdCCtx.java @@ -0,0 +1,88 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zstd; + +import io.netty.buffer.ByteBuf; +import lombok.NonNull; +import net.daporkchop.lib.compression.CCtx; +import net.daporkchop.lib.compression.util.exception.DictionaryNotAllowedException; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; + +/** + * Compression context for {@link Zstd}. + *

+ * Not thread-safe. + * + * @author DaPorkchop_ + */ +public interface ZstdCCtx extends CCtx { + @Override + ZstdProvider provider(); + + /** + * Compresses the given source data into the given destination buffer at the configured Zstd level. + * + * @see CCtx#compress(ByteBuf, ByteBuf) + */ + @Override + default boolean compress(@NonNull ByteBuf src, @NonNull ByteBuf dst) throws InvalidBufferTypeException { + return this.compress(src, dst, this.level()); + } + + /** + * Compresses the given source data into a single Zstd frame into the given destination buffer at the given Zstd level. + *

+ * This is possible because Zstd allows using the same context for any compression level without having to reallocate it. + * + * @see #compress(ByteBuf, ByteBuf) + */ + boolean compress(@NonNull ByteBuf src, @NonNull ByteBuf dst, int compressionLevel) throws InvalidBufferTypeException; + + /** + * Compresses the given source data into the given destination buffer at the configured Zstd level using the given dictionary. + * + * @see CCtx#compress(ByteBuf, ByteBuf, ByteBuf) + */ + @Override + default boolean compress(@NonNull ByteBuf src, @NonNull ByteBuf dst, ByteBuf dict) throws InvalidBufferTypeException, DictionaryNotAllowedException { + return this.compress(src, dst, dict, this.level()); + } + + /** + * Compresses the given source data into a single Zstd frame into the given destination buffer at the given Zstd level using the given dictionary. + *

+ * This is possible because Zstd allows using the same context for any compression level without having to reallocate it. + * + * @see #compress(ByteBuf, ByteBuf, int) + * @see #compress(ByteBuf, ByteBuf, ByteBuf) + */ + boolean compress(@NonNull ByteBuf src, @NonNull ByteBuf dst, ByteBuf dict, int compressionLevel) throws InvalidBufferTypeException; + + /** + * Compresses the given source data into a single Zstd frame into the given destination buffer using the given dictionary. + *

+ * As the dictionary has already been digested, this is far faster than the other dictionary compression methods. + * + * @param dictionary the dictionary to use + * @see #compress(ByteBuf, ByteBuf) + */ + boolean compress(@NonNull ByteBuf src, @NonNull ByteBuf dst, @NonNull ZstdCDict dictionary) throws InvalidBufferTypeException; + + @Override + default boolean hasDict() { + return true; + } +} diff --git a/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/ZstdCDict.java b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/ZstdCDict.java new file mode 100644 index 000000000..87a20609f --- /dev/null +++ b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/ZstdCDict.java @@ -0,0 +1,42 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zstd; + +import io.netty.util.ReferenceCounted; +import net.daporkchop.lib.common.misc.refcount.RefCounted; +import net.daporkchop.lib.compression.CompressionProvider; +import net.daporkchop.lib.unsafe.capability.Releasable; +import net.daporkchop.lib.unsafe.util.exception.AlreadyReleasedException; + +/** + * A digested dictionary used by {@link Zstd} compression. + * + * @author DaPorkchop_ + */ +public interface ZstdCDict extends RefCounted { + /** + * @return the {@link CompressionProvider} that created this context + */ + ZstdProvider provider(); + + /** + * @return the compression level that the dictionary will use + */ + int level(); + + @Override + ZstdCDict retain() throws AlreadyReleasedException; +} diff --git a/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/ZstdDCtx.java b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/ZstdDCtx.java new file mode 100644 index 000000000..9ed90baba --- /dev/null +++ b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/ZstdDCtx.java @@ -0,0 +1,48 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zstd; + +import io.netty.buffer.ByteBuf; +import lombok.NonNull; +import net.daporkchop.lib.compression.DCtx; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; + +/** + * Deompression context for {@link Zstd}. + *

+ * Not thread-safe. + * + * @author DaPorkchop_ + */ +public interface ZstdDCtx extends DCtx { + @Override + ZstdProvider provider(); + + /** + * Decompresses the given compressed data into the given destination buffer using the given dictionary. + *

+ * As the dictionary has already been digested, this is far faster than {@link #decompress(ByteBuf, ByteBuf, ByteBuf)}. + * + * @param dictionary the dictionary to use + * @see #decompress(ByteBuf, ByteBuf) + */ + boolean decompress(@NonNull ByteBuf src, @NonNull ByteBuf dst, @NonNull ZstdDDict dictionary) throws InvalidBufferTypeException; + + @Override + default boolean hasDict() { + return true; + } +} diff --git a/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/ZstdDDict.java b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/ZstdDDict.java new file mode 100644 index 000000000..299ab4e5f --- /dev/null +++ b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/ZstdDDict.java @@ -0,0 +1,35 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zstd; + +import net.daporkchop.lib.common.misc.refcount.RefCounted; +import net.daporkchop.lib.compression.CompressionProvider; +import net.daporkchop.lib.unsafe.util.exception.AlreadyReleasedException; + +/** + * A digested dictionary used by {@link Zstd} decompression. + * + * @author DaPorkchop_ + */ +public interface ZstdDDict extends RefCounted { + /** + * @return the {@link CompressionProvider} that created this context + */ + ZstdProvider provider(); + + @Override + ZstdDDict retain() throws AlreadyReleasedException; +} diff --git a/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/ZstdProvider.java b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/ZstdProvider.java new file mode 100644 index 000000000..d01275fb1 --- /dev/null +++ b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/ZstdProvider.java @@ -0,0 +1,124 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zstd; + +import io.netty.buffer.ByteBuf; +import lombok.NonNull; +import net.daporkchop.lib.common.util.PValidation; +import net.daporkchop.lib.common.util.exception.ValueCannotFitException; +import net.daporkchop.lib.compression.CompressionProvider; +import net.daporkchop.lib.compression.OneShotCompressionProvider; +import net.daporkchop.lib.compression.util.exception.InvalidCompressionLevelException; +import net.daporkchop.lib.compression.zstd.util.exception.ContentSizeUnknownException; +import net.daporkchop.lib.natives.impl.Feature; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; + +/** + * Representation of a Zstd implementation. + * + * @author DaPorkchop_ + */ +public interface ZstdProvider extends OneShotCompressionProvider, Feature { + /** + * Compresses the given source data into a single Zstd frame into the given destination buffer at the default compression level. + * + * @see #compress(ByteBuf, ByteBuf, int) + */ + default boolean compress(@NonNull ByteBuf src, @NonNull ByteBuf dst) throws InvalidBufferTypeException { + return this.compress(src, dst, Zstd.LEVEL_DEFAULT); + } + + /** + * Compresses the given source data into a single Zstd frame into the given destination buffer. + *

+ * If the destination buffer does not have enough space writable for the compressed data, the operation will fail and both buffer's indices will remain + * unchanged, however the destination buffer's contents may be modified. + * + * @param src the {@link ByteBuf} to read source data from + * @param dst the {@link ByteBuf} to write compressed data to + * @param compressionLevel the Zstd level to compress at + * @return whether or not compression was successful. If {@code false}, the destination buffer was too small for the compressed data + */ + boolean compress(@NonNull ByteBuf src, @NonNull ByteBuf dst, int compressionLevel) throws InvalidBufferTypeException; + + /** + * Decompresses the given Zstd-compressed into the given destination buffer. + *

+ * If the destination buffer does not have enough space writable for the decompressed data, the operation will fail and both buffer's indices will remain + * unchanged, however the destination buffer's contents may be modified. + * + * @param src the {@link ByteBuf} to read compressed data from + * @param dst the {@link ByteBuf} to write decompressed data to + * @return whether or not decompression was successful. If {@code false}, the destination buffer was too small for the decompressed data + */ + boolean decompress(@NonNull ByteBuf src, @NonNull ByteBuf dst) throws InvalidBufferTypeException; + + /** + * @throws ValueCannotFitException if the returned value is too large to fit in an {@code int} + * @see #frameContentSizeLong(ByteBuf) + */ + default int frameContentSize(@NonNull ByteBuf src) throws InvalidBufferTypeException, ContentSizeUnknownException, ValueCannotFitException { + return PValidation.toInt(this.frameContentSizeLong(src)); + } + + /** + * Gets the decompressed size of the given Zstd-compressed data. + * + * @param src the {@link ByteBuf} containing the compressed data. This {@link ByteBuf}'s indices will not be modified by this method + * @return the size (in bytes) of the decompressed data + * @throws ContentSizeUnknownException if the decompressed size cannot be determined + */ + long frameContentSizeLong(@NonNull ByteBuf src) throws InvalidBufferTypeException, ContentSizeUnknownException; + + @Override + default ZstdCCtx compressionContext() { + return this.compressionContext(Zstd.LEVEL_DEFAULT); + } + + @Override + ZstdCCtx compressionContext(int level) throws InvalidCompressionLevelException; + + @Override + ZstdDCtx decompressionContext(); + + /** + * Digests a Zstd dictionary for compression at the default level. + * + * @param dict the {@link ByteBuf} containing the dictionary + * @return the digested dictionary + */ + default ZstdCDict compressionDictionary(@NonNull ByteBuf dict) throws InvalidBufferTypeException { + return this.compressionDictionary(dict, Zstd.LEVEL_DEFAULT); + } + + /** + * Digests a Zstd dictionary for compression at the given level. + * + * @param dict the {@link ByteBuf} containing the dictionary + * @param level the compression level to use + * @return the digested dictionary + */ + ZstdCDict compressionDictionary(@NonNull ByteBuf dict, int level) throws InvalidBufferTypeException; + + /** + * Digests a Zstd dictionary for decompression. + * + * @param dict the {@link ByteBuf} containing the dictionary + * @return the digested dictionary + */ + ZstdDDict decompressionDictionary(@NonNull ByteBuf dict) throws InvalidBufferTypeException; + +} diff --git a/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/natives/NativeZstd.java b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/natives/NativeZstd.java new file mode 100644 index 000000000..293332c28 --- /dev/null +++ b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/natives/NativeZstd.java @@ -0,0 +1,117 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zstd.natives; + +import io.netty.buffer.ByteBuf; +import lombok.NonNull; +import net.daporkchop.lib.common.util.PValidation; +import net.daporkchop.lib.compression.PDeflater; +import net.daporkchop.lib.compression.PInflater; +import net.daporkchop.lib.compression.util.exception.InvalidCompressionLevelException; +import net.daporkchop.lib.compression.zstd.Zstd; +import net.daporkchop.lib.compression.zstd.ZstdCCtx; +import net.daporkchop.lib.compression.zstd.ZstdCDict; +import net.daporkchop.lib.compression.zstd.ZstdDCtx; +import net.daporkchop.lib.compression.zstd.ZstdDDict; +import net.daporkchop.lib.compression.zstd.ZstdProvider; +import net.daporkchop.lib.compression.zstd.util.exception.ContentSizeUnknownException; +import net.daporkchop.lib.natives.impl.NativeFeature; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; + +/** + * @author DaPorkchop_ + */ +public final class NativeZstd extends NativeFeature implements ZstdProvider { + @Override + public boolean directAccepted() { + return true; + } + + @Override + public int levelFast() { + return Zstd.LEVEL_MIN; + } + + @Override + public int levelDefault() { + return Zstd.LEVEL_DEFAULT; + } + + @Override + public int levelBest() { + return Zstd.LEVEL_MAX; + } + + @Override + public boolean compress(@NonNull ByteBuf src, @NonNull ByteBuf dst, int compressionLevel) throws InvalidBufferTypeException { + int val = this.doCompress(this.assertAcceptable(src).memoryAddress() + src.readerIndex(), src.readableBytes(), + this.assertAcceptable(dst).memoryAddress() + dst.writerIndex(), dst.writableBytes(), + compressionLevel); + + return NativeZstdHelper.finalizeOneShot(src, dst, val); + } + + private native int doCompress(long srcAddr, int srcSize, long dstAddr, int dstSize, int compressionLevel); + + @Override + public boolean decompress(@NonNull ByteBuf src, @NonNull ByteBuf dst) throws InvalidBufferTypeException { + int val = this.doDecompress(this.assertAcceptable(src).memoryAddress() + src.readerIndex(), src.readableBytes(), + this.assertAcceptable(dst).memoryAddress() + dst.writerIndex(), dst.writableBytes()); + + return NativeZstdHelper.finalizeOneShot(src, dst, val); + } + + private native int doDecompress(long srcAddr, int srcSize, long dstAddr, int dstSize); + + @Override + public long frameContentSizeLong(@NonNull ByteBuf src) throws InvalidBufferTypeException, ContentSizeUnknownException { + long size = this.doFrameContentSizeLong(this.assertAcceptable(src).memoryAddress() + src.readerIndex(), src.readableBytes()); + if (size >= 0L) { + return size; + } else { + throw new ContentSizeUnknownException(); + } + } + + private native long doFrameContentSizeLong(long srcAddr, int srcSize); + + @Override + public long compressBoundLong(long srcSize) { + return this.doCompressBoundLong(PValidation.ensureNonNegative(srcSize)); + } + + private native long doCompressBoundLong(long srcSize); + + @Override + public ZstdCCtx compressionContext(int level) throws InvalidCompressionLevelException { + return new NativeZstdCCtx(this, level); + } + + @Override + public ZstdDCtx decompressionContext() { + return new NativeZstdDCtx(this); + } + + @Override + public ZstdCDict compressionDictionary(@NonNull ByteBuf dict, int level) throws InvalidBufferTypeException { + return new NativeZstdCDict(this, dict, level, true); + } + + @Override + public ZstdDDict decompressionDictionary(@NonNull ByteBuf dict) throws InvalidBufferTypeException { + return new NativeZstdDDict(this, dict, true); + } +} diff --git a/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/natives/NativeZstdCCtx.java b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/natives/NativeZstdCCtx.java new file mode 100644 index 000000000..63122ae40 --- /dev/null +++ b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/natives/NativeZstdCCtx.java @@ -0,0 +1,124 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zstd.natives; + +import io.netty.buffer.ByteBuf; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import net.daporkchop.lib.compression.zstd.ZstdCCtx; +import net.daporkchop.lib.compression.zstd.ZstdCDict; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; +import net.daporkchop.lib.unsafe.PCleaner; +import net.daporkchop.lib.unsafe.util.AbstractReleasable; + +/** + * Native implementation of {@link ZstdCCtx}. + * + * @author DaPorkchop_ + */ +@Accessors(fluent = true) +final class NativeZstdCCtx extends AbstractReleasable implements ZstdCCtx { + private static native long allocateCtx(); + + private static native void releaseCtx(long ctx); + + private final long ctx = allocateCtx(); + private final PCleaner cleaner = PCleaner.cleaner(this, new Releaser(this.ctx)); + + @Getter + private final NativeZstd provider; + + @Getter + private int level; + + NativeZstdCCtx(@NonNull NativeZstd provider, int level) { + this.provider = provider; + this.level = level; + } + + @Override + public boolean compress(@NonNull ByteBuf src, @NonNull ByteBuf dst, int compressionLevel) throws InvalidBufferTypeException { + int val = this.doCompressNoDict(this.ctx, + this.assertAcceptable(src).memoryAddress() + src.readerIndex(), src.readableBytes(), + this.assertAcceptable(dst).memoryAddress() + dst.writerIndex(), dst.writableBytes(), + compressionLevel); + + return NativeZstdHelper.finalizeOneShot(src, dst, val); + } + + private native int doCompressNoDict(long ctx, long srcAddr, int srcSize, long dstAddr, int dstSize, int compressionLevel); + + @Override + public boolean compress(@NonNull ByteBuf src, @NonNull ByteBuf dst, ByteBuf dict, int compressionLevel) throws InvalidBufferTypeException { + if (dict == null) { + //compress without dictionary + return this.compress(src, dst, compressionLevel); + } + + int val = this.doCompressRawDict(this.ctx, + this.assertAcceptable(src).memoryAddress() + src.readerIndex(), src.readableBytes(), + this.assertAcceptable(dst).memoryAddress() + dst.writerIndex(), dst.writableBytes(), + this.assertAcceptable(dict).memoryAddress() + dict.readerIndex(), dict.readableBytes(), + compressionLevel); + + return NativeZstdHelper.finalizeOneShot(src, dst, val); + } + + private native int doCompressRawDict(long ctx, long srcAddr, int srcSize, long dstAddr, int dstSize, long dictAddr, int dictSize, int compressionLevel); + + @Override + public boolean compress(@NonNull ByteBuf src, @NonNull ByteBuf dst, @NonNull ZstdCDict dictionary) throws InvalidBufferTypeException { + if (!(dictionary instanceof NativeZstdCDict)) { + throw new IllegalArgumentException(dictionary.getClass().getCanonicalName()); + } + + dictionary.retain(); + try { + int val = this.doCompressCDict(this.ctx, + this.assertAcceptable(src).memoryAddress() + src.readerIndex(), src.readableBytes(), + this.assertAcceptable(dst).memoryAddress() + dst.writerIndex(), dst.writableBytes(), + ((NativeZstdCDict) dictionary).dict()); + + return NativeZstdHelper.finalizeOneShot(src, dst, val); + } finally { + dictionary.release(); + } + } + + private native int doCompressCDict(long ctx, long srcAddr, int srcSize, long dstAddr, int dstSize, long dictAddr); + + @Override + public boolean directAccepted() { + return true; + } + + @Override + protected void doRelease() { + this.cleaner.clean(); + } + + @RequiredArgsConstructor + private static final class Releaser implements Runnable { + private final long ctx; + + @Override + public void run() { + releaseCtx(this.ctx); + } + } +} diff --git a/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/natives/NativeZstdCDict.java b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/natives/NativeZstdCDict.java new file mode 100644 index 000000000..e99a72822 --- /dev/null +++ b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/natives/NativeZstdCDict.java @@ -0,0 +1,81 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zstd.natives; + +import io.netty.buffer.ByteBuf; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import net.daporkchop.lib.common.misc.refcount.AbstractRefCounted; +import net.daporkchop.lib.compression.zstd.ZstdCDict; +import net.daporkchop.lib.unsafe.PCleaner; +import net.daporkchop.lib.unsafe.util.exception.AlreadyReleasedException; + +/** + * @author DaPorkchop_ + */ +@Getter +@Accessors(fluent = true) +final class NativeZstdCDict extends AbstractRefCounted implements ZstdCDict { + private static native long createCDict(long dictAddr, int dictSize, int compressionLevel, boolean copy); + + private static native void releaseCDict(long dict); + + @Getter(AccessLevel.PACKAGE) + private final long dict; + + @Getter(AccessLevel.NONE) + private final PCleaner cleaner; + + private final NativeZstd provider; + private final int level; + + NativeZstdCDict(@NonNull NativeZstd provider, @NonNull ByteBuf dict, int level, boolean copy) { + this.provider = provider; + this.level = level; + + this.dict = createCDict(dict.memoryAddress() + dict.readerIndex(), dict.readableBytes(), level, copy); + + this.cleaner = PCleaner.cleaner(this, new Releaser(this.dict, copy ? dict : null)); + } + + @Override + public ZstdCDict retain() throws AlreadyReleasedException { + super.retain(); + return this; + } + + @Override + protected void doRelease() { + this.cleaner.clean(); + } + + @RequiredArgsConstructor + private static final class Releaser implements Runnable { + private final long dict; + private final ByteBuf data; + + @Override + public void run() { + releaseCDict(this.dict); + if (this.data != null) { + this.data.release(); + } + } + } +} diff --git a/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/natives/NativeZstdDCtx.java b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/natives/NativeZstdDCtx.java new file mode 100644 index 000000000..8fc343b07 --- /dev/null +++ b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/natives/NativeZstdDCtx.java @@ -0,0 +1,117 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zstd.natives; + +import io.netty.buffer.ByteBuf; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import net.daporkchop.lib.compression.zstd.ZstdCCtx; +import net.daporkchop.lib.compression.zstd.ZstdDCtx; +import net.daporkchop.lib.compression.zstd.ZstdDDict; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; +import net.daporkchop.lib.unsafe.PCleaner; +import net.daporkchop.lib.unsafe.util.AbstractReleasable; + +/** + * Native implementation of {@link ZstdCCtx}. + * + * @author DaPorkchop_ + */ +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +@Accessors(fluent = true) +final class NativeZstdDCtx extends AbstractReleasable implements ZstdDCtx { + private static native long allocateCtx(); + + private static native void releaseCtx(long ctx); + + private final long ctx = allocateCtx(); + private final PCleaner cleaner = PCleaner.cleaner(this, new Releaser(this.ctx)); + + @Getter + private final NativeZstd provider; + + @Override + public boolean decompress(@NonNull ByteBuf src, @NonNull ByteBuf dst) throws InvalidBufferTypeException { + int val = this.doDecompressNoDict(this.ctx, + this.assertAcceptable(src).memoryAddress() + src.readerIndex(), src.readableBytes(), + this.assertAcceptable(dst).memoryAddress() + dst.writerIndex(), dst.writableBytes()); + + return NativeZstdHelper.finalizeOneShot(src, dst, val); + } + + private native int doDecompressNoDict(long ctx, long srcAddr, int srcSize, long dstAddr, int dstSize); + + @Override + public boolean decompress(@NonNull ByteBuf src, @NonNull ByteBuf dst, ByteBuf dict) throws InvalidBufferTypeException { + if (dict == null) { + //decompress without dictionary + return this.decompress(src, dst); + } + + int val = this.doDecompressRawDict(this.ctx, + this.assertAcceptable(src).memoryAddress() + src.readerIndex(), src.readableBytes(), + this.assertAcceptable(dst).memoryAddress() + dst.writerIndex(), dst.writableBytes(), + this.assertAcceptable(dict).memoryAddress() + dict.readerIndex(), dict.readableBytes()); + + return NativeZstdHelper.finalizeOneShot(src, dst, val); + } + + private native int doDecompressRawDict(long ctx, long srcAddr, int srcSize, long dstAddr, int dstSize, long dictAddr, int dictSize); + + @Override + public boolean decompress(@NonNull ByteBuf src, @NonNull ByteBuf dst, @NonNull ZstdDDict dictionary) throws InvalidBufferTypeException { + if (!(dictionary instanceof NativeZstdDDict)) { + throw new IllegalArgumentException(dictionary.getClass().getCanonicalName()); + } + + dictionary.retain(); + try { + int val = this.doDecompressCDict(this.ctx, + this.assertAcceptable(src).memoryAddress() + src.readerIndex(), src.readableBytes(), + this.assertAcceptable(dst).memoryAddress() + dst.writerIndex(), dst.writableBytes(), + ((NativeZstdDDict) dictionary).dict()); + + return NativeZstdHelper.finalizeOneShot(src, dst, val); + } finally { + dictionary.release(); + } + } + + private native int doDecompressCDict(long ctx, long srcAddr, int srcSize, long dstAddr, int dstSize, long dictAddr); + + @Override + public boolean directAccepted() { + return true; + } + + @Override + protected void doRelease() { + this.cleaner.clean(); + } + + @RequiredArgsConstructor + private static final class Releaser implements Runnable { + private final long ctx; + + @Override + public void run() { + releaseCtx(this.ctx); + } + } +} diff --git a/natives/src/main/java/net/daporkchop/lib/natives/zlib/NativeDeflater.java b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/natives/NativeZstdDDict.java similarity index 52% rename from natives/src/main/java/net/daporkchop/lib/natives/zlib/NativeDeflater.java rename to compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/natives/NativeZstdDDict.java index a23bd59b8..7f1195997 100644 --- a/natives/src/main/java/net/daporkchop/lib/natives/zlib/NativeDeflater.java +++ b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/natives/NativeZstdDDict.java @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -13,60 +13,66 @@ * */ -package net.daporkchop.lib.natives.zlib; +package net.daporkchop.lib.compression.zstd.natives; +import io.netty.buffer.ByteBuf; import lombok.AccessLevel; import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; import lombok.experimental.Accessors; +import net.daporkchop.lib.common.misc.refcount.AbstractRefCounted; +import net.daporkchop.lib.compression.zstd.ZstdDDict; import net.daporkchop.lib.unsafe.PCleaner; import net.daporkchop.lib.unsafe.util.exception.AlreadyReleasedException; /** - * Implementation of {@link PDeflater} using native code. - * * @author DaPorkchop_ */ -@Getter @Accessors(fluent = true) -public final class NativeDeflater implements PDeflater { - static native void load(); - - private static native long init(int level, int mode); +final class NativeZstdDDict extends AbstractRefCounted implements ZstdDDict { + private static native long createDDict(long dictAddr, int dictSize, boolean copy); - private static native void end(long ctx); + private static native void releaseDDict(long dict); - @Getter(AccessLevel.NONE) - private final long ctx; + @Getter(AccessLevel.PACKAGE) + private final long dict; - @Getter(AccessLevel.NONE) private final PCleaner cleaner; - private int readBytes; - private int writtenBytes; + @Getter + private final NativeZstd provider; - private boolean finished; + NativeZstdDDict(@NonNull NativeZstd provider, @NonNull ByteBuf dict, boolean copy) { + this.provider = provider; - NativeDeflater(int level, int mode) { - long ctx = this.ctx = init(level, mode); - this.cleaner = PCleaner.cleaner(this, () -> end(ctx)); - } + this.dict = createDDict(dict.memoryAddress() + dict.readerIndex(), dict.readableBytes(), copy); - @Override - public native void input(long addr, int size); + this.cleaner = PCleaner.cleaner(this, new Releaser(this.dict, copy ? dict : null)); + } @Override - public native void output(long addr, int size); + public ZstdDDict retain() throws AlreadyReleasedException { + super.retain(); + return this; + } @Override - public native void deflate(boolean finish); + protected void doRelease() { + this.cleaner.clean(); + } - @Override - public native void reset(); + @RequiredArgsConstructor + private static final class Releaser implements Runnable { + private final long dict; + private final ByteBuf data; - @Override - public void release() throws AlreadyReleasedException { - if (!this.cleaner.tryClean()) { - throw new AlreadyReleasedException(); + @Override + public void run() { + releaseDDict(this.dict); + if (this.data != null) { + this.data.release(); + } } } } diff --git a/natives/src/main/java/net/daporkchop/lib/natives/PNatives.java b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/natives/NativeZstdHelper.java similarity index 73% rename from natives/src/main/java/net/daporkchop/lib/natives/PNatives.java rename to compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/natives/NativeZstdHelper.java index 98bf44cd4..6c6eb1d6b 100644 --- a/natives/src/main/java/net/daporkchop/lib/natives/PNatives.java +++ b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/natives/NativeZstdHelper.java @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -13,19 +13,25 @@ * */ -package net.daporkchop.lib.natives; +package net.daporkchop.lib.compression.zstd.natives; +import io.netty.buffer.ByteBuf; import lombok.experimental.UtilityClass; -import net.daporkchop.lib.natives.zlib.JavaZlib; -import net.daporkchop.lib.natives.zlib.NativeZlib; -import net.daporkchop.lib.natives.zlib.Zlib; /** - * Utility class, has static references to {@link NativeCode} for all implemented features. + * Helper methods used by various native Zstd implementations. * * @author DaPorkchop_ */ @UtilityClass -public class PNatives { - public static final NativeCode ZLIB = new NativeCode<>(NativeZlib::new, JavaZlib::new); +class NativeZstdHelper { + public static boolean finalizeOneShot(ByteBuf src, ByteBuf dst, int val) { + if (val >= 0) { + src.skipBytes(src.readableBytes()); + dst.writerIndex(dst.writerIndex() + val); + return true; + } else { + return false; + } + } } diff --git a/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/util/exception/ContentSizeUnknownException.java b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/util/exception/ContentSizeUnknownException.java new file mode 100644 index 000000000..dbbc8135c --- /dev/null +++ b/compression/zstd/src/main/java/net/daporkchop/lib/compression/zstd/util/exception/ContentSizeUnknownException.java @@ -0,0 +1,27 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.compression.zstd.util.exception; + +import io.netty.buffer.ByteBuf; +import net.daporkchop.lib.compression.zstd.ZstdProvider; + +/** + * Thrown when {@link ZstdProvider#frameContentSizeLong(ByteBuf)} cannot identify the content size of the given frame. + * + * @author DaPorkchop_ + */ +public final class ContentSizeUnknownException extends RuntimeException { +} diff --git a/compression/zstd/src/main/native/common/NativeZstd.cpp b/compression/zstd/src/main/native/common/NativeZstd.cpp new file mode 100644 index 000000000..f0781dfe4 --- /dev/null +++ b/compression/zstd/src/main/native/common/NativeZstd.cpp @@ -0,0 +1,54 @@ +#include +#include "NativeZstd.h" + +#include +#include + +__attribute__((visibility("default"))) jint JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstd_doCompress + (JNIEnv* env, jobject obj, jlong srcAddr, jint srcSize, jlong dstAddr, jint dstSize, jint compressionLevel) { + auto ret = ZSTD_compress((void*) dstAddr, dstSize, (void*) srcAddr, srcSize, compressionLevel); + + if (ZSTD_isError(ret)) { + if (ZSTD_getErrorCode(ret) == ZSTD_error_dstSize_tooSmall) { + return -1; + } else { + throwException(env, ZSTD_getErrorName(ret), (jlong) ret); + return 0; + } + } + + return (jint) ret; +} + +__attribute__((visibility("default"))) jint JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstd_doDecompress + (JNIEnv* env, jobject obj, jlong srcAddr, jint srcSize, jlong dstAddr, jint dstSize) { + auto ret = ZSTD_decompress((void*) dstAddr, dstSize, (void*) srcAddr, srcSize); + + if (ZSTD_isError(ret)) { + if (ZSTD_getErrorCode(ret) == ZSTD_error_dstSize_tooSmall) { + return -1; + } else { + throwException(env, ZSTD_getErrorName(ret), (jlong) ret); + return 0; + } + } + + return (jint) ret; +} + +__attribute__((visibility("default"))) jlong JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstd_doFrameContentSizeLong + (JNIEnv* env, jobject obj, jlong srcAddr, jint srcSize) { + auto contentSize = ZSTD_getFrameContentSize((void*) srcAddr, srcSize); + + if (contentSize == ZSTD_CONTENTSIZE_ERROR) { + throwException(env, "ZSTD_CONTENTSIZE_ERROR", (jlong) ZSTD_CONTENTSIZE_ERROR); + return 0; + } + + return contentSize; +} + +__attribute__((visibility("default"))) jlong JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstd_doCompressBoundLong + (JNIEnv* env, jobject obj, jlong srcSize) { + return ZSTD_compressBound(srcSize); +} diff --git a/compression/zstd/src/main/native/common/NativeZstd.h b/compression/zstd/src/main/native/common/NativeZstd.h new file mode 100644 index 000000000..93695a0ea --- /dev/null +++ b/compression/zstd/src/main/native/common/NativeZstd.h @@ -0,0 +1,49 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +//actually it's not, it was initially though +//easier to make this by hand lol +#include + +#ifndef _Included_net_daporkchop_lib_compression_zstd_natives_NativeZstd +#define _Included_net_daporkchop_lib_compression_zstd_natives_NativeZstd + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Class: net_daporkchop_lib_compression_zstd_natives_NativeZstd + * Method: doCompress + * Signature: (JIJII)I + */ +JNIEXPORT jint JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstd_doCompress + (JNIEnv *, jobject, jlong, jint, jlong, jint, jint); + +/* + * Class: net_daporkchop_lib_compression_zstd_natives_NativeZstd + * Method: doDecompress + * Signature: (JIJI)I + */ +JNIEXPORT jint JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstd_doDecompress + (JNIEnv *, jobject, jlong, jint, jlong, jint); + +/* + * Class: net_daporkchop_lib_compression_zstd_natives_NativeZstd + * Method: doFrameContentSize + * Signature: (JI)J + */ +JNIEXPORT jlong JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstd_doFrameContentSizeLong + (JNIEnv *, jobject, jlong, jint); + +/* + * Class: net_daporkchop_lib_compression_zstd_natives_NativeZstd + * Method: doCompressBound + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstd_doCompressBoundLong + (JNIEnv *, jobject, jlong); + +#ifdef __cplusplus +} +#endif + +#endif //_Included_net_daporkchop_lib_compression_zstd_natives_NativeZstd diff --git a/compression/zstd/src/main/native/common/NativeZstdCCtx.cpp b/compression/zstd/src/main/native/common/NativeZstdCCtx.cpp new file mode 100644 index 000000000..e4803516c --- /dev/null +++ b/compression/zstd/src/main/native/common/NativeZstdCCtx.cpp @@ -0,0 +1,68 @@ +#include +#include "NativeZstdCCtx.h" + +#include +#include + +__attribute__((visibility("default"))) jlong JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdCCtx_allocateCtx + (JNIEnv* env, jclass cla) { + return (jlong) ZSTD_createCCtx(); +} + +__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdCCtx_releaseCtx + (JNIEnv* env, jclass cla, jlong ctx) { + auto ret = ZSTD_freeCCtx((ZSTD_CCtx*) ctx); + + if (ZSTD_isError(ret)) { + throwException(env, ZSTD_getErrorName(ret), (jlong) ret); + return; + } +} + +__attribute__((visibility("default"))) jint JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdCCtx_doCompressNoDict + (JNIEnv* env, jobject obj, jlong ctx, jlong srcAddr, jint srcSize, jlong dstAddr, jint dstSize, jint compressionLevel) { + auto ret = ZSTD_compressCCtx((ZSTD_CCtx*) ctx, (void*) dstAddr, dstSize, (void*) srcAddr, srcSize, compressionLevel); + + if (ZSTD_isError(ret)) { + if (ZSTD_getErrorCode(ret) == ZSTD_error_dstSize_tooSmall) { + return -1; + } else { + throwException(env, ZSTD_getErrorName(ret), (jlong) ret); + return 0; + } + } + + return (jint) ret; +} + +__attribute__((visibility("default"))) jint JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdCCtx_doCompressRawDict + (JNIEnv* env, jobject obj, jlong ctx, jlong srcAddr, jint srcSize, jlong dstAddr, jint dstSize, jlong dictAddr, jint dictSize, jint compressionLevel) { + auto ret = ZSTD_compress_usingDict((ZSTD_CCtx*) ctx, (void*) dstAddr, dstSize, (void*) srcAddr, srcSize, (void*) dictAddr, dictSize, compressionLevel); + + if (ZSTD_isError(ret)) { + if (ZSTD_getErrorCode(ret) == ZSTD_error_dstSize_tooSmall) { + return -1; + } else { + throwException(env, ZSTD_getErrorName(ret), (jlong) ret); + return 0; + } + } + + return (jint) ret; +} + +__attribute__((visibility("default"))) jint JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdCCtx_doCompressCDict + (JNIEnv* env, jobject obj, jlong ctx, jlong srcAddr, jint srcSize, jlong dstAddr, jint dstSize, jlong dictAddr) { + auto ret = ZSTD_compress_usingCDict((ZSTD_CCtx*) ctx, (void*) dstAddr, dstSize, (void*) srcAddr, srcSize, (ZSTD_CDict*) dictAddr); + + if (ZSTD_isError(ret)) { + if (ZSTD_getErrorCode(ret) == ZSTD_error_dstSize_tooSmall) { + return -1; + } else { + throwException(env, ZSTD_getErrorName(ret), (jlong) ret); + return 0; + } + } + + return (jint) ret; +} diff --git a/compression/zstd/src/main/native/common/NativeZstdCCtx.h b/compression/zstd/src/main/native/common/NativeZstdCCtx.h new file mode 100644 index 000000000..c38c7c8a4 --- /dev/null +++ b/compression/zstd/src/main/native/common/NativeZstdCCtx.h @@ -0,0 +1,57 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +//actually it's not, it was initially though +//easier to make this by hand lol +#include + +#ifndef _Included_net_daporkchop_lib_compression_zstd_natives_NativeZstdCCtx +#define _Included_net_daporkchop_lib_compression_zstd_natives_NativeZstdCCtx + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Class: net_daporkchop_lib_compression_zstd_natives_NativeZstdCCtx + * Method: allocateCtx + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdCCtx_allocateCtx + (JNIEnv *, jclass); + +/* + * Class: net_daporkchop_lib_compression_zstd_natives_NativeZstdCCtx + * Method: releaseCtx + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdCCtx_releaseCtx + (JNIEnv *, jclass, jlong); + +/* + * Class: net_daporkchop_lib_compression_zstd_natives_NativeZstdCCtx + * Method: doCompressNoDict + * Signature: (JJIJII)I + */ +JNIEXPORT jint JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdCCtx_doCompressNoDict + (JNIEnv *, jobject, jlong, jlong, jint, jlong, jint, jint); + +/* + * Class: net_daporkchop_lib_compression_zstd_natives_NativeZstdCCtx + * Method: doCompressRawDict + * Signature: (JJIJIJII)I + */ +JNIEXPORT jint JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdCCtx_doCompressRawDict + (JNIEnv *, jobject, jlong, jlong, jint, jlong, jint, jlong, jint, jint); + +/* + * Class: net_daporkchop_lib_compression_zstd_natives_NativeZstdCCtx + * Method: doCompressCDict + * Signature: (JJIJIJII)I + */ +JNIEXPORT jint JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdCCtx_doCompressCDict + (JNIEnv *, jobject, jlong, jlong, jint, jlong, jint, jlong); + +#ifdef __cplusplus +} +#endif + +#endif //_Included_net_daporkchop_lib_compression_zstd_natives_NativeZstdCCtx diff --git a/compression/zstd/src/main/native/common/NativeZstdCDict.cpp b/compression/zstd/src/main/native/common/NativeZstdCDict.cpp new file mode 100644 index 000000000..1ded3b423 --- /dev/null +++ b/compression/zstd/src/main/native/common/NativeZstdCDict.cpp @@ -0,0 +1,22 @@ +#include +#include "NativeZstdCDict.h" + +#include +#include + +__attribute__((visibility("default"))) jlong JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdCDict_createCDict + (JNIEnv* env, jclass cla, jlong dictAddr, jint dictSize, jint compressionLevel, jboolean copy) { + return copy + ? (jlong) ZSTD_createCDict((void*) dictAddr, dictSize, compressionLevel) + : 0;//: (jlong) ZSTD_createCDict_byReference((void*) dictAddr, dictSize, compressionLevel); +} + +__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdCDict_releaseCDict + (JNIEnv* env, jclass cla, jlong ctx) { + auto ret = ZSTD_freeCDict((ZSTD_CDict*) ctx); + + if (ZSTD_isError(ret)) { + throwException(env, ZSTD_getErrorName(ret), (jlong) ret); + return; + } +} diff --git a/compression/zstd/src/main/native/common/NativeZstdCDict.h b/compression/zstd/src/main/native/common/NativeZstdCDict.h new file mode 100644 index 000000000..460fb49c7 --- /dev/null +++ b/compression/zstd/src/main/native/common/NativeZstdCDict.h @@ -0,0 +1,33 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +//actually it's not, it was initially though +//easier to make this by hand lol +#include + +#ifndef _Included_net_daporkchop_lib_compression_zstd_natives_NativeZstdCDict +#define _Included_net_daporkchop_lib_compression_zstd_natives_NativeZstdCDict + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Class: net_daporkchop_lib_compression_zstd_natives_NativeZstdCDict + * Method: createCDict + * Signature: (JIIZ)J + */ +JNIEXPORT jlong JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdCDict_createCDict + (JNIEnv *, jclass, jlong, jint, jint, jboolean); + +/* + * Class: net_daporkchop_lib_compression_zstd_natives_NativeZstdCDict + * Method: releaseCDict + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdCDict_releaseCDict + (JNIEnv *, jclass, jlong); + +#ifdef __cplusplus +} +#endif + +#endif //_Included_net_daporkchop_lib_compression_zstd_natives_NativeZstdCDict diff --git a/compression/zstd/src/main/native/common/NativeZstdDCtx.cpp b/compression/zstd/src/main/native/common/NativeZstdDCtx.cpp new file mode 100644 index 000000000..c7d95bf97 --- /dev/null +++ b/compression/zstd/src/main/native/common/NativeZstdDCtx.cpp @@ -0,0 +1,68 @@ +#include +#include "NativeZstdDCtx.h" + +#include +#include + +__attribute__((visibility("default"))) jlong JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdDCtx_allocateCtx + (JNIEnv* env, jclass cla) { + return (jlong) ZSTD_createDCtx(); +} + +__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdDCtx_releaseCtx + (JNIEnv* env, jclass cla, jlong ctx) { + auto ret = ZSTD_freeDCtx((ZSTD_DCtx*) ctx); + + if (ZSTD_isError(ret)) { + throwException(env, ZSTD_getErrorName(ret), (jlong) ret); + return; + } +} + +__attribute__((visibility("default"))) jint JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdDCtx_doDecompressNoDict + (JNIEnv* env, jobject obj, jlong ctx, jlong srcAddr, jint srcSize, jlong dstAddr, jint dstSize) { + auto ret = ZSTD_decompressDCtx((ZSTD_DCtx*) ctx, (void*) dstAddr, dstSize, (void*) srcAddr, srcSize); + + if (ZSTD_isError(ret)) { + if (ZSTD_getErrorCode(ret) == ZSTD_error_dstSize_tooSmall) { + return -1; + } else { + throwException(env, ZSTD_getErrorName(ret), (jlong) ret); + return 0; + } + } + + return (jint) ret; +} + +__attribute__((visibility("default"))) jint JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdDCtx_doDecompressRawDict + (JNIEnv* env, jobject obj, jlong ctx, jlong srcAddr, jint srcSize, jlong dstAddr, jint dstSize, jlong dictAddr, jint dictSize) { + auto ret = ZSTD_decompress_usingDict((ZSTD_DCtx*) ctx, (void*) dstAddr, dstSize, (void*) srcAddr, srcSize, (void*) dictAddr, dictSize); + + if (ZSTD_isError(ret)) { + if (ZSTD_getErrorCode(ret) == ZSTD_error_dstSize_tooSmall) { + return -1; + } else { + throwException(env, ZSTD_getErrorName(ret), (jlong) ret); + return 0; + } + } + + return (jint) ret; +} + +__attribute__((visibility("default"))) jint JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdDCtx_doDecompressCDict + (JNIEnv* env, jobject obj, jlong ctx, jlong srcAddr, jint srcSize, jlong dstAddr, jint dstSize, jlong dictAddr) { + auto ret = ZSTD_decompress_usingDDict((ZSTD_DCtx*) ctx, (void*) dstAddr, dstSize, (void*) srcAddr, srcSize, (ZSTD_DDict*) dictAddr); + + if (ZSTD_isError(ret)) { + if (ZSTD_getErrorCode(ret) == ZSTD_error_dstSize_tooSmall) { + return -1; + } else { + throwException(env, ZSTD_getErrorName(ret), (jlong) ret); + return 0; + } + } + + return (jint) ret; +} diff --git a/compression/zstd/src/main/native/common/NativeZstdDCtx.h b/compression/zstd/src/main/native/common/NativeZstdDCtx.h new file mode 100644 index 000000000..34ed26cfb --- /dev/null +++ b/compression/zstd/src/main/native/common/NativeZstdDCtx.h @@ -0,0 +1,57 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +//actually it's not, it was initially though +//easier to make this by hand lol +#include + +#ifndef _Included_net_daporkchop_lib_compression_zstd_natives_NativeZstdDCtx +#define _Included_net_daporkchop_lib_compression_zstd_natives_NativeZstdDCtx + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Class: net_daporkchop_lib_compression_zstd_natives_NativeZstdDCtx + * Method: allocateCtx + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdDCtx_allocateCtx + (JNIEnv *, jclass); + +/* + * Class: net_daporkchop_lib_compression_zstd_natives_NativeZstdDCtx + * Method: releaseCtx + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdDCtx_releaseCtx + (JNIEnv *, jclass, jlong); + +/* + * Class: net_daporkchop_lib_compression_zstd_natives_NativeZstdDCtx + * Method: doDecompressNoDict + * Signature: (JJIJI)I + */ +JNIEXPORT jint JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdDCtx_doDecompressNoDict + (JNIEnv *, jobject, jlong, jlong, jint, jlong, jint); + +/* + * Class: net_daporkchop_lib_compression_zstd_natives_NativeZstdDCtx + * Method: doDecompressRawDict + * Signature: (JJIJIJI)I + */ +JNIEXPORT jint JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdDCtx_doDecompressRawDict + (JNIEnv *, jobject, jlong, jlong, jint, jlong, jint, jlong, jint); + +/* + * Class: net_daporkchop_lib_compression_zstd_natives_NativeZstdDCtx + * Method: doDecompressCDict + * Signature: (JJIJIJ)I + */ +JNIEXPORT jint JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdDCtx_doDecompressCDict + (JNIEnv *, jobject, jlong, jlong, jint, jlong, jint, jlong); + +#ifdef __cplusplus +} +#endif + +#endif //_Included_net_daporkchop_lib_compression_zstd_natives_NativeZstdDCtx diff --git a/compression/zstd/src/main/native/common/NativeZstdDDict.cpp b/compression/zstd/src/main/native/common/NativeZstdDDict.cpp new file mode 100644 index 000000000..7e17eaf1e --- /dev/null +++ b/compression/zstd/src/main/native/common/NativeZstdDDict.cpp @@ -0,0 +1,22 @@ +#include +#include "NativeZstdDDict.h" + +#include +#include + +__attribute__((visibility("default"))) jlong JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdDDict_createDDict + (JNIEnv* env, jclass cla, jlong dictAddr, jint dictSize, jboolean copy) { + return copy + ? (jlong) ZSTD_createDDict((void*) dictAddr, dictSize) + : 0;//: (jlong) ZSTD_createDDict_byReference((void*) dictAddr, dictSize); +} + +__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdDDict_releaseDDict + (JNIEnv* env, jclass cla, jlong ctx) { + auto ret = ZSTD_freeDDict((ZSTD_DDict*) ctx); + + if (ZSTD_isError(ret)) { + throwException(env, ZSTD_getErrorName(ret), (jlong) ret); + return; + } +} diff --git a/compression/zstd/src/main/native/common/NativeZstdDDict.h b/compression/zstd/src/main/native/common/NativeZstdDDict.h new file mode 100644 index 000000000..620de5052 --- /dev/null +++ b/compression/zstd/src/main/native/common/NativeZstdDDict.h @@ -0,0 +1,33 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +//actually it's not, it was initially though +//easier to make this by hand lol +#include + +#ifndef _Included_net_daporkchop_lib_compression_zstd_natives_NativeZstdDDict +#define _Included_net_daporkchop_lib_compression_zstd_natives_NativeZstdDDict + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Class: net_daporkchop_lib_compression_zstd_natives_NativeZstdDDict + * Method: createDDict + * Signature: (JIZ)J + */ +JNIEXPORT jlong JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdDDict_createDDict + (JNIEnv *, jclass, jlong, jint, jboolean); + +/* + * Class: net_daporkchop_lib_compression_zstd_natives_NativeZstdDDict + * Method: releaseDDict + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_net_daporkchop_lib_compression_zstd_natives_NativeZstdDDict_releaseDDict + (JNIEnv *, jclass, jlong); + +#ifdef __cplusplus +} +#endif + +#endif //_Included_net_daporkchop_lib_compression_zstd_natives_NativeZstdDDict diff --git a/crypto/src/test/java/net/daporkchop/lib/crypto/EncryptionTest.java b/crypto/src/test/java/net/daporkchop/lib/crypto/EncryptionTest.java index efe9aeb73..36997e4fe 100644 --- a/crypto/src/test/java/net/daporkchop/lib/crypto/EncryptionTest.java +++ b/crypto/src/test/java/net/daporkchop/lib/crypto/EncryptionTest.java @@ -16,6 +16,7 @@ package net.daporkchop.lib.crypto; import lombok.NonNull; +import net.daporkchop.lib.binary.oio.StreamUtil; import net.daporkchop.lib.common.function.io.IOConsumer; import net.daporkchop.lib.common.misc.TestRandomData; import net.daporkchop.lib.crypto.cipher.Cipher; @@ -304,7 +305,7 @@ private void testSeekable(@NonNull Function keyGenerator, @No byte[] encrypted = baos.toByteArray(); byte[] decrypted; try (InputStream in = cipher2.decrypt(new ByteArrayInputStream(encrypted), offset, b.length)) { - decrypted = IOUtils.readFully(in, -1, false); + decrypted = StreamUtil.toByteArray(in); } if (isInvalid(b, decrypted)) { throw new IllegalStateException("Decrypted data isn't the same!"); diff --git a/encoding/build.gradle b/encoding/build.gradle index 9f1c551df..030ac658a 100644 --- a/encoding/build.gradle +++ b/encoding/build.gradle @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -16,7 +16,4 @@ dependencies { compile project(":binary") compile project(":math") - - compile "org.tukaani:xz:$xzVersion" - compile "org.apache.commons:commons-compress:$apacheCommonsCompressVersion" } diff --git a/encoding/nbt/src/main/java/net/daporkchop/lib/nbt/NBTIO.java b/encoding/nbt/src/main/java/net/daporkchop/lib/nbt/NBTIO.java index 916d4aaa9..a81c99311 100644 --- a/encoding/nbt/src/main/java/net/daporkchop/lib/nbt/NBTIO.java +++ b/encoding/nbt/src/main/java/net/daporkchop/lib/nbt/NBTIO.java @@ -17,7 +17,6 @@ import lombok.NonNull; import lombok.experimental.UtilityClass; -import net.daporkchop.lib.encoding.compression.Compression; import net.daporkchop.lib.nbt.tag.TagRegistry; import net.daporkchop.lib.nbt.tag.notch.CompoundTag; @@ -29,6 +28,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; /** * Helper class for reading and writing NBT tags. @@ -76,7 +77,7 @@ public CompoundTag readGzipCompressed(@NonNull InputStream inputStream) throws I } public CompoundTag readGzipCompressed(@NonNull InputStream inputStream, @NonNull TagRegistry registry) throws IOException { - return new NBTInputStream(inputStream, Compression.GZIP_NORMAL).readTag(registry); + return new NBTInputStream(new GZIPInputStream(inputStream)).readTag(registry); } public CompoundTag readGzipCompressed(@NonNull File file) throws IOException { @@ -85,7 +86,7 @@ public CompoundTag readGzipCompressed(@NonNull File file) throws IOException { public CompoundTag readGzipCompressed(@NonNull File file, @NonNull TagRegistry registry) throws IOException { verifyFileExists(file, false); - try (NBTInputStream in = new NBTInputStream(new BufferedInputStream(new FileInputStream(file)), Compression.GZIP_NORMAL)) { + try (NBTInputStream in = new NBTInputStream(new GZIPInputStream(new FileInputStream(file)))) { return in.readTag(registry); } } @@ -115,7 +116,7 @@ public void writeGzipCompressed(@NonNull OutputStream out, @NonNull CompoundTag } public void writeGzipCompressed(@NonNull OutputStream out, @NonNull CompoundTag tag, @NonNull TagRegistry registry) throws IOException { - NBTOutputStream nbtOut = new NBTOutputStream(out, Compression.GZIP_NORMAL); + NBTOutputStream nbtOut = new NBTOutputStream(new GZIPOutputStream(out)); nbtOut.writeTag(tag, registry); } @@ -125,7 +126,7 @@ public void writeGzipCompressed(@NonNull File file, @NonNull CompoundTag tag) th public void writeGzipCompressed(@NonNull File file, @NonNull CompoundTag tag, @NonNull TagRegistry registry) throws IOException { verifyFileExists(file, true); - try (NBTOutputStream out = new NBTOutputStream(new BufferedOutputStream(new FileOutputStream(file)), Compression.GZIP_NORMAL)) { + try (NBTOutputStream out = new NBTOutputStream(new GZIPOutputStream(new FileOutputStream(file)))) { out.writeTag(tag, registry); } } diff --git a/encoding/nbt/src/main/java/net/daporkchop/lib/nbt/NBTInputStream.java b/encoding/nbt/src/main/java/net/daporkchop/lib/nbt/NBTInputStream.java index d8785e88b..4627c05f1 100644 --- a/encoding/nbt/src/main/java/net/daporkchop/lib/nbt/NBTInputStream.java +++ b/encoding/nbt/src/main/java/net/daporkchop/lib/nbt/NBTInputStream.java @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -19,18 +19,14 @@ import lombok.NonNull; import lombok.experimental.Accessors; import net.daporkchop.lib.binary.stream.DataIn; -import net.daporkchop.lib.encoding.compression.Compression; -import net.daporkchop.lib.encoding.compression.CompressionHelper; import net.daporkchop.lib.nbt.alloc.DefaultNBTArrayAllocator; import net.daporkchop.lib.nbt.alloc.NBTArrayAllocator; -import net.daporkchop.lib.nbt.tag.Tag; import net.daporkchop.lib.nbt.tag.TagRegistry; import net.daporkchop.lib.nbt.tag.notch.CompoundTag; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.util.Stack; import java.util.function.Function; /** @@ -47,35 +43,19 @@ public final class NBTInputStream extends DataIn { protected final NBTArrayAllocator alloc; public NBTInputStream(@NonNull InputStream in) throws IOException { - this(in, Compression.NONE, TagRegistry.NOTCHIAN, DefaultNBTArrayAllocator.INSTANCE); - } - - public NBTInputStream(@NonNull InputStream in, @NonNull CompressionHelper compression) throws IOException { - this(in, compression, TagRegistry.NOTCHIAN, DefaultNBTArrayAllocator.INSTANCE); + this(in, TagRegistry.NOTCHIAN, DefaultNBTArrayAllocator.INSTANCE); } public NBTInputStream(@NonNull InputStream in, @NonNull TagRegistry registry) throws IOException { - this(in, Compression.NONE, registry, DefaultNBTArrayAllocator.INSTANCE); - } - - public NBTInputStream(@NonNull InputStream in, @NonNull CompressionHelper compression, @NonNull TagRegistry registry) throws IOException { - this(in, compression, registry, DefaultNBTArrayAllocator.INSTANCE); + this(in, registry, DefaultNBTArrayAllocator.INSTANCE); } public NBTInputStream(@NonNull InputStream in, @NonNull NBTArrayAllocator alloc) throws IOException { - this(in, Compression.NONE, TagRegistry.NOTCHIAN, alloc); - } - - public NBTInputStream(@NonNull InputStream in, @NonNull CompressionHelper compression, @NonNull NBTArrayAllocator alloc) throws IOException { - this(in, compression, TagRegistry.NOTCHIAN, alloc); + this(in, TagRegistry.NOTCHIAN, alloc); } public NBTInputStream(@NonNull InputStream in, @NonNull TagRegistry registry, @NonNull NBTArrayAllocator alloc) throws IOException { - this(in, Compression.NONE, registry, alloc); - } - - public NBTInputStream(@NonNull InputStream in, @NonNull CompressionHelper compression, @NonNull TagRegistry registry, @NonNull NBTArrayAllocator alloc) throws IOException { - this.in = DataIn.wrap(compression.inflate(in)); + this.in = DataIn.wrap(in); this.defaultRegistry = registry; this.alloc = alloc; } @@ -266,18 +246,18 @@ public long readVarLong() throws IOException { } @Override - public byte[] readFully(@NonNull byte[] b) throws IOException { - return this.in.readFully(b); + public byte[] readFully(@NonNull byte[] dst) throws IOException { + return this.in.readFully(dst); } @Override - public byte[] readFully(@NonNull byte[] b, int off, int len) throws IOException { - return this.in.readFully(b, off, len); + public byte[] readFully(@NonNull byte[] dst, int start, int length) throws IOException { + return this.in.readFully(dst, start, length); } @Override - public byte[] readAllAvailableBytes() throws IOException { - return this.in.readAllAvailableBytes(); + public byte[] toByteArray() throws IOException { + return this.in.toByteArray(); } @Override diff --git a/encoding/nbt/src/main/java/net/daporkchop/lib/nbt/NBTOutputStream.java b/encoding/nbt/src/main/java/net/daporkchop/lib/nbt/NBTOutputStream.java index 57dfa9558..f8d007753 100644 --- a/encoding/nbt/src/main/java/net/daporkchop/lib/nbt/NBTOutputStream.java +++ b/encoding/nbt/src/main/java/net/daporkchop/lib/nbt/NBTOutputStream.java @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -17,8 +17,6 @@ import lombok.NonNull; import net.daporkchop.lib.binary.stream.DataOut; -import net.daporkchop.lib.encoding.compression.Compression; -import net.daporkchop.lib.encoding.compression.CompressionHelper; import net.daporkchop.lib.nbt.tag.TagRegistry; import net.daporkchop.lib.nbt.tag.notch.CompoundTag; @@ -34,19 +32,11 @@ public final class NBTOutputStream extends DataOut { private final TagRegistry defaultRegistry; public NBTOutputStream(@NonNull OutputStream out) throws IOException { - this(out, Compression.NONE, TagRegistry.NOTCHIAN); - } - - public NBTOutputStream(@NonNull OutputStream out, @NonNull CompressionHelper compression) throws IOException { - this(out, compression, TagRegistry.NOTCHIAN); + this(out, TagRegistry.NOTCHIAN); } public NBTOutputStream(@NonNull OutputStream out, @NonNull TagRegistry registry) throws IOException { - this(out, Compression.NONE, registry); - } - - public NBTOutputStream(@NonNull OutputStream out, @NonNull CompressionHelper compression, @NonNull TagRegistry registry) throws IOException { - this.out = DataOut.wrap(compression.deflate(out)); + this.out = DataOut.wrap(out); this.defaultRegistry = registry; } diff --git a/encoding/nbt/src/test/java/NBTTest.java b/encoding/nbt/src/test/java/NBTTest.java index e1d615841..8be825d69 100644 --- a/encoding/nbt/src/test/java/NBTTest.java +++ b/encoding/nbt/src/test/java/NBTTest.java @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -14,13 +14,12 @@ */ import lombok.NonNull; -import net.daporkchop.lib.encoding.compression.Compression; +import net.daporkchop.lib.binary.oio.StreamUtil; import net.daporkchop.lib.nbt.NBTInputStream; import net.daporkchop.lib.nbt.NBTOutputStream; import net.daporkchop.lib.nbt.tag.Tag; import net.daporkchop.lib.nbt.tag.notch.CompoundTag; import net.daporkchop.lib.nbt.tag.notch.ListTag; -import org.apache.commons.compress.utils.IOUtils; import org.junit.Test; import java.io.BufferedOutputStream; @@ -30,6 +29,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.zip.GZIPInputStream; /** * @author DaPorkchop_ @@ -60,9 +60,8 @@ public static String space(int count) { @Test public void testWriting() throws IOException { byte[] original_uncompressed; - try (InputStream is = NBTTest.class.getResourceAsStream("bigtest.nbt")) { - byte[] b = IOUtils.toByteArray(is); - original_uncompressed = Compression.GZIP_NORMAL.inflate(b); + try (InputStream is = new GZIPInputStream(NBTTest.class.getResourceAsStream("bigtest.nbt"))) { + original_uncompressed = StreamUtil.toByteArray(is); } CompoundTag tag; try (NBTInputStream in = new NBTInputStream(new ByteArrayInputStream(original_uncompressed))) { @@ -103,7 +102,7 @@ public void testHelloWorld() throws IOException { @Test public void testBig() throws IOException { - try (NBTInputStream in = new NBTInputStream(NBTTest.class.getResourceAsStream("bigtest.nbt"), Compression.GZIP_NORMAL)) { + try (NBTInputStream in = new NBTInputStream(new GZIPInputStream(NBTTest.class.getResourceAsStream("bigtest.nbt")))) { CompoundTag tag = in.readTag(); printTagRecursive(tag, 0); } diff --git a/encoding/src/main/java/net/daporkchop/lib/encoding/Hexadecimal.java b/encoding/src/main/java/net/daporkchop/lib/encoding/Hexadecimal.java index b111c6592..e40303eac 100644 --- a/encoding/src/main/java/net/daporkchop/lib/encoding/Hexadecimal.java +++ b/encoding/src/main/java/net/daporkchop/lib/encoding/Hexadecimal.java @@ -32,12 +32,13 @@ @UtilityClass public class Hexadecimal { private final char[] ALPHABET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - private final byte[] INDEX = new byte['f' + 1]; + private final byte[] INDEX = new byte[128]; static { Arrays.fill(INDEX, (byte) -1); for (byte i = 0; i < ALPHABET.length; i++) { - INDEX[ALPHABET[i]] = i; + INDEX[Character.toLowerCase(ALPHABET[i])] = i; + INDEX[Character.toUpperCase(ALPHABET[i])] = i; } } diff --git a/encoding/src/main/java/net/daporkchop/lib/encoding/compression/Compression.java b/encoding/src/main/java/net/daporkchop/lib/encoding/compression/Compression.java deleted file mode 100644 index c65afdd61..000000000 --- a/encoding/src/main/java/net/daporkchop/lib/encoding/compression/Compression.java +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Adapted from the Wizardry License - * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors - * - * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. - * - * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. - * - * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -package net.daporkchop.lib.encoding.compression; - -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.experimental.UtilityClass; -import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream; -import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream; -import org.apache.commons.compress.compressors.deflate.DeflateCompressorInputStream; -import org.apache.commons.compress.compressors.deflate.DeflateCompressorOutputStream; -import org.apache.commons.compress.compressors.deflate.DeflateParameters; -import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; -import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream; -import org.apache.commons.compress.compressors.gzip.GzipParameters; -import org.apache.commons.compress.compressors.lz4.BlockLZ4CompressorInputStream; -import org.apache.commons.compress.compressors.lz4.BlockLZ4CompressorOutputStream; -import org.apache.commons.compress.compressors.lz4.FramedLZ4CompressorOutputStream; -import org.tukaani.xz.LZMA2Options; -import org.tukaani.xz.LZMAInputStream; -import org.tukaani.xz.LZMAOutputStream; -import org.tukaani.xz.XZInputStream; -import org.tukaani.xz.XZOutputStream; - -import java.io.IOException; -import java.io.OutputStream; -import java.lang.reflect.Field; - -/** - * Contains static references to a number of standard compression algorithms. - * - * @author DaPorkchop_ - */ -//TODO: strip some dependencies off of this -@UtilityClass -public class Compression { - public static final CompressionHelper NONE = CompressionHelper.builder("Uncompressed") - .setInputStreamWrapperSimple(in -> in) - .setOutputStreamWrapperSimple(out -> out) - .build(); - - public static final CompressionHelper GZIP_LOW = CompressionHelper.builder("GZip", "Low") - .setParams(new GzipParameters() { - { - this.setCompressionLevel(0); - } - }) - .setInputStreamWrapperSimple(GzipCompressorInputStream::new) - .setOutputStreamWrapper(GzipCompressorOutputStream::new) - .build(); - - public static final CompressionHelper GZIP_NORMAL = CompressionHelper.builder("GZip", "Normal") - .setParams(new GzipParameters() { - { - this.setCompressionLevel(4); - } - }) - .setInputStreamWrapperSimple(GzipCompressorInputStream::new) - .setOutputStreamWrapper(GzipCompressorOutputStream::new) - .build(); - - public static final CompressionHelper GZIP_HIGH = CompressionHelper.builder("GZip", "High") - .setParams(new GzipParameters() { - { - this.setCompressionLevel(8); - } - }) - .setInputStreamWrapperSimple(GzipCompressorInputStream::new) - .setOutputStreamWrapper(GzipCompressorOutputStream::new) - .build(); - - public static final CompressionHelper BZIP2_LOW = CompressionHelper.builder("BZip2", "Low") - .setParams(1) - .setInputStreamWrapperSimple(BZip2CompressorInputStream::new) - .setOutputStreamWrapper(BZip2CompressorOutputStream::new) - .build(); - - public static final CompressionHelper BZIP2_NORMAL = CompressionHelper.builder("BZip2", "Normal") - .setParams(1) - .setInputStreamWrapperSimple(BZip2CompressorInputStream::new) - .setOutputStreamWrapper(BZip2CompressorOutputStream::new) - .build(); - - public static final CompressionHelper BZIP2_HIGH = CompressionHelper.builder("BZip2", "High") - .setParams(1) - .setInputStreamWrapperSimple(BZip2CompressorInputStream::new) - .setOutputStreamWrapper(BZip2CompressorOutputStream::new) - .build(); - - public static final CompressionHelper DEFLATE_LOW = CompressionHelper.builder("Deflate", "Low") - .setParams(new DeflateParameters() { - { - this.setCompressionLevel(0); - } - }) - .setInputStreamWrapper(DeflateCompressorInputStream::new) - .setOutputStreamWrapper(DeflateCompressorOutputStream::new) - .build(); - - public static final CompressionHelper DEFLATE_NORMAL = CompressionHelper.builder("Deflate", "Normal") - .setParams(new DeflateParameters() { - { - this.setCompressionLevel(4); - } - }) - .setInputStreamWrapper(DeflateCompressorInputStream::new) - .setOutputStreamWrapper(DeflateCompressorOutputStream::new) - .build(); - - public static final CompressionHelper DEFLATE_HIGH = CompressionHelper.builder("Deflate", "High") - .setParams(new DeflateParameters() { - { - this.setCompressionLevel(8); - } - }) - .setInputStreamWrapper(DeflateCompressorInputStream::new) - .setOutputStreamWrapper(DeflateCompressorOutputStream::new) - .build(); - - public static final CompressionHelper LZ4_BLOCK = CompressionHelper.builder("LZ4", "Block") - .setInputStreamWrapperSimple(BlockLZ4CompressorInputStream::new) - .setOutputStreamWrapperSimple(BlockLZ4CompressorOutputStream::new) - .build(); - - public static final CompressionHelper LZ4_FRAMED_64KB = CompressionHelper.builder("LZ4", "Framed - 64KB") - .setParams(new FramedLZ4CompressorOutputStream.Parameters(FramedLZ4CompressorOutputStream.BlockSize.K64)) - .setInputStreamWrapperSimple(BlockLZ4CompressorInputStream::new) - .setOutputStreamWrapperSimple(BlockLZ4CompressorOutputStream::new) - .build(); - - public static final CompressionHelper LZ4_FRAMED_256KB = CompressionHelper.builder("LZ4", "Framed - 256KB") - .setParams(new FramedLZ4CompressorOutputStream.Parameters(FramedLZ4CompressorOutputStream.BlockSize.K256)) - .setInputStreamWrapperSimple(BlockLZ4CompressorInputStream::new) - .setOutputStreamWrapperSimple(BlockLZ4CompressorOutputStream::new) - .build(); - - public static final CompressionHelper LZ4_FRAMED_1MB = CompressionHelper.builder("LZ4", "Framed - 1MB") - .setParams(new FramedLZ4CompressorOutputStream.Parameters(FramedLZ4CompressorOutputStream.BlockSize.M1)) - .setInputStreamWrapperSimple(BlockLZ4CompressorInputStream::new) - .setOutputStreamWrapperSimple(BlockLZ4CompressorOutputStream::new) - .build(); - - public static final CompressionHelper LZ4_FRAMED_4MB = CompressionHelper.builder("LZ4", "Framed - 4MB") - .setParams(new FramedLZ4CompressorOutputStream.Parameters(FramedLZ4CompressorOutputStream.BlockSize.M4)) - .setInputStreamWrapperSimple(BlockLZ4CompressorInputStream::new) - .setOutputStreamWrapperSimple(BlockLZ4CompressorOutputStream::new) - .build(); - - /*public static final CompressionHelper LZMA2_LOW = CompressionHelper.builder("LZMA2", "Low") - .setParamsFunc(() -> new LZMA2Options(0)) - .setInputStreamWrapperSimple(in -> new LZMA2InputStream(in, -1)) - .setOutputStreamWrapper(LZMA2OutputStream::new) - .build(); - - public static final CompressionHelper LZMA2_NORMAL = CompressionHelper.builder("LZMA2", "Normal") - .setParamsFunc(() -> new LZMA2Options(0)) - .setInputStreamWrapperSimple(BlockLZ4CompressorInputStream::new) - .setOutputStreamWrapperSimple(BlockLZ4CompressorOutputStream::new) - .build(); - - public static final CompressionHelper LZMA2_HIGH = CompressionHelper.builder("LZMA2", "High") - .setParamsFunc(() -> new LZMA2Options(0)) - .setInputStreamWrapperSimple(BlockLZ4CompressorInputStream::new) - .setOutputStreamWrapperSimple(BlockLZ4CompressorOutputStream::new) - .build();*/ - - public static final CompressionHelper LZMA_LOW = CompressionHelper.builder("LZMA", "Low") - .setParamsFunc(() -> new LZMA2Options(0)) - .setInputStreamWrapperSimple(LZMAInputStream::new) - .setOutputStreamWrapper((out, params) -> new LZMAWrapperOut(new LZMAOutputStream(out, params, -1L))) - .build(); - - public static final CompressionHelper LZMA_NORMAL = CompressionHelper.builder("LZMA", "Normal") - .setParamsFunc(() -> new LZMA2Options(6)) - .setInputStreamWrapperSimple(LZMAInputStream::new) - .setOutputStreamWrapper((out, params) -> new LZMAWrapperOut(new LZMAOutputStream(out, params, -1L))) - .build(); - - public static final CompressionHelper LZMA_HIGH = CompressionHelper.builder("LZMA", "High") - .setParamsFunc(() -> new LZMA2Options(9)) - .setInputStreamWrapperSimple(LZMAInputStream::new) - .setOutputStreamWrapper((out, params) -> new LZMAWrapperOut(new LZMAOutputStream(out, params, -1L))) - .build(); - - public static final CompressionHelper XZ_LOW = CompressionHelper.builder("XZ", "Low") - .setParamsFunc(() -> new LZMA2Options(0)) - .setInputStreamWrapperSimple(XZInputStream::new) - .setOutputStreamWrapper(XZOutputStream::new) - .build(); - - public static final CompressionHelper XZ_NORMAL = CompressionHelper.builder("XZ", "Normal") - .setParamsFunc(() -> new LZMA2Options(6)) - .setInputStreamWrapperSimple(XZInputStream::new) - .setOutputStreamWrapper(XZOutputStream::new) - .build(); - - public static final CompressionHelper XZ_HIGH = CompressionHelper.builder("XZ", "High") - .setParamsFunc(() -> new LZMA2Options(9)) - .setInputStreamWrapperSimple(XZInputStream::new) - .setOutputStreamWrapper(XZOutputStream::new) - .build(); - - static { - try { - for (Field field : Compression.class.getDeclaredFields()) { - if (field.getType() == CompressionHelper.class) { - //System.out.printf("Found compression algorithm: %s\n", field.getName()); - CompressionHelper.registerCompressionType(field.getName(), (CompressionHelper) field.get(null)); - } - } - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - @RequiredArgsConstructor - private static class LZMAWrapperOut extends OutputStream { - @NonNull - private final LZMAOutputStream out; - - @Override - public void write(int b) throws IOException { - this.out.write(b); - } - - /*@Override - public void write(byte[] b) throws IOException { - this.out.write(b); - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - this.out.write(b, off, len); - }*/ - - @Override - public void close() throws IOException { - this.out.close(); - } - } -} diff --git a/encoding/src/main/java/net/daporkchop/lib/encoding/compression/CompressionHelper.java b/encoding/src/main/java/net/daporkchop/lib/encoding/compression/CompressionHelper.java deleted file mode 100644 index 3dd8d8dc2..000000000 --- a/encoding/src/main/java/net/daporkchop/lib/encoding/compression/CompressionHelper.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Adapted from the Wizardry License - * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors - * - * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. - * - * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. - * - * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -package net.daporkchop.lib.encoding.compression; - -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import lombok.experimental.Accessors; -import net.daporkchop.lib.common.function.ThrowingSupplier; -import net.daporkchop.lib.common.function.io.IOBiFunction; -import net.daporkchop.lib.common.function.io.IOFunction; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Collections; -import java.util.Comparator; -import java.util.Hashtable; -import java.util.IdentityHashMap; -import java.util.Map; -import java.util.function.BiConsumer; -import java.util.stream.Stream; - -/** - * Utility to store initialization functions for various compression algorithms - * - * @author DaPorkchop_ - */ -@AllArgsConstructor(access = AccessLevel.PRIVATE) -public class CompressionHelper { - static final Map nameLookup = Collections.synchronizedMap(new Hashtable<>()); - static final Map reverseNameLookup = Collections.synchronizedMap(new IdentityHashMap<>()); - - static boolean registerCompressionType(@NonNull String name, @NonNull CompressionHelper helper) { - return nameLookup.putIfAbsent(name, helper) == null && reverseNameLookup.put(helper, name) == null; - } - - public static CompressionHelper forName(@NonNull String name) { - return nameLookup.get(name); - } - - public static String getName(@NonNull CompressionHelper name) { - return reverseNameLookup.get(name); - } - - public static void forAllRegisteredAlgs(@NonNull BiConsumer consumer) { - forAllRegisteredAlgs(consumer, false); - } - - public static void forAllRegisteredAlgs(@NonNull BiConsumer consumer, boolean parallel) { - Stream> stream = nameLookup.entrySet().stream(); - if (parallel) { - stream = stream.parallel(); - } else { - stream = stream.sorted(Comparator.comparing(Map.Entry::getKey, Comparator.naturalOrder())); - } - stream.forEach(e -> consumer.accept(e.getKey(), e.getValue())); - } - - public static Builder builder(@NonNull String name) { - return new Builder<>(name); - } - - public static Builder builder(@NonNull String name, @NonNull String level) { - return new Builder<>(name, level); - } - @NonNull - @Getter - private final String name; - @NonNull - @Getter - private final String level; - private final ParamType params; - @NonNull - private final IOBiFunction inputStreamWrapper; - @NonNull - private final IOBiFunction outputStreamWrapper; - - public InputStream inflate(@NonNull InputStream in) throws IOException { - return this.inputStreamWrapper.applyThrowing(in, this.params); - } - - public OutputStream deflate(@NonNull OutputStream out) throws IOException { - return this.outputStreamWrapper.applyThrowing(out, this.params); - } - - public byte[] inflate(@NonNull byte[] compressed) { - try (InputStream in = this.inflate(new ByteArrayInputStream(compressed))) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - int i; - while ((i = in.read()) != -1) { - baos.write(i); - } - return baos.toByteArray(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public byte[] deflate(@NonNull byte[] original) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try (InputStream in = new ByteArrayInputStream(original); - OutputStream out = this.deflate(baos)) { - int i; - while ((i = in.read()) != -1) { - out.write(i); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - return baos.toByteArray(); - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } else if (obj instanceof CompressionHelper) { - CompressionHelper compressionHelper = (CompressionHelper) obj; - return this.name.equals(compressionHelper.name) && this.level.equals(compressionHelper.level); - } else { - return false; - } - } - - @Override - public String toString() { - return String.format("%s (%s)", this.name, this.level); - } - - @Getter - @Setter - @Accessors(chain = true) - @RequiredArgsConstructor(access = AccessLevel.PRIVATE) - public static class Builder { - @NonNull - private final String name; - @NonNull - private final String level; - - private ParamType params; - private IOBiFunction inputStreamWrapper; - private IOBiFunction outputStreamWrapper; - - private Builder(@NonNull String name) { - this(name, "Default"); - } - - public Builder setParamsFunc(@NonNull ThrowingSupplier supplier) { - this.params = supplier.get(); - return this; - } - - public Builder setInputStreamWrapperSimple(@NonNull IOFunction inputStreamWrapper) { - this.inputStreamWrapper = (in, params) -> inputStreamWrapper.applyThrowing(in); - return this; - } - - public Builder setOutputStreamWrapperSimple(@NonNull IOFunction outputStreamWrapper) { - this.outputStreamWrapper = (out, params) -> outputStreamWrapper.applyThrowing(out); - return this; - } - - public CompressionHelper build() { - return new CompressionHelper<>(this.name, this.level, this.params, this.inputStreamWrapper, this.outputStreamWrapper); - } - } -} diff --git a/encoding/src/test/java/encoding/CompressionTest.java b/encoding/src/test/java/encoding/CompressionTest.java deleted file mode 100644 index 84d1169b6..000000000 --- a/encoding/src/test/java/encoding/CompressionTest.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Adapted from the Wizardry License - * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors - * - * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. - * - * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. - * - * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -package encoding; - -import net.daporkchop.lib.common.misc.TestRandomData; -import net.daporkchop.lib.encoding.compression.Compression; -import net.daporkchop.lib.encoding.compression.CompressionHelper; -import org.junit.Test; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.Hashtable; -import java.util.Map; - -/** - * @author DaPorkchop_ - */ -public class CompressionTest { - @Test - public void test() { - Compression.BZIP2_HIGH.toString(); - Map ratios = Collections.synchronizedMap(new Hashtable<>()); - Map times = Collections.synchronizedMap(new Hashtable<>()); - CompressionHelper.forAllRegisteredAlgs((name, helper) -> { - System.out.printf("Testing compression: %s\n", helper); - int inputSize = 0; - int outputSize = 0; - long time = System.currentTimeMillis(); - for (byte[] orig : TestRandomData.randomBytes) { - inputSize += orig.length; - byte[] compressed = helper.deflate(orig); - outputSize += compressed.length; - byte[] inflated = helper.inflate(compressed); - if (!Arrays.equals(orig, inflated)) { - throw new IllegalStateException(String.format("Inflated data was not the same as original data on %s", helper)); - } - } - ratios.put(helper.toString(), ((double) inputSize / (double) outputSize) * 100.0d); - times.put(helper.toString(), System.currentTimeMillis() - time); - }, true); - System.out.println(); - System.out.println("Compression ratios:"); - ratios.entrySet().stream() - .sorted(Comparator.comparing(Map.Entry::getValue, Double::compare)) - .forEachOrdered(e -> System.out.printf(" %.3f%% for %s\n", e.getValue(), e.getKey())); - - System.out.println(); - System.out.println("Compression times:"); - times.entrySet().stream() - .sorted(Comparator.comparing(Map.Entry::getValue, Long::compare)) - .forEachOrdered(e -> System.out.printf(" %05dms for %s\n", e.getValue(), e.getKey())); - } -} diff --git a/http/http-netty/src/main/java/net/daporkchop/lib/http/impl/netty/util/NettyHttpUtil.java b/http/http-netty/src/main/java/net/daporkchop/lib/http/impl/netty/util/NettyHttpUtil.java index 1511f3830..69bbd57ac 100644 --- a/http/http-netty/src/main/java/net/daporkchop/lib/http/impl/netty/util/NettyHttpUtil.java +++ b/http/http-netty/src/main/java/net/daporkchop/lib/http/impl/netty/util/NettyHttpUtil.java @@ -49,12 +49,17 @@ public class NettyHttpUtil { //protected final Pattern _BASE_URL_PATTERN = Pattern.compile("^((?:[\\/A-Za-z+*\\-._]|%(?:[a-zA-Z0-9]{2})+)+)(\\?(?:(?1)(?:=(?1)+)?&)*(?:(?1)+(?:=(?1)+)?))?(?:#((?1)*))?$"); + //protected final String _URL_CHARS = "[A-Za-z0-9-._~:\\/?#[\\]@!\\$&'\\(\\)\\*\\+,;=]"; + protected final String _URL_CHARS = "[A-Za-z0-9-._~:\\/\\[\\]@!\\$&'\\(\\)\\*\\+,;=]"; + protected final String _PARAMS_CHARS = "[A-Za-z0-9-._~:\\/\\[\\]@!\\$&'\\(\\)\\*\\+,;]"; + //formatting for regex101: //^(?((?:[\/A-Za-z0-9+*\-._]|%(?:[a-fA-F0-9]{2})+)+))(?>\?(?(?>(?2)(?>=(?2))?)(?>&(?¶ms))?)?)?(?>#(?(?2)*))?$ - protected final Pattern _URL_PATTERN = Pattern.compile("^(?((?:[\\/A-Za-z0-9+*\\-._]|%(?:[a-fA-F0-9]{2})+)+))(?>\\?(?(?>(?2)(?>=(?2))?)(?>&(?'params'))?)?)?(?>#(?(?2)*))?$"); + protected final Pattern _URL_PATTERN = Pattern.compile("^(?((?:" + _URL_CHARS + "|%(?:[a-fA-F0-9]{2})+)+))(?>\\?(?(?>(?2)(?>=(?2))?)(?>&(?'params'))?)?)?(?>#(?(?2)*))?$"); - protected final Pattern _PARAMS_PATTERN = Pattern.compile("(?>^|&)(?((?>[\\/A-Za-z0-9+*\\-._]|%(?:[a-fA-F0-9]{2})+)+))(?>=(?(?2)))?"); + protected final Pattern _PARAMS_PATTERN = Pattern.compile("(?>^|&)(?((?>" + _PARAMS_CHARS + "|%(?:[a-fA-F0-9]{2})+)+))(?>=(?(?2)))?"); + //TODO: this still doesn't work correctly... public Query parseQuery(@NonNull HttpMethod method, @NonNull CharSequence query) throws HttpException { Matcher urlMatcher = _URL_PATTERN.matcher(query); if (!urlMatcher.find()) { diff --git a/http/src/main/java/net/daporkchop/lib/http/util/URLEncoding.java b/http/src/main/java/net/daporkchop/lib/http/util/URLEncoding.java index fd552ce77..a6bcff07c 100644 --- a/http/src/main/java/net/daporkchop/lib/http/util/URLEncoding.java +++ b/http/src/main/java/net/daporkchop/lib/http/util/URLEncoding.java @@ -93,27 +93,26 @@ public void encode(@NonNull Appendable dst, @NonNull CharSequence text, boolean } else if (c == ' ') { dst.append('+'); } else { - dst.append('%'); if (c < 0x80) { - Hexadecimal.encode(dst, (byte) c); + Hexadecimal.encode(dst.append('%'), (byte) c); } else if (c < 0x800) { - Hexadecimal.encode(dst, (byte) (0xC0 | (c >> 6))); - Hexadecimal.encode(dst, (byte) (0x80 | (c & 0x3F))); + Hexadecimal.encode(dst.append('%'), (byte) (0xC0 | (c >> 6))); + Hexadecimal.encode(dst.append('%'), (byte) (0x80 | (c & 0x3F))); } else if (StringUtil.isSurrogate(c)) { if (Character.isHighSurrogate(c) && ++i < length) { char c2 = arr != null ? arr[i] : text.charAt(i); if (Character.isLowSurrogate(c2)) { int codePoint = Character.toCodePoint(c, c2); - Hexadecimal.encode(dst, (byte) (0xF0 | (codePoint >> 18))); - Hexadecimal.encode(dst, (byte) (0x80 | ((codePoint >> 12) & 0x3F))); - Hexadecimal.encode(dst, (byte) (0x80 | ((codePoint >> 6) & 0x3F))); - Hexadecimal.encode(dst, (byte) (0x80 | (codePoint & 0x3F))); + Hexadecimal.encode(dst.append('%'), (byte) (0xF0 | (codePoint >> 18))); + Hexadecimal.encode(dst.append('%'), (byte) (0x80 | ((codePoint >> 12) & 0x3F))); + Hexadecimal.encode(dst.append('%'), (byte) (0x80 | ((codePoint >> 6) & 0x3F))); + Hexadecimal.encode(dst.append('%'), (byte) (0x80 | (codePoint & 0x3F))); } else { - Hexadecimal.encode(dst, (byte) '?'); + Hexadecimal.encode(dst.append('%'), (byte) '?'); i--; } } else { - Hexadecimal.encode(dst, (byte) '?'); + Hexadecimal.encode(dst.append('%'), (byte) '?'); } } } @@ -149,18 +148,23 @@ public void decode(@NonNull StringBuilder to, @NonNull CharSequence text) throws throw StatusCodes.BAD_REQUEST.exception(); } if ((b & 0xE0) == 0xC0) { - i += 2; + i += 3; int b2; - if (i >= length || (b2 = Hexadecimal.decodeUnsigned(arr != null ? arr[i - 1] : text.charAt(i - 1), arr != null ? arr[i] : text.charAt(i))) < 0) { + if (i >= length + || (arr != null ? arr[i - 2] : text.charAt(i - 2)) != '%' + || (b2 = Hexadecimal.decodeUnsigned(arr != null ? arr[i - 1] : text.charAt(i - 1), arr != null ? arr[i] : text.charAt(i))) < 0) { throw StatusCodes.BAD_REQUEST.exception(); } to.append((char) (((b & 0x1F) << 6) | (b2 & 0x3F))); } else if ((b & 0xF0) == 0xF0) { - i += 6; + i += 9; int b2, b3, b4; if (i >= length - || (b2 = Hexadecimal.decodeUnsigned(arr != null ? arr[i - 5] : text.charAt(i - 5), arr != null ? arr[i - 4] : text.charAt(i - 4))) < 0 - || (b3 = Hexadecimal.decodeUnsigned(arr != null ? arr[i - 3] : text.charAt(i - 3), arr != null ? arr[i - 2] : text.charAt(i - 2))) < 0 + || (arr != null ? arr[i - 8] : text.charAt(i - 8)) != '%' + || (b2 = Hexadecimal.decodeUnsigned(arr != null ? arr[i - 7] : text.charAt(i - 7), arr != null ? arr[i - 6] : text.charAt(i - 6))) < 0 + || (arr != null ? arr[i - 5] : text.charAt(i - 5)) != '%' + || (b3 = Hexadecimal.decodeUnsigned(arr != null ? arr[i - 4] : text.charAt(i - 4), arr != null ? arr[i - 3] : text.charAt(i - 3))) < 0 + || (arr != null ? arr[i - 2] : text.charAt(i - 2)) != '%' || (b4 = Hexadecimal.decodeUnsigned(arr != null ? arr[i - 1] : text.charAt(i - 1), arr != null ? arr[i] : text.charAt(i))) < 0) { throw StatusCodes.BAD_REQUEST.exception(); } diff --git a/http/src/test/java/http/HttpTest.java b/http/src/test/java/http/HttpTest.java index 0a1a63acc..a23804da6 100644 --- a/http/src/test/java/http/HttpTest.java +++ b/http/src/test/java/http/HttpTest.java @@ -55,7 +55,9 @@ public void test2() throws IOException { @Test public void testGET() throws IOException { String text = Base58.encodeBase58(TestRandomData.getRandomBytes(64, 128)); - String response = new JsonParser().parse(Http.getString(String.format("http://httpbin.org/get?data=%s", text))).getAsJsonObject().get("args").getAsJsonObject().get("keys").getAsString(); + String response = new JsonParser().parse(Http.getString(String.format("http://httpbin.org/get?data=%s", text))).getAsJsonObject() + .get("args").getAsJsonObject() + .get("data").getAsString(); if (!text.equals(response)) { throw new IllegalStateException(String.format("Data not identical! Sent=%s Received=%s", text, response)); } diff --git a/minecraft/build.gradle b/minecraft/build.gradle index dff67b94e..415be7707 100644 --- a/minecraft/build.gradle +++ b/minecraft/build.gradle @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -13,20 +13,14 @@ * */ -repositories { - maven { - name = "SpigotMC Snapshots" - url = "https://hub.spigotmc.org/nexus/content/repositories/sonatype-nexus-snapshots/" - } -} - dependencies { compile project(":binary") + compile project(":compression:compression-zlib") compile project(":encoding") compile project(":encoding:nbt") compile project(":minecraft:minecraft-text") - compile project(":natives") compile project(":primitive") + //TODO: i'm only using LoadingCache, figure out a way to not have to pull in all of guava for that one feature compile "com.google.guava:guava:$guavaVersion" } diff --git a/minecraft/src/main/java/net/daporkchop/lib/minecraft/world/format/anvil/AnvilSaveFormat.java b/minecraft/src/main/java/net/daporkchop/lib/minecraft/world/format/anvil/AnvilSaveFormat.java index 00b6e00d2..b9badeb7d 100644 --- a/minecraft/src/main/java/net/daporkchop/lib/minecraft/world/format/anvil/AnvilSaveFormat.java +++ b/minecraft/src/main/java/net/daporkchop/lib/minecraft/world/format/anvil/AnvilSaveFormat.java @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -17,8 +17,6 @@ import lombok.Getter; import lombok.NonNull; -import net.daporkchop.lib.common.misc.Tuple; -import net.daporkchop.lib.encoding.compression.Compression; import net.daporkchop.lib.minecraft.registry.IDRegistry; import net.daporkchop.lib.minecraft.registry.IDRegistryBuilder; import net.daporkchop.lib.minecraft.registry.ResourceLocation; @@ -29,18 +27,16 @@ import net.daporkchop.lib.minecraft.world.format.WorldManager; import net.daporkchop.lib.nbt.NBTInputStream; import net.daporkchop.lib.nbt.tag.notch.CompoundTag; -import net.daporkchop.lib.nbt.tag.notch.IntTag; import net.daporkchop.lib.nbt.tag.notch.ListTag; -import net.daporkchop.lib.nbt.tag.notch.StringTag; import net.daporkchop.lib.primitive.function.biconsumer.IntObjBiConsumer; import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.util.Arrays; import java.util.function.BiConsumer; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.zip.GZIPInputStream; /** * @author DaPorkchop_ @@ -80,7 +76,7 @@ public void init(@NonNull MinecraftSave save) throws IOException { } else { throw new UnsupportedOperationException("create world"); } - try (NBTInputStream is = new NBTInputStream(new FileInputStream(levelDat_file), Compression.GZIP_NORMAL)) { + try (NBTInputStream is = new NBTInputStream(new GZIPInputStream(new FileInputStream(levelDat_file)))) { this.levelDat = is.readTag(); } } diff --git a/minecraft/src/main/java/net/daporkchop/lib/minecraft/world/format/anvil/AnvilWorldManager.java b/minecraft/src/main/java/net/daporkchop/lib/minecraft/world/format/anvil/AnvilWorldManager.java index 10a9b216a..8d2c46953 100644 --- a/minecraft/src/main/java/net/daporkchop/lib/minecraft/world/format/anvil/AnvilWorldManager.java +++ b/minecraft/src/main/java/net/daporkchop/lib/minecraft/world/format/anvil/AnvilWorldManager.java @@ -28,6 +28,8 @@ import net.daporkchop.lib.common.cache.Cache; import net.daporkchop.lib.common.cache.ThreadCache; import net.daporkchop.lib.common.misc.file.PFiles; +import net.daporkchop.lib.compression.PInflater; +import net.daporkchop.lib.compression.zlib.Zlib; import net.daporkchop.lib.math.vector.i.Vec2i; import net.daporkchop.lib.minecraft.registry.ResourceLocation; import net.daporkchop.lib.minecraft.tileentity.TileEntity; @@ -41,9 +43,6 @@ import net.daporkchop.lib.minecraft.world.impl.section.DirectSectionImpl; import net.daporkchop.lib.minecraft.world.impl.section.HeapSectionImpl; import net.daporkchop.lib.minecraft.world.impl.vanilla.VanillaChunkImpl; -import net.daporkchop.lib.natives.PNatives; -import net.daporkchop.lib.natives.zlib.PInflater; -import net.daporkchop.lib.natives.zlib.Zlib; import net.daporkchop.lib.nbt.NBTInputStream; import net.daporkchop.lib.nbt.alloc.NBTArrayAllocator; import net.daporkchop.lib.nbt.tag.notch.CompoundTag; @@ -68,7 +67,7 @@ public class AnvilWorldManager implements WorldManager { protected static final Cache CHUNK_CACHE = ThreadCache.soft(() -> new HeapSectionImpl(-1, null)); protected static final Pattern REGION_PATTERN = Pattern.compile("^r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mca$"); - protected static final Cache INFLATER_CACHE = ThreadCache.soft(() -> PNatives.ZLIB.get().inflater(Zlib.ZLIB_MODE_AUTO)); + protected static final Cache INFLATER_CACHE = ThreadCache.soft(() -> Zlib.PROVIDER.inflater(Zlib.MODE_AUTO)); protected final AnvilSaveFormat format; protected final File root; @@ -145,7 +144,7 @@ public void loadColumn(Chunk chunk) { PInflater inflater = INFLATER_CACHE.get(); ByteBuf uncompressed = PooledByteBufAllocator.DEFAULT.directBuffer(); try { - inflater.inflate(compressed, uncompressed); + inflater.fullInflateGrowing(compressed, uncompressed); try (NBTInputStream in = new NBTInputStream(DataIn.wrap(uncompressed), this.arrayAllocator)) { rootTag = in.readTag().getCompound("Level"); } @@ -245,26 +244,27 @@ private void loadSection(@NonNull HeapSectionImpl impl, @NonNull CompoundTag tag } private void loadSection(@NonNull DirectSectionImpl impl, @NonNull CompoundTag tag) { - final long addr = impl.memoryAddress(); + final Object ref = impl.memoryRef(); + final long addr = impl.memoryOff(); PUnsafe.copyMemory( tag.getByteArray("Data"), PUnsafe.ARRAY_BYTE_BASE_OFFSET, - null, + ref, addr + DirectSectionImpl.OFFSET_META, DirectSectionImpl.SIZE_NIBBLE_LAYER ); PUnsafe.copyMemory( tag.getByteArray("BlockLight"), PUnsafe.ARRAY_BYTE_BASE_OFFSET, - null, + ref, addr + DirectSectionImpl.OFFSET_BLOCK_LIGHT, DirectSectionImpl.SIZE_NIBBLE_LAYER ); PUnsafe.copyMemory( tag.getByteArray("SkyLight"), PUnsafe.ARRAY_BYTE_BASE_OFFSET, - null, + ref, addr + DirectSectionImpl.OFFSET_SKY_LIGHT, DirectSectionImpl.SIZE_NIBBLE_LAYER ); diff --git a/minecraft/src/main/java/net/daporkchop/lib/minecraft/world/format/anvil/region/RegionConstants.java b/minecraft/src/main/java/net/daporkchop/lib/minecraft/world/format/anvil/region/RegionConstants.java index ffc23d1c8..7dd18e413 100644 --- a/minecraft/src/main/java/net/daporkchop/lib/minecraft/world/format/anvil/region/RegionConstants.java +++ b/minecraft/src/main/java/net/daporkchop/lib/minecraft/world/format/anvil/region/RegionConstants.java @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -16,12 +16,6 @@ package net.daporkchop.lib.minecraft.world.format.anvil.region; import lombok.experimental.UtilityClass; -import net.daporkchop.lib.encoding.compression.Compression; -import net.daporkchop.lib.encoding.compression.CompressionHelper; -import net.daporkchop.lib.primitive.map.ByteObjMap; -import net.daporkchop.lib.primitive.map.ObjByteMap; -import net.daporkchop.lib.primitive.map.hash.open.ByteObjOpenHashMap; -import net.daporkchop.lib.primitive.map.hash.open.ObjByteOpenHashMap; /** * Constants used when interacting with region files. @@ -41,59 +35,8 @@ public class RegionConstants { public static final int VERSION_HEADER_SIZE = 1; public static final int CHUNK_HEADER_SIZE = LENGTH_HEADER_SIZE + VERSION_HEADER_SIZE; - /** - * A bitmask to identify non-official compression versions. - *

- * Any version containing this mask is unofficial, added by me. - */ - public static final byte PORKIAN_ID_MASK = (byte) 0x80; - public static final byte ID_GZIP = 1; //official, no longer used by vanilla public static final byte ID_ZLIB = 2; //official - public static final byte ID_NONE = PORKIAN_ID_MASK | 1; - public static final byte ID_BZIP2 = PORKIAN_ID_MASK | 2; - public static final byte ID_LZ4 = PORKIAN_ID_MASK | 3; - public static final byte ID_LZMA = PORKIAN_ID_MASK | 4; - public static final byte ID_XZ = PORKIAN_ID_MASK | 5; - - public static final ByteObjMap COMPRESSION_IDS = new ByteObjOpenHashMap<>(); - public static final ObjByteMap REVERSE_COMPRESSION_IDS = new ObjByteOpenHashMap<>(); - - public static final boolean DEBUG_SECTORS = false; - - static { - //id => compression algo - COMPRESSION_IDS.put(ID_NONE, Compression.NONE); - COMPRESSION_IDS.put(ID_GZIP, Compression.GZIP_NORMAL); - COMPRESSION_IDS.put(ID_ZLIB, Compression.DEFLATE_NORMAL); - COMPRESSION_IDS.put(ID_BZIP2, Compression.BZIP2_NORMAL); - COMPRESSION_IDS.put(ID_LZ4, Compression.LZ4_BLOCK); - COMPRESSION_IDS.put(ID_LZMA, Compression.LZMA_NORMAL); - COMPRESSION_IDS.put(ID_XZ, Compression.XZ_NORMAL); - - //compression algo => id - REVERSE_COMPRESSION_IDS.put(Compression.NONE, ID_NONE); - REVERSE_COMPRESSION_IDS.put(Compression.GZIP_LOW, ID_GZIP); - REVERSE_COMPRESSION_IDS.put(Compression.GZIP_NORMAL, ID_GZIP); - REVERSE_COMPRESSION_IDS.put(Compression.GZIP_HIGH, ID_GZIP); - REVERSE_COMPRESSION_IDS.put(Compression.DEFLATE_LOW, ID_ZLIB); - REVERSE_COMPRESSION_IDS.put(Compression.DEFLATE_NORMAL, ID_ZLIB); - REVERSE_COMPRESSION_IDS.put(Compression.DEFLATE_HIGH, ID_ZLIB); - REVERSE_COMPRESSION_IDS.put(Compression.BZIP2_LOW, ID_BZIP2); - REVERSE_COMPRESSION_IDS.put(Compression.BZIP2_NORMAL, ID_BZIP2); - REVERSE_COMPRESSION_IDS.put(Compression.BZIP2_HIGH, ID_BZIP2); - REVERSE_COMPRESSION_IDS.put(Compression.LZ4_BLOCK, ID_LZ4); - REVERSE_COMPRESSION_IDS.put(Compression.LZ4_FRAMED_64KB, ID_LZ4); - REVERSE_COMPRESSION_IDS.put(Compression.LZ4_FRAMED_256KB, ID_LZ4); - REVERSE_COMPRESSION_IDS.put(Compression.LZ4_FRAMED_1MB, ID_LZ4); - REVERSE_COMPRESSION_IDS.put(Compression.LZ4_FRAMED_4MB, ID_LZ4); - REVERSE_COMPRESSION_IDS.put(Compression.LZMA_LOW, ID_LZMA); - REVERSE_COMPRESSION_IDS.put(Compression.LZMA_NORMAL, ID_LZMA); - REVERSE_COMPRESSION_IDS.put(Compression.LZMA_HIGH, ID_LZMA); - REVERSE_COMPRESSION_IDS.put(Compression.XZ_LOW, ID_XZ); - REVERSE_COMPRESSION_IDS.put(Compression.XZ_NORMAL, ID_XZ); - REVERSE_COMPRESSION_IDS.put(Compression.XZ_HIGH, ID_XZ); - } public static void assertInBounds(int x, int z) { if (x < 0 || x >= 32 || z < 0 || z >= 32) { diff --git a/minecraft/src/main/java/net/daporkchop/lib/minecraft/world/format/anvil/region/RegionFile.java b/minecraft/src/main/java/net/daporkchop/lib/minecraft/world/format/anvil/region/RegionFile.java index 71b90d5d2..a3cfbe828 100644 --- a/minecraft/src/main/java/net/daporkchop/lib/minecraft/world/format/anvil/region/RegionFile.java +++ b/minecraft/src/main/java/net/daporkchop/lib/minecraft/world/format/anvil/region/RegionFile.java @@ -16,13 +16,7 @@ package net.daporkchop.lib.minecraft.world.format.anvil.region; import io.netty.buffer.ByteBuf; -import io.netty.buffer.PooledByteBufAllocator; import lombok.NonNull; -import net.daporkchop.lib.binary.stream.DataIn; -import net.daporkchop.lib.binary.stream.DataOut; -import net.daporkchop.lib.binary.stream.netty.NettyByteBufOut; -import net.daporkchop.lib.binary.stream.stream.StreamOut; -import net.daporkchop.lib.encoding.compression.CompressionHelper; import net.daporkchop.lib.minecraft.world.format.anvil.region.ex.CorruptedRegionException; import net.daporkchop.lib.minecraft.world.format.anvil.region.ex.ReadOnlyRegionException; import net.daporkchop.lib.minecraft.world.format.anvil.region.impl.BufferedRegionFile; @@ -31,8 +25,6 @@ import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; /** @@ -62,25 +54,6 @@ static RegionFile open(@NonNull File file, @NonNull RegionOpenOptions options) t throw new IllegalArgumentException(options.toString()); } - /** - * Gets an {@link InputStream} that can inflate and read the contents of the chunk at the given coordinates (relative to this region). - * - * @param x the chunk's X coordinate - * @param z the chunk's Z coordinate - * @return an {@link InputStream} that can inflate and read the contents of the chunk at the given coordinates, or {@code null} if the chunk is not present - * @throws IOException if an IO exception occurs you dummy - */ - default InputStream read(int x, int z) throws IOException { - ByteBuf buf = this.readDirect(x, z); - if (buf == null) { - return null; - } else { - DataIn in = DataIn.wrap(buf, true); - byte compressionId = buf.readByte(); - return compressionId == RegionConstants.ID_NONE ? in : RegionConstants.COMPRESSION_IDS.get(compressionId).inflate(in); - } - } - /** * Reads the raw contents of the chunk at the given coordinates into a buffer. *

@@ -93,67 +66,6 @@ default InputStream read(int x, int z) throws IOException { */ ByteBuf readDirect(int x, int z) throws IOException; - /** - * Gets a {@link DataOut} that will compress data written to it using the given compression type. The compressed data will be written to disk at the specified - * region-local chunk coordinates when the {@link DataOut} instance is closed (using {@link DataOut#close()}. - * - * @param x the chunk's X coordinate - * @param z the chunk's Z coordinate - * @param compression the type of compression to use - * @return a {@link DataOut} for writing data to the given chunk - * @throws ReadOnlyRegionException if the region is opened in read-only mode - * @throws IOException if an IO exception occurs you dummy - */ - default DataOut write(int x, int z, @NonNull CompressionHelper compression) throws ReadOnlyRegionException, IOException { - byte compressionId = RegionConstants.REVERSE_COMPRESSION_IDS.get(compression); - if (compressionId == -1) { - throw new IllegalArgumentException(String.format("Unregistered compression format: %s", compression)); - } else { - return this.write(x, z, compression, compressionId); - } - } - - /** - * Gets a {@link DataOut} that will compress data written to it using the given compression type. The compressed data will be written to disk at the specified - * region-local chunk coordinates when the {@link DataOut} instance is closed (using {@link DataOut#close()}. - * - * @param x the chunk's X coordinate - * @param z the chunk's Z coordinate - * @param compression the type of compression to use - * @param compressionId the compression's ID, for writing to disk for decompression - * @return a {@link DataOut} for writing data to the given chunk - * @throws ReadOnlyRegionException if the region is opened in read-only mode - * @throws IOException if an IO exception occurs you dummy - */ - default DataOut write(int x, int z, @NonNull CompressionHelper compression, byte compressionId) throws ReadOnlyRegionException, IOException { - RegionConstants.assertInBounds(x, z); - this.assertWritable(); - - ByteBuf buf = PooledByteBufAllocator.DEFAULT.ioBuffer(RegionConstants.SECTOR_BYTES << 2).writeInt(-1).writeByte(compressionId); - - OutputStream out = DataOut.wrap(buf); - OutputStream compressedOut = compression.deflate(out); - - if (out == compressedOut) { - //no compression will be applied, wrap buffer directly - return new NettyByteBufOut(buf) { - @Override - protected boolean handleClose(@NonNull ByteBuf buf) throws IOException { - RegionFile.this.writeDirect(x, z, this.buf.setInt(0, this.buf.readableBytes() - RegionConstants.LENGTH_HEADER_SIZE)); - return false; - } - }; - } else { - return new StreamOut(compressedOut) { - @Override - public void close() throws IOException { - this.out.close(); - RegionFile.this.writeDirect(x, z, buf.setInt(0, buf.readableBytes() - RegionConstants.LENGTH_HEADER_SIZE)); - } - }; - } - } - /** * Writes raw chunk data to the region at the given region-local coordinates. * diff --git a/minecraft/src/main/java/net/daporkchop/lib/minecraft/world/impl/section/DirectSectionImpl.java b/minecraft/src/main/java/net/daporkchop/lib/minecraft/world/impl/section/DirectSectionImpl.java index 87f051309..231f0cf91 100644 --- a/minecraft/src/main/java/net/daporkchop/lib/minecraft/world/impl/section/DirectSectionImpl.java +++ b/minecraft/src/main/java/net/daporkchop/lib/minecraft/world/impl/section/DirectSectionImpl.java @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -121,7 +121,12 @@ public void release() throws AlreadyReleasedException { } @Override - public long memoryAddress() throws AlreadyReleasedException { + public Object memoryRef() { + return null; + } + + @Override + public long memoryOff() throws AlreadyReleasedException { return this.addr; } diff --git a/natives/.gitignore b/natives/.gitignore new file mode 100644 index 000000000..36e665b17 --- /dev/null +++ b/natives/.gitignore @@ -0,0 +1,3 @@ +/*.tar.gz +/src/main/native/*/build*/ +/tmp/ diff --git a/natives/Makefile b/natives/Makefile index 0550fe4f4..1b478084f 100644 --- a/natives/Makefile +++ b/natives/Makefile @@ -1,44 +1,59 @@ export SHELL := /bin/bash -export GCC_VERSION := 8.1.0 + +export NPROC := $(shell nproc) export TARGET := $(shell basename $(CURDIR)) export TOPDIR := $(CURDIR) -export SRCDIR := $(TOPDIR)/src/main/native -export OUTDIR := $(TOPDIR)/src/main/resources +export TOOLCHAINS := $(CURDIR)/toolchain +export COMMONSRC := $(CURDIR)/src/main/native -export CFLAGS := -shared -Ofast -ffast-math -fPIC -ffunction-sections -fdata-sections -fvisibility=hidden -flto +export CFLAGS := -shared -Ofast -ffast-math -fPIC -ffunction-sections -fdata-sections -fvisibility=hidden export CXXFLAGS := $(CFLAGS) export LDFLAGS := $(CFLAGS) -Wl,--gc-sections -export SOURCES := $(SRCDIR)/common/source -export INCLUDES := $(SRCDIR)/common/include $(JAVA_HOME)include $(JAVA_HOME)include/linux +ifndef PORKLIB_NATIVES_DEBUG +export CFLAGS := $(CFLAGS) +export BUILD_TYPE := release +else +export CFLAGS := $(CFLAGS) -DPORKLIB_NATIVES_DEBUG +export BUILD_TYPE := debug +endif +$(info natives: building for $(BUILD_TYPE)) + +export SOURCES := +export INCLUDES := $(COMMONSRC)/include $(JAVA_HOME)include $(JAVA_HOME)include/linux -export ARCHS := x86_64-linux-gnu x86-linux-gnu x86_64-w64-mingw32 +export ARCHS := aarch64-linux-gnu x86_64-linux-gnu x86_64-w64-mingw32 #export ARCHS := $(foreach arch,$(ARCHS),$(if $(shell which $(arch)-gcc),$(arch))) #export ARCHS := $(foreach arch,$(ARCHS),$(if $(shell which $(arch)-g++),$(arch))) export ARCH_TASKS := $(foreach arch,$(ARCHS),build.$(arch)) -export MODULES := zlib +export MODULES := ../compression/zlib ../compression/zstd + +export LIBDIR := $(TOPDIR)/src/main/native/lib +export LIB_URL_BASE := https://cloud.daporkchop.net/programs/source/ +export LIBS := zlib-ng-1.2.11.tar.gz zstd-1.4.4.tar.gz -#.PHONY: build clean .FORCE $(ARCH_TASKS) $(foreach arch,$(ARCHS),$(foreach module,$(MODULES),$(arch)/$(module).lib)) .PHONY: build clean .FORCE -#build: $(ARCH_TASKS) $(foreach arch,$(ARCHS),$(foreach mod,$(MODULES),$(OUTDIR)/$(arch)/lib$(mod).so)) -build: $(ARCH_TASKS) +build: $(ARCH_TASKS) $(LIBS) -build.%: .FORCE $(foreach module,$(MODULES),%/$(module).lib) +build.%: .FORCE $(foreach module,$(MODULES),%,$(module).lib) @echo Built libraries for $(shell echo '$@' | perl -n -e '/build\.(.+)/ && print $$1')! -%.lib: .FORCE - @_PRJ_NAME=$(shell echo "$@" | perl -n -e '/\/([^.]+)\.lib$$/ && print $$1') && \ - _ARCH=$(shell echo "$@" | perl -n -e '/^([^\/]*?)\/[^.]+\.lib$$/ && print $$1') && \ - $(MAKE) -C $(SRCDIR)/$$_PRJ_NAME BUILD=$$_ARCH PROJDIR=$(SRCDIR)/$$_PRJ_NAME $$_ARCH && \ +%.lib: .FORCE $(LIBS) + @_PRJ_NAME=$(shell echo "$@" | perl -n -e '/,(.*?)\.lib$$/ && print $$1') && \ + _ARCH=$(shell echo "$@" | perl -n -e '/^([^,]*?),.*?\.lib$$/ && print $$1') && \ + $(MAKE) --no-print-directory -C $(TOPDIR)/$$_PRJ_NAME BUILD=$$_ARCH PROJDIR=$(TOPDIR)/$$_PRJ_NAME $$_ARCH && \ echo Built $$_PRJ_NAME for target $$_ARCH! -clean: $(foreach mod,$(MODULES),clean.$(mod)) - @rm -rf $(OUTDIR)/*/ +#echo "arg = $@" && echo "prj_name = $$_PRJ_NAME" && echo "arch = $$_ARCH" && \ + +clean: + @for f in $(MODULES); do $(MAKE) -C $(TOPDIR)/$$f clean; done -clean.%: - @$(MAKE) -C $(SRCDIR)/$(subst clean.,,$@) clean +%.tar.gz: + @echo "Downloading source for $@" + @curl -o $@ $(LIB_URL_BASE)$@ .FORCE: diff --git a/natives/build.gradle b/natives/build.gradle index 30b4f2ace..07b04257c 100644 --- a/natives/build.gradle +++ b/natives/build.gradle @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -18,7 +18,7 @@ dependencies { } if (System.getProperty("os.name", "").toLowerCase().replaceAll("[^a-z0-9]+", "").startsWith("windows")) { - System.out.println("You are running Windows, which is bad and you should feel bad.") + System.out.println("You are running Windows, which is bad and you should feel bad.\nThis means that native libraries will not be built.") } else { task compileNatives(type: Exec) { workingDir "$projectDir" diff --git a/natives/compile.sh b/natives/compile.sh index 17203fb4c..ea1919380 100644 --- a/natives/compile.sh +++ b/natives/compile.sh @@ -2,4 +2,8 @@ #i use this script simply so that i can add the natives compilation as a run configuration in intellij -make -j$( nproc ) +export PORKLIB_NATIVES_DEBUG="true" + +#make clean && \ +#make -j$( nproc ) +make -j$( nproc ) build.x86_64-linux-gnu diff --git a/natives/src/example/java/NativeTests.java b/natives/src/example/java/NativeTests.java deleted file mode 100644 index 9ff71683b..000000000 --- a/natives/src/example/java/NativeTests.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Adapted from the Wizardry License - * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors - * - * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. - * - * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. - * - * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import net.daporkchop.lib.common.util.PorkUtil; -import net.daporkchop.lib.natives.PNatives; -import net.daporkchop.lib.natives.zlib.PDeflater; -import net.daporkchop.lib.natives.zlib.PInflater; -import net.daporkchop.lib.natives.zlib.Zlib; - -import java.io.IOException; -import java.util.concurrent.ThreadLocalRandom; - -/** - * @author DaPorkchop_ - */ -public class NativeTests { - private static final int SIZE = 67108864; //64 MiB - - public static void main(String... args) throws IOException { - ByteBuf orig = Unpooled.directBuffer(SIZE).writerIndex(SIZE); - for (int i = 0; i < SIZE; i++) { - orig.setByte(i, ThreadLocalRandom.current().nextInt(8)); - } - ByteBuf compressed = Unpooled.directBuffer(SIZE >>> 3, SIZE).clear(); - - try (PDeflater deflater = PNatives.ZLIB.get().deflater(Zlib.ZLIB_LEVEL_BEST)) { - System.out.printf("Deflating with %s...\nFinished: %b\n", PorkUtil.className(deflater), deflater.finished()); - deflater.deflate(orig, compressed); - System.out.printf("Read: %d\nWritten: %d\nFinished: %b\n", deflater.readBytes(), deflater.writtenBytes(), deflater.finished()); - } - - ByteBuf decompressed = Unpooled.directBuffer(SIZE, SIZE).clear(); - - try (PInflater inflater = PNatives.ZLIB.get().inflater()) { - System.out.printf("Inflating with %s...\nFinished: %b\n", PorkUtil.className(inflater), inflater.finished()); - inflater.inflate(compressed, decompressed); - System.out.printf("Read: %d\nWritten: %d\nFinished: %b\n", inflater.readBytes(), inflater.writtenBytes(), inflater.finished()); - } - - for (int i = 0; i < SIZE; i++) { - if (orig.getByte(i) != decompressed.getByte(i)) { - throw new IllegalStateException(); - } - } - } -} diff --git a/natives/src/main/java/net/daporkchop/lib/natives/FeatureBuilder.java b/natives/src/main/java/net/daporkchop/lib/natives/FeatureBuilder.java new file mode 100644 index 000000000..3561cd0c8 --- /dev/null +++ b/natives/src/main/java/net/daporkchop/lib/natives/FeatureBuilder.java @@ -0,0 +1,83 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.natives; + +import lombok.AccessLevel; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import net.daporkchop.lib.common.util.PorkUtil; +import net.daporkchop.lib.natives.impl.Feature; +import net.daporkchop.lib.natives.impl.NativeFeature; +import net.daporkchop.lib.unsafe.PUnsafe; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.function.Supplier; + +/** + * A container around multiple implementations of a {@link Feature}. + *

+ * Serves mainly to automatically choose the best implementation to use, and to avoid unnecessary loading of implementation classes that won't actually end + * up being used. + * + * @param the type of the {@link Feature} to be implemented + * @author DaPorkchop_ + */ +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public final class FeatureBuilder> { + public static > FeatureBuilder create(@NonNull Class currentClass) { + return new FeatureBuilder<>(currentClass); + } + + private final Collection> implementations = new ArrayList<>(); + + @NonNull + private final Class currentClass; + + public FeatureBuilder addNative(@NonNull String className, @NonNull String libName) { + return this.addNative(className, libName, this.currentClass.getClassLoader()); + } + + public FeatureBuilder addNative(@NonNull String className, @NonNull String libName, @NonNull Class currentClass) { + return this.addNative(className, libName, currentClass.getClassLoader()); + } + + public FeatureBuilder addNative(@NonNull String className, @NonNull String libName, @NonNull ClassLoader loader) { + if (NativeFeature.AVAILABLE) { + this.implementations.add(() -> { + Class clazz = PorkUtil.uninitializedClassForName(className, loader); + return NativeFeature.loadNativeLibrary(libName, clazz) ? PUnsafe.allocateInstance(clazz) : null; + }); + } + return this; + } + + public FeatureBuilder addJava(@NonNull String className) { + this.implementations.add(() -> PUnsafe.allocateInstance(PorkUtil.uninitializedClassForName(className))); + return this; + } + + public F build() { + for (Supplier implementation : this.implementations) { + F value = implementation.get(); + if (value != null) { + return value; + } + } + + throw new IllegalStateException("No implementations could be loaded!"); + } +} diff --git a/natives/src/main/java/net/daporkchop/lib/natives/NativeCode.java b/natives/src/main/java/net/daporkchop/lib/natives/NativeCode.java deleted file mode 100644 index 5f96c8ca9..000000000 --- a/natives/src/main/java/net/daporkchop/lib/natives/NativeCode.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Adapted from the Wizardry License - * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors - * - * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. - * - * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. - * - * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -package net.daporkchop.lib.natives; - -import lombok.Getter; -import lombok.NonNull; -import lombok.experimental.Accessors; -import net.daporkchop.lib.common.system.Architecture; -import net.daporkchop.lib.common.system.OperatingSystem; -import net.daporkchop.lib.common.system.PlatformInfo; -import net.daporkchop.lib.unsafe.PUnsafe; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.function.Supplier; - -/** - * A wrapper around multiple distinct implementations of something. - * - * @param the type of the feature to be implemented - * @author DaPorkchop_ - */ -public final class NativeCode implements Supplier { - private static String LIB_ARCH; - private static String LIB_EXT; - - public static void loadNativeLibrary(@NonNull String name) { - if (!NativeImpl.AVAILABLE) { - throw new UnsupportedOperationException("native libraries are not available!"); - } else if (LIB_ARCH == null) { - synchronized (NativeCode.class) { - if (LIB_ARCH == null) { - switch (PlatformInfo.OPERATING_SYSTEM) { - case Linux: - LIB_EXT = "so"; - switch (PlatformInfo.ARCHITECTURE) { - case x86_64: - LIB_ARCH = "x86_64-linux-gnu"; - break; - case x86: - LIB_ARCH = "x86-linux-gnu"; - break; - } - break; - case Windows: - if (PlatformInfo.ARCHITECTURE == Architecture.x86_64) { - LIB_EXT = "dll"; - LIB_ARCH = "x86_64-w64-mingw32"; - } - break; - } - if (LIB_ARCH == null || LIB_EXT == null) { - throw new IllegalStateException(); - } - } - } - } - try { - File file = File.createTempFile(String.format("%s-%s-", name, LIB_ARCH), String.format(".%s", LIB_EXT)); - file.deleteOnExit(); - try (InputStream is = NativeCode.class.getResourceAsStream(String.format("/%s/lib%s.%s", LIB_ARCH, name, LIB_EXT)); - OutputStream os = new FileOutputStream(file)) { - byte[] arr = new byte[PUnsafe.pageSize()]; - for (int b; (b = is.read(arr)) >= 0; os.write(arr, 0, b)); - } - System.load(file.getAbsolutePath()); - } catch (Exception e) { - throw new RuntimeException(String.format("Unable to load library \"%s\"", name), e); - } - } - - private final Impl implementation; - - @SafeVarargs - public NativeCode(@NonNull Supplier>... implementationFactories) { - for (Supplier> implementationFactory : implementationFactories) { - Impl implementation = implementationFactory.get(); - if (implementation.available()) { - this.implementation = implementation; - return; - } - } - - throw new IllegalStateException("No implementations found!"); - } - - @Override - public T get() { - return this.implementation.get(); - } - - /** - * @return whether or not the currently used implementation is based on native code - */ - public boolean isNative() { - return this.implementation instanceof NativeImpl; - } - - /** - * An implementation for use by {@link NativeCode}. - * - * @param the type of the feature to be implemented - * @author DaPorkchop_ - */ - @Getter - @Accessors(fluent = true) - public static abstract class Impl implements Supplier { - protected final boolean available = this._available(); - - @Override - public T get() { - if (this.available) { - return this._get(); - } else { - throw new IllegalStateException("Not available!"); - } - } - - protected abstract T _get(); - - protected abstract boolean _available(); - } - - /** - * Extension of {@link Impl} for use by implementations that actually use native code. - *

- * Eliminates the boilerplate of checking if the current system is supported. - * - * @param the type of the feature to be implemented - * @author DaPorkchop_ - */ - public static abstract class NativeImpl extends Impl { - /** - * Whether or not native libraries are available. - */ - public static final boolean AVAILABLE = - ((PlatformInfo.ARCHITECTURE == Architecture.x86 || PlatformInfo.ARCHITECTURE == Architecture.x86_64) && PlatformInfo.OPERATING_SYSTEM == OperatingSystem.Linux) - || (PlatformInfo.ARCHITECTURE == Architecture.x86_64 && PlatformInfo.OPERATING_SYSTEM == OperatingSystem.Windows); - - @Override - protected boolean _available() { - return AVAILABLE; - } - } -} diff --git a/natives/src/main/java/net/daporkchop/lib/natives/NativeCodeException.java b/natives/src/main/java/net/daporkchop/lib/natives/NativeException.java similarity index 72% rename from natives/src/main/java/net/daporkchop/lib/natives/NativeCodeException.java rename to natives/src/main/java/net/daporkchop/lib/natives/NativeException.java index 7ba563a87..64656b9be 100644 --- a/natives/src/main/java/net/daporkchop/lib/natives/NativeCodeException.java +++ b/natives/src/main/java/net/daporkchop/lib/natives/NativeException.java @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -19,23 +19,32 @@ import lombok.experimental.Accessors; /** - * Thrown by native libraries when an exception occurs. + * Thrown when an exception occurs in native code. * * @author DaPorkchop_ */ @Getter @Accessors(fluent = true) -public final class NativeCodeException extends RuntimeException { - protected final int err; +public class NativeException extends RuntimeException { + protected final long code; - public NativeCodeException(String message, int err) { + public NativeException(String message) { + this(message, 0L); + } + + public NativeException(String message, int code) { + this(message, (long) code); + } + + public NativeException(String message, long code) { super(message); - this.err = err; + this.code = code; } @Override public String getMessage() { - return String.format("%d: %s", this.err, super.getMessage()); + String message = super.getMessage(); + return message == null ? String.valueOf(this.code) : this.code + ": " + message; } } diff --git a/natives/src/main/java/net/daporkchop/lib/natives/impl/Feature.java b/natives/src/main/java/net/daporkchop/lib/natives/impl/Feature.java new file mode 100644 index 000000000..6a9cb0226 --- /dev/null +++ b/natives/src/main/java/net/daporkchop/lib/natives/impl/Feature.java @@ -0,0 +1,26 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.natives.impl; + +/** + * @author DaPorkchop_ + */ +public interface Feature> { + /** + * @return whether or not this feature implementation is native + */ + boolean isNative(); +} diff --git a/natives/src/main/java/net/daporkchop/lib/natives/impl/NativeFeature.java b/natives/src/main/java/net/daporkchop/lib/natives/impl/NativeFeature.java new file mode 100644 index 000000000..22432706c --- /dev/null +++ b/natives/src/main/java/net/daporkchop/lib/natives/impl/NativeFeature.java @@ -0,0 +1,101 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.natives.impl; + +import lombok.NonNull; +import net.daporkchop.lib.common.system.PlatformInfo; +import net.daporkchop.lib.common.util.PorkUtil; +import net.daporkchop.lib.unsafe.PUnsafe; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Abstraction of an implementation of a {@link Feature} using native code. + * + * @author DaPorkchop_ + */ +public abstract class NativeFeature> implements Feature { + private static final String LIB_FMT; + private static final String LIB_EXT; + + public static final boolean AVAILABLE; + + static { + //these are the platforms that we compile native libraries for + switch (PlatformInfo.OPERATING_SYSTEM) { + case Linux: + switch (PlatformInfo.ARCHITECTURE) { + case AARCH64: + LIB_FMT = "/aarch64-linux-gnu/lib"; + break; + case x86_64: + LIB_FMT = "/x86_64-linux-gnu/lib"; + break; + default: + LIB_FMT = null; + } + LIB_EXT = LIB_FMT == null ? null : ".so"; + break; + case Windows: + switch (PlatformInfo.ARCHITECTURE) { + case x86_64: + LIB_FMT = "/x86_64-w64-mingw32/lib"; + break; + default: + LIB_FMT = null; + } + LIB_EXT = LIB_FMT == null ? null : ".dll"; + break; + default: + LIB_EXT = LIB_FMT = null; + } + + AVAILABLE = LIB_FMT != null; + } + + public static boolean loadNativeLibrary(@NonNull String name, @NonNull Class clazz) { + if (!NativeFeature.AVAILABLE) { + return false; + } + + try (InputStream is = clazz.getResourceAsStream(LIB_FMT + name + LIB_EXT)) { + if (is == null) { + //library doesn't exist + return false; + } + + File file = File.createTempFile(name, LIB_EXT); + file.deleteOnExit(); + try (OutputStream os = new FileOutputStream(file)) { + byte[] arr = new byte[PUnsafe.pageSize()]; + for (int b; (b = is.read(arr)) >= 0; os.write(arr, 0, b)) ; + } + System.load(file.getAbsolutePath()); + } catch (Exception e) { + throw new RuntimeException(String.format("Unable to load library \"%s\"", name), e); + } + + return true; + } + + @Override + public boolean isNative() { + return true; + } +} diff --git a/natives/src/main/java/net/daporkchop/lib/natives/util/BufferTyped.java b/natives/src/main/java/net/daporkchop/lib/natives/util/BufferTyped.java new file mode 100644 index 000000000..fbf4e91b4 --- /dev/null +++ b/natives/src/main/java/net/daporkchop/lib/natives/util/BufferTyped.java @@ -0,0 +1,64 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.natives.util; + +import io.netty.buffer.ByteBuf; +import lombok.NonNull; +import net.daporkchop.lib.natives.util.exception.InvalidBufferTypeException; + +/** + * A type with method(s) that accept {@link ByteBuf}(s) as parameters, but are restricted to either direct buffers only or heap buffers only. Attempting to + * pass a {@link ByteBuf} of an unsupported type to any superclass methods will result in an {@link InvalidBufferTypeException}. + * + * @author DaPorkchop_ + */ +public interface BufferTyped { + /** + * @return whether this implementation accepts direct buffers + */ + boolean directAccepted(); + + /** + * @return whether this implementation accepts heap buffers + */ + default boolean heapAccepted() { + return !this.directAccepted(); + } + + /** + * Checks whether the given {@link ByteBuf} is accepted by this implementation. + * + * @param buf the {@link ByteBuf} to check + * @return whether or not the given {@link ByteBuf} is accepted + */ + default boolean isAcceptable(@NonNull ByteBuf buf) { + return (this.directAccepted() && buf.hasMemoryAddress()) || (this.heapAccepted() && buf.hasArray()); + } + + /** + * Ensures that the given {@link ByteBuf} will be accepted by this implementation. + * + * @param buf the {@link ByteBuf} to check + * @return the {@link ByteBuf} + * @throws InvalidBufferTypeException if the given {@link ByteBuf} is not acceptable + */ + default ByteBuf assertAcceptable(@NonNull ByteBuf buf) throws InvalidBufferTypeException { + if (!this.isAcceptable(buf)) { + throw InvalidBufferTypeException.of(this.directAccepted(), this.heapAccepted()); + } + return buf; + } +} diff --git a/natives/src/main/java/net/daporkchop/lib/natives/util/exception/InvalidBufferTypeException.java b/natives/src/main/java/net/daporkchop/lib/natives/util/exception/InvalidBufferTypeException.java new file mode 100644 index 000000000..289290bd3 --- /dev/null +++ b/natives/src/main/java/net/daporkchop/lib/natives/util/exception/InvalidBufferTypeException.java @@ -0,0 +1,51 @@ +/* + * Adapted from the Wizardry License + * + * Copyright (c) 2018-2020 DaPorkchop_ and contributors + * + * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. + * + * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. + * + * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package net.daporkchop.lib.natives.util.exception; + +/** + * Thrown when a {@link io.netty.buffer.ByteBuf} argument to an implementation of {@link net.daporkchop.lib.natives.util.BufferTyped} is of the wrong type. + * + * @author DaPorkchop_ + */ +public final class InvalidBufferTypeException extends IllegalArgumentException { + public static InvalidBufferTypeException direct() { + return new InvalidBufferTypeException("direct buffer expected!"); + } + + public static InvalidBufferTypeException heap() { + return new InvalidBufferTypeException("heap buffer expected!"); + } + + public static InvalidBufferTypeException any() { + return new InvalidBufferTypeException("either direct or heap buffer expected!"); + } + + public static InvalidBufferTypeException of(boolean directAccepted, boolean heapAccepted) { + if (directAccepted && heapAccepted) { + return any(); + } else if (directAccepted) { + return direct(); + } else if (heapAccepted) { + return heap(); + } else { + throw new IllegalArgumentException("neither direct nor heap buffers are accepted?!?"); + } + } + + private InvalidBufferTypeException(String s) { + super(s); + } +} diff --git a/natives/src/main/java/net/daporkchop/lib/natives/zlib/JavaDeflater.java b/natives/src/main/java/net/daporkchop/lib/natives/zlib/JavaDeflater.java deleted file mode 100644 index 2c59bab51..000000000 --- a/natives/src/main/java/net/daporkchop/lib/natives/zlib/JavaDeflater.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Adapted from the Wizardry License - * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors - * - * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. - * - * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. - * - * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -package net.daporkchop.lib.natives.zlib; - -import lombok.Getter; -import lombok.experimental.Accessors; -import net.daporkchop.lib.unsafe.PUnsafe; -import net.daporkchop.lib.unsafe.util.exception.AlreadyReleasedException; - -import java.util.zip.Deflater; - -/** - * Implementation of {@link PDeflater} in pure Java. - * - * @author DaPorkchop_ - */ -@Accessors(fluent = true) -public final class JavaDeflater implements PDeflater { - private long inputAddr; - private long outputAddr; - - private final Deflater deflater; - private byte[] inputBuffer = new byte[8192]; - private byte[] outputBuffer = new byte[8192]; - - @Getter - private int readBytes; - @Getter - private int writtenBytes; - - private int inputSize; - private int outputSize; - - public JavaDeflater(int level, int mode) { - switch (mode) { - case Zlib.ZLIB_MODE_ZLIB: - this.deflater = new Deflater(level, false); - break; - case Zlib.ZLIB_MODE_RAW: - this.deflater = new Deflater(level, true); - break; - case Zlib.ZLIB_MODE_AUTO: - case Zlib.ZLIB_MODE_GZIP: - throw new IllegalArgumentException("Pure Java deflater doesn't support Gzip!"); - default: - throw new IllegalArgumentException(String.format("Invalid mode: %d", mode)); - } - } - - @Override - public void input(long addr, int size) { - this.inputAddr = addr; - this.inputSize = size; - } - - @Override - public void output(long addr, int size) { - this.outputAddr = addr; - this.outputSize = size; - } - - @Override - public void deflate(boolean finish) { - if (finish && this.inputSize <= this.inputBuffer.length) { - this.deflater.finish(); - } - - long prevBytesRead = this.deflater.getBytesRead(); - int inputCount = Math.min(this.inputSize, this.inputBuffer.length); - if (inputCount > 0) { - PUnsafe.copyMemory(null, this.inputAddr, this.inputBuffer, PUnsafe.ARRAY_BYTE_BASE_OFFSET, inputCount); - } - this.deflater.setInput(this.inputBuffer, 0, inputCount); - int written = this.deflater.deflate(this.outputBuffer, 0, Math.min(this.outputSize, this.outputBuffer.length)); - if (written > 0) { - PUnsafe.copyMemory(this.outputBuffer, PUnsafe.ARRAY_BYTE_BASE_OFFSET, null, this.outputAddr, written); - } - this.readBytes = (int) (this.deflater.getBytesRead() - prevBytesRead); - this.writtenBytes = written; - - this.inputAddr += this.readBytes; - this.inputSize -= this.readBytes; - this.outputAddr += written; - this.outputAddr -= written; - } - - @Override - public boolean finished() { - return this.deflater.finished(); - } - - @Override - public void reset() { - this.deflater.reset(); - this.inputAddr = this.outputAddr = 0L; - this.inputSize = this.outputSize = 0; - this.readBytes = this.writtenBytes = 0; - } - - @Override - public void release() throws AlreadyReleasedException { - this.deflater.end(); - this.inputBuffer = this.outputBuffer = null; - } -} diff --git a/natives/src/main/java/net/daporkchop/lib/natives/zlib/JavaInflater.java b/natives/src/main/java/net/daporkchop/lib/natives/zlib/JavaInflater.java deleted file mode 100644 index da2958fbb..000000000 --- a/natives/src/main/java/net/daporkchop/lib/natives/zlib/JavaInflater.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Adapted from the Wizardry License - * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors - * - * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. - * - * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. - * - * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -package net.daporkchop.lib.natives.zlib; - -import lombok.Getter; -import lombok.experimental.Accessors; -import net.daporkchop.lib.unsafe.PUnsafe; -import net.daporkchop.lib.unsafe.util.exception.AlreadyReleasedException; - -import java.util.zip.DataFormatException; -import java.util.zip.Inflater; - -/** - * Implementation of {@link PInflater} in pure Java. - * - * @author DaPorkchop_ - */ -@Accessors(fluent = true) -public final class JavaInflater implements PInflater { - private long inputAddr; - private long outputAddr; - - private final Inflater inflater; - private byte[] inputBuffer = new byte[8192]; - private byte[] outputBuffer = new byte[8192]; - - @Getter - private int readBytes; - @Getter - private int writtenBytes; - - private int inputSize; - private int outputSize; - - public JavaInflater(int mode) { - switch (mode) { - case Zlib.ZLIB_MODE_ZLIB: - this.inflater = new Inflater(false); - break; - case Zlib.ZLIB_MODE_RAW: - this.inflater = new Inflater(true); - break; - case Zlib.ZLIB_MODE_AUTO: - case Zlib.ZLIB_MODE_GZIP: - throw new IllegalArgumentException("Pure Java inflater doesn't support Gzip!"); - default: - throw new IllegalArgumentException(String.format("Invalid mode: %d", mode)); - } - } - - @Override - public void input(long addr, int size) { - this.inputAddr = addr; - this.inputSize = size; - } - - @Override - public void output(long addr, int size) { - this.outputAddr = addr; - this.outputSize = size; - } - - @Override - public void inflate() { - long prevBytesRead = this.inflater.getBytesRead(); - int inputCount = Math.min(this.inputSize, this.inputBuffer.length); - if (inputCount > 0) { - PUnsafe.copyMemory(null, this.inputAddr, this.inputBuffer, PUnsafe.ARRAY_BYTE_BASE_OFFSET, inputCount); - } - this.inflater.setInput(this.inputBuffer, 0, inputCount); - try { - int written = this.inflater.inflate(this.outputBuffer, 0, Math.min(this.outputSize, this.outputBuffer.length)); - if (written > 0) { - PUnsafe.copyMemory(this.outputBuffer, PUnsafe.ARRAY_BYTE_BASE_OFFSET, null, this.outputAddr, written); - } - this.readBytes = (int) (this.inflater.getBytesRead() - prevBytesRead); - this.writtenBytes = written; - - this.inputAddr += this.readBytes; - this.inputSize -= this.readBytes; - this.outputAddr += written; - this.outputAddr -= written; - } catch (DataFormatException e) { - throw new RuntimeException(e); - } - } - - @Override - public boolean finished() { - return this.inflater.finished(); - } - - @Override - public void reset() { - this.inflater.reset(); - this.inputAddr = this.outputAddr = 0L; - this.inputSize = this.outputSize = 0; - this.readBytes = this.writtenBytes = 0; - } - - @Override - public void release() throws AlreadyReleasedException { - this.inflater.end(); - this.inputBuffer = this.outputBuffer = null; - } -} diff --git a/natives/src/main/java/net/daporkchop/lib/natives/zlib/PDeflater.java b/natives/src/main/java/net/daporkchop/lib/natives/zlib/PDeflater.java deleted file mode 100644 index 7a160dc04..000000000 --- a/natives/src/main/java/net/daporkchop/lib/natives/zlib/PDeflater.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Adapted from the Wizardry License - * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors - * - * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. - * - * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. - * - * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -package net.daporkchop.lib.natives.zlib; - -import io.netty.buffer.ByteBuf; -import lombok.NonNull; -import net.daporkchop.lib.unsafe.capability.Releasable; - -/** - * Used for creating a Zlib-compressed data stream. - * - * @author DaPorkchop_ - */ -public interface PDeflater extends Releasable { - /** - * Does the entire compression process in one go. - *

- * This method ignores (and overwrites) any values set by {@link #input(long, int)} or {@link #output(long, int)}. - *

- * Calling this method will update the values of {@link #readBytes()} and {@link #writtenBytes()}, however the values will (probably) be garbage. - *

- * Attempting to use this method in combination with any of the following methods between resets may result in undefined behavior: - * - {@link #deflate(boolean)} - * - {@link #finished()} - * - * @param input a {@link ByteBuf} containing the input data to be compressed - * @param output a {@link ByteBuf} that the output data will be written to - */ - default void deflate(@NonNull ByteBuf input, @NonNull ByteBuf output) { - if (!input.isReadable()) { - throw new IllegalStateException("Input not readable!"); - } - this.input(input.memoryAddress() + input.readerIndex(), input.readableBytes()); //we don't need to set this in a loop - - do { - //System.out.printf("readable: %d, writable: %d\n", input.readableBytes(), output.writableBytes()); - this.output(output.memoryAddress() + output.writerIndex(), output.writableBytes()); - - this.deflate(true); - - input.skipBytes(this.readBytes()); - output.writerIndex(output.writerIndex() + this.writtenBytes()); - } while (!this.finished() && output.ensureWritable(8192).isWritable()); - - //System.out.printf("Done! readable: %d, writable: %d\n", input.readableBytes(), output.writableBytes()); - } - - /** - * Sets the input (source) data to be compressed. - * - * @param addr the base address of the data to be compressed - * @param size the size of the data to be compressed - */ - void input(long addr, int size); - - /** - * Sets the output (destination) where compressed data will be written to. - * - * @param addr the base address of the destination - * @param size the maximum size of the output data - */ - void output(long addr, int size); - - /** - * Does the actual data compression, blocking until either the input buffer is empty or the output buffer is full. - *

- * This method requires {@link #input(long, int)} and {@link #output(long, int)} to be set. - *

- * Calling this method will update the values of {@link #readBytes()} and {@link #writtenBytes()}. - *

- * Attempting to use this method in combination with {@link #deflate(ByteBuf, ByteBuf)} between resets may result in undefined behavior. - * - * @param finish if {@code true}, this will attempt to read all bytes from the input buffer and then finish the compression process. Even if this - * parameter is set, {@link #finished()} will not be set to {@code true} if the output buffer fills up. - */ - void deflate(boolean finish); - - /** - * @return whether or not the current deflation process is complete - */ - boolean finished(); - - /** - * @return the number of bytes read from the input buffer during the last invocation of {@link #deflate(boolean)} - */ - int readBytes(); - - /** - * @return the number of bytes written to the output buffer during the last invocation of {@link #deflate(boolean)} - */ - int writtenBytes(); - - /** - * Resets this {@link PDeflater} instance. - *

- * This must be called after the compression process is completed if this instance should be re-used (but doesn't have to be, it can also be immediately - * released or left to be garbage collected). - */ - void reset(); -} diff --git a/natives/src/main/java/net/daporkchop/lib/natives/zlib/PInflater.java b/natives/src/main/java/net/daporkchop/lib/natives/zlib/PInflater.java deleted file mode 100644 index dc9d61ee8..000000000 --- a/natives/src/main/java/net/daporkchop/lib/natives/zlib/PInflater.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Adapted from the Wizardry License - * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors - * - * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. - * - * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. - * - * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -package net.daporkchop.lib.natives.zlib; - -import io.netty.buffer.ByteBuf; -import lombok.NonNull; -import net.daporkchop.lib.unsafe.capability.Releasable; - -/** - * Used for inflating a Zlib-compressed data stream. - * - * @author DaPorkchop_ - */ -public interface PInflater extends Releasable { - /** - * Does the entire decompression process in one go. - *

- * This method ignores (and overwrites) any values set by {@link #input(long, int)} or {@link #output(long, int)}. - *

- * Calling this method will update the values of {@link #readBytes()} and {@link #writtenBytes()}, however the values will (probably) be garbage. - *

- * Attempting to use this method in combination with any of the following methods between resets may result in undefined behavior: - * - {@link #inflate()} - * - {@link #finished()} - * - * @param input a {@link ByteBuf} containing the input data to be decompressed - * @param output a {@link ByteBuf} that the output data will be written to - */ - default void inflate(@NonNull ByteBuf input, @NonNull ByteBuf output) { - if (!input.isReadable()) { - throw new IllegalStateException("Input not readable!"); - } - this.input(input.memoryAddress() + input.readerIndex(), input.readableBytes()); //we don't need to set this in a loop - - do { - this.output(output.memoryAddress() + output.writerIndex(), output.writableBytes()); - - this.inflate(); - - input.skipBytes(this.readBytes()); - output.writerIndex(output.writerIndex() + this.writtenBytes()); - } while (!this.finished() && output.ensureWritable(8192).isWritable()); - } - - /** - * Sets the input (source) data to be decompressed. - * - * @param addr the base address of the data to be decompressed - * @param size the size of the data to be decompressed - */ - void input(long addr, int size); - - /** - * Sets the output (destination) where decompressed data will be written to. - * - * @param addr the base address of the destination - * @param size the maximum size of the output data - */ - void output(long addr, int size); - - /** - * Does the actual data decompression, blocking until either the input buffer is empty or the output buffer is full. - *

- * This method requires {@link #input(long, int)} and {@link #output(long, int)} to be set. - *

- * Calling this method will update the values of {@link #readBytes()} and {@link #writtenBytes()}. - *

- * Attempting to use this method in combination with {@link #inflate(ByteBuf, ByteBuf)} between resets may result in undefined behavior. - */ - void inflate(); - - /** - * @return whether or not the current deflation process is complete - */ - boolean finished(); - - /** - * @return the number of bytes read from the input buffer during the last invocation of {@link #inflate()} - */ - int readBytes(); - - /** - * @return the number of bytes written to the output buffer during the last invocation of {@link #inflate()} - */ - int writtenBytes(); - - /** - * Resets this {@link PInflater} instance. - *

- * This must be called after the decompression process is completed if this instance should be re-used (but doesn't have to be, it can also be immediately - * released or left to be garbage collected). - */ - void reset(); -} diff --git a/natives/src/main/java/net/daporkchop/lib/natives/zlib/Zlib.java b/natives/src/main/java/net/daporkchop/lib/natives/zlib/Zlib.java deleted file mode 100644 index ee4feaa69..000000000 --- a/natives/src/main/java/net/daporkchop/lib/natives/zlib/Zlib.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Adapted from the Wizardry License - * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors - * - * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. - * - * The persons and/or organizations are also disallowed from sub-licensing and/or trademarking this software without explicit permission from DaPorkchop_. - * - * Any persons and/or organizations using this software must disclose their source code and have it publicly available, include this license, provide sufficient credit to the original authors of the project (IE: DaPorkchop_), as well as provide a link to the original project. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -package net.daporkchop.lib.natives.zlib; - -/** - * Base representation of the Zlib algorithm. - * - * @author DaPorkchop_ - */ -public interface Zlib { - /** - * No compression will be applied. - */ - int ZLIB_LEVEL_NONE = 0; - - /** - * Poorest compression ratio in exchange for fastest speeds. - */ - int ZLIB_LEVEL_FASTEST = 1; - - /** - * Best compression ratio in exchange for lowest speeds. - */ - int ZLIB_LEVEL_BEST = 9; - - /** - * Allows the zlib library to choose a default compression level. - */ - int ZLIB_LEVEL_DEFAULT = -1; - - /** - * Zlib wrapping mode, uses a 6-byte header with an Adler32 checksum. - *

- * This is the default mode. - */ - int ZLIB_MODE_ZLIB = 0; - - /** - * Gzip wrapping mode, uses an (at least) 18-byte header with a CRC32 checksum. - */ - int ZLIB_MODE_GZIP = 1; - - /** - * Only for use by {@link PInflater}, automatically detects whether the input data is in the Zlib or Gzip format. - *

- * Note that this doesn't work for data encoded in the {@link #ZLIB_MODE_RAW} format. - */ - int ZLIB_MODE_AUTO = 2; - - /** - * "Raw" Deflate mode, no wrapping is applied to the compressed data. - */ - int ZLIB_MODE_RAW = 3; - - /** - * Creates a new {@link PDeflater}. - *

- * The returned {@link PDeflater} will use {@link #ZLIB_MODE_ZLIB}. - * - * @param level the Deflate compression level to use. Must be in range 0-9 (inclusive), or {@link #ZLIB_LEVEL_DEFAULT} to use the library default - * @return a new {@link PDeflater} instance - * @see #deflater(int, int) - */ - default PDeflater deflater(int level) { - return this.deflater(level, ZLIB_MODE_ZLIB); - } - - /** - * Creates a new {@link PDeflater}. - * - * @param level the Deflate compression level to use. Must be in range 0-9 (inclusive), or {@link #ZLIB_LEVEL_DEFAULT} to use the library default - * @param mode the zlib wrapping mode to use. Must be one of {@link #ZLIB_MODE_ZLIB}, {@link #ZLIB_MODE_RAW} or {@link #ZLIB_MODE_GZIP} - * @return a new {@link PDeflater} instance - */ - PDeflater deflater(int level, int mode); - - /** - * Creates a new {@link PInflater}. - *

- * The returned {@link PInflater} will use {@link #ZLIB_MODE_ZLIB}. - * - * @return a new {@link PInflater} instance - * @see #inflater(int) - */ - default PInflater inflater() { - return this.inflater(ZLIB_MODE_ZLIB); - } - - /** - * Creates a new {@link PDeflater}. - * - * @param mode the zlib wrapping mode to use. Must be one of {@link #ZLIB_MODE_ZLIB}, {@link #ZLIB_MODE_GZIP}, {@link #ZLIB_MODE_AUTO} or {@link #ZLIB_MODE_RAW} - * @return a new {@link PInflater} instance - */ - PInflater inflater(int mode); -} diff --git a/natives/src/main/native/common/common.cpp b/natives/src/main/native/common/common.cpp new file mode 100644 index 000000000..91b36a083 --- /dev/null +++ b/natives/src/main/native/common/common.cpp @@ -0,0 +1,41 @@ +#include + +jint throwException(JNIEnv* env, const char* msg) { + jclass clazz = env->FindClass("net/daporkchop/lib/natives/NativeException"); + + return env->Throw((jthrowable) env->NewObject( + clazz, + env->GetMethodID(clazz, "", "(Ljava/lang/String;)V"), + env->NewStringUTF(msg) + )); +} + +jint throwException(JNIEnv* env, const char* msg, jint err) { + jclass clazz = env->FindClass("net/daporkchop/lib/natives/NativeException"); + + return env->Throw((jthrowable) env->NewObject( + clazz, + env->GetMethodID(clazz, "", "(Ljava/lang/String;I)V"), + env->NewStringUTF(msg), + err + )); +} + +jint throwException(JNIEnv* env, const char* msg, jlong err) { + jclass clazz = env->FindClass("net/daporkchop/lib/natives/NativeException"); + + return env->Throw((jthrowable) env->NewObject( + clazz, + env->GetMethodID(clazz, "", "(Ljava/lang/String;J)V"), + env->NewStringUTF(msg), + err + )); +} + +jlong max_l(jlong a, jlong b) { + return a > b ? a : b; +} + +jlong min_l(jlong a, jlong b) { + return a < b ? a : b; +} diff --git a/natives/src/main/native/common/include/common.h b/natives/src/main/native/common/include/common.h deleted file mode 100644 index 9aaf51f64..000000000 --- a/natives/src/main/native/common/include/common.h +++ /dev/null @@ -1,7 +0,0 @@ -#include - -jint throwException(JNIEnv* env, const char* msg, int err); - -jlong max_l(jlong a, jlong b); - -jlong min_l(jlong a, jlong b); diff --git a/natives/src/main/native/common/source/common.cpp b/natives/src/main/native/common/source/common.cpp deleted file mode 100644 index 6959ca7fe..000000000 --- a/natives/src/main/native/common/source/common.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include - -jint throwException(JNIEnv* env, const char* msg, int err) { - jclass clazz = env->FindClass("net/daporkchop/lib/natives/NativeCodeException"); - - return env->Throw((jthrowable) env->NewObject( - clazz, - env->GetMethodID(clazz, "", "(Ljava/lang/String;I)V"), - env->NewStringUTF(msg), - err - )); -} - -jlong max_l(jlong a, jlong b) { - return a > b ? a : b; -} - -jlong min_l(jlong a, jlong b) { - return a < b ? a : b; -} diff --git a/natives/src/main/native/include/common.h b/natives/src/main/native/include/common.h new file mode 100644 index 000000000..f251390f5 --- /dev/null +++ b/natives/src/main/native/include/common.h @@ -0,0 +1,19 @@ +#include + +#ifdef PORKLIB_NATIVES_DEBUG +//include some extra headers that might be useful while debugging +#include +#include + +#include +#endif + +jint throwException(JNIEnv* env, const char* msg); + +jint throwException(JNIEnv* env, const char* msg, jint err); + +jint throwException(JNIEnv* env, const char* msg, jlong err); + +jlong max_l(jlong a, jlong b); + +jlong min_l(jlong a, jlong b); diff --git a/natives/src/main/native/zlib/Makefile b/natives/src/main/native/zlib/Makefile deleted file mode 100644 index f8886ed25..000000000 --- a/natives/src/main/native/zlib/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -BUILDDIR := build/$(BUILD) - -ifneq ($(BUILD),$(notdir $(CURDIR))) -.PHONY: $(BUILD) clean - -clean: - @rm -rf build/ tmp/ - -$(BUILD): $(CURDIR)/lib-zlib/zlib.h - @[ -d $(BUILDDIR) ] || mkdir -p $(BUILDDIR) - @$(MAKE) --no-print-directory -C $(BUILDDIR) -f $(CURDIR)/Makefile BUILD=$(BUILD) zlib-$(BUILD) - -$(CURDIR)/lib-zlib/zlib.h: - @echo "Downloading zlib sources..." - @curl -o - https://cloud.daporkchop.net/programs/source/zlib-1.2.11.tar.gz | tar zxf - - @mv zlib-1.2.11/ lib-zlib/ - -else -.PHONY: zlib-$(BUILD) - -include $(TOPDIR)/toolchain/$(BUILD) - -SOURCES := $(SOURCES) $(PROJDIR)/source $(PROJDIR)/lib-zlib - -CFILES := $(foreach dir,$(SOURCES),$(shell find -L $(dir) -maxdepth 1 -type f -name '*.c')) -CPPFILES := $(foreach dir,$(SOURCES),$(shell find -L $(dir) -maxdepth 1 -type f -name '*.cpp')) -OFILES := $(addsuffix .o,$(subst /,__,$(CFILES)) $(subst /,__,$(CPPFILES))) - -CFLAGS := $(CFLAGS) -D__LARGEFILE64__SOURCE=1 -DHAVE__HIDDEN -DPIC -INCLUDE := $(foreach dir,$(INCLUDES),-I$(dir)) -I$(PROJDIR)/include - -zlib-$(BUILD): $(OUTDIR)/$(BUILD)/libzlib.$(EXT) - -$(OUTDIR)/$(BUILD)/libzlib.$(EXT): $(CFILES) $(CPPFILES) $(OFILES) $(TOPDIR)/Makefile $(TOPDIR)/src/main/native/zlib/Makefile - @[ -d $(OUTDIR)/$(BUILD) ] || mkdir -p $(OUTDIR)/$(BUILD) - @echo "Linking $@..." - @$(LD) $(LDFLAGS) $(INCLUDE) -o $@ $(OFILES) - @echo "Stripping $@..." - @strip $@ - -%.c.o: $(CFILES) $(TOPDIR)/Makefile $(TOPDIR)/src/main/native/zlib/Makefile - @echo "Building $(subst .o,,$(subst __,/,$@))..." - @$(CC) $(CFLAGS) $(INCLUDE) -c $(subst .o,,$(subst __,/,$@)) -o $@ - -%.cpp.o: $(CPPFILES) $(TOPDIR)/Makefile $(TOPDIR)/src/main/native/zlib/Makefile - @echo "Building $(subst .o,,$(subst __,/,$@))..." - @$(CXX) $(CXXFLAGS) $(INCLUDE) -c $(subst .o,,$(subst __,/,$@)) -o $@ - -endif diff --git a/natives/src/main/native/zlib/source/deflater.cpp b/natives/src/main/native/zlib/source/deflater.cpp deleted file mode 100644 index 1874ef2e7..000000000 --- a/natives/src/main/native/zlib/source/deflater.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include -#include "net_daporkchop_lib_natives_zlib_NativeDeflater.h" - -#include "../lib-zlib/zlib.h" - -#include -#include - -#include - -static jfieldID ctxID; -static jfieldID readBytesID; -static jfieldID writtenBytesID; -static jfieldID finishedID; - -__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeDeflater_load(JNIEnv* env, jclass cla) { - ctxID = env->GetFieldID(cla, "ctx", "J"); - readBytesID = env->GetFieldID(cla, "readBytes", "I"); - writtenBytesID = env->GetFieldID(cla, "writtenBytes", "I"); - finishedID = env->GetFieldID(cla, "finished", "Z"); -} - -__attribute__((visibility("default"))) jlong JNICALL Java_net_daporkchop_lib_natives_zlib_NativeDeflater_init(JNIEnv* env, jclass cla, jint level, jint mode) { - if (level < 0 || level > 9) { - throwException(env, "Invalid level!", level); - return 0; - } - - int windowBits; - if (mode == 0) { //zlib - windowBits = 15; - } else if (mode == 1) { //gzip - windowBits = 15 + 16; - } else if (mode == 3) { //raw deflate - windowBits = -15; - } else { - throwException(env, "Invalid deflater mode!", mode); - return 0; - } - - z_stream* stream = (z_stream*) malloc(sizeof(z_stream)); - memset(stream, 0, sizeof(z_stream)); - - int ret = deflateInit2(stream, level, Z_DEFLATED, windowBits, 8, Z_DEFAULT_STRATEGY); - - if (ret != Z_OK) { - char* msg = stream->msg; - free(stream); - throwException(env, msg == nullptr ? "Couldn't init deflater!" : msg, ret); - } - - return (jlong) stream; -} - -__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeDeflater_end(JNIEnv* env, jclass cla, jlong ctx) { - z_stream* stream = (z_stream*) ctx; - int ret = deflateReset(stream); - if (ret != Z_OK) { - throwException(env, stream->msg == nullptr ? "Couldn't reset deflater!" : stream->msg, ret); - return; - } - - ret = deflateEnd(stream); - char* msg = stream->msg; - free(stream); - - if (ret != Z_OK) { - throwException(env, msg == nullptr ? "Couldn't end deflater!" : msg, ret); - } -} - -__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeDeflater_input(JNIEnv* env, jobject obj, jlong srcAddr, jint srcLen) { - z_stream* stream = (z_stream*) env->GetLongField(obj, ctxID); - - stream->next_in = (unsigned char*) srcAddr; - stream->avail_in = srcLen; -} - -__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeDeflater_output(JNIEnv* env, jobject obj, jlong dstAddr, jint dstLen) { - z_stream* stream = (z_stream*) env->GetLongField(obj, ctxID); - - stream->next_out = (unsigned char*) dstAddr; - stream->avail_out = dstLen; -} - -__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeDeflater_deflate(JNIEnv* env, jobject obj, jboolean finish) { - z_stream* stream = (z_stream*) env->GetLongField(obj, ctxID); - - jint avail_in = stream->avail_in; - jint avail_out = stream->avail_out; - - //even if finish is set to true, don't actually run deflate with the finish flag if the entire data isn't going to be able to be read this invocation - int ret = deflate(stream, finish ? Z_FINISH : Z_NO_FLUSH); - if (ret == Z_STREAM_END) { - env->SetBooleanField(obj, finishedID, (jboolean) 1); - } else if (ret != Z_OK) { - throwException(env, stream->msg == nullptr ? "Invalid return value from deflate()!" : stream->msg, ret); - return; - } - - env->SetIntField(obj, readBytesID, (avail_in - stream->avail_in)); - env->SetIntField(obj, writtenBytesID, (avail_out - stream->avail_out)); -} - -__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeDeflater_reset(JNIEnv* env, jobject obj) { - z_stream* stream = (z_stream*) env->GetLongField(obj, ctxID); - int ret = deflateReset(stream); - - if (ret != Z_OK) { - throwException(env, stream->msg == nullptr ? "Couldn't reset deflater!" : stream->msg, ret); - } - - env->SetIntField(obj, readBytesID, 0); - env->SetIntField(obj, writtenBytesID, 0); - env->SetBooleanField(obj, finishedID, (jboolean) 0); -} diff --git a/natives/src/main/native/zlib/source/inflater.cpp b/natives/src/main/native/zlib/source/inflater.cpp deleted file mode 100644 index 9ac00f89e..000000000 --- a/natives/src/main/native/zlib/source/inflater.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include -#include "net_daporkchop_lib_natives_zlib_NativeInflater.h" - -#include "../lib-zlib/zlib.h" - -#include -#include - -static jfieldID ctxID; -static jfieldID readBytesID; -static jfieldID writtenBytesID; -static jfieldID finishedID; - -__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeInflater_load(JNIEnv* env, jclass cla) { - ctxID = env->GetFieldID(cla, "ctx", "J"); - readBytesID = env->GetFieldID(cla, "readBytes", "I"); - writtenBytesID = env->GetFieldID(cla, "writtenBytes", "I"); - finishedID = env->GetFieldID(cla, "finished", "Z"); -} - -__attribute__((visibility("default"))) jlong JNICALL Java_net_daporkchop_lib_natives_zlib_NativeInflater_init(JNIEnv* env, jclass cla, jint mode) { - int windowBits; - if (mode == 0) { //zlib - windowBits = 0; - } else if (mode == 1) { //gzip - windowBits = 16; - } else if (mode == 2) { //auto-detect zlib or gzip - windowBits = 32; - } else if (mode == 3) { //raw deflate - windowBits = -15; - } else { - throwException(env, "Invalid inflater mode!", mode); - return 0; - } - - z_stream* stream = (z_stream*) malloc(sizeof(z_stream)); - memset(stream, 0, sizeof(z_stream)); - - int ret = inflateInit2(stream, windowBits); - - if (ret != Z_OK) { - char* msg = stream->msg; - free(stream); - throwException(env, msg == nullptr ? "Couldn't init inflater!" : msg, ret); - } - - return (jlong) stream; -} - -__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeInflater_end(JNIEnv* env, jclass cla, jlong ctx) { - z_stream* stream = (z_stream*) ctx; - int ret = inflateReset(stream); - if (ret != Z_OK) { - char* msg = stream->msg; - free(stream); - throwException(env, msg == nullptr ? "Couldn't reset inflater!" : msg, ret); - return; - } - - ret = inflateEnd(stream); - char* msg = stream->msg; - free(stream); - - if (ret != Z_OK) { - throwException(env, msg == nullptr ? "Couldn't end inflater!" : msg, ret); - } -} - -__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeInflater_input(JNIEnv* env, jobject obj, jlong srcAddr, jint srcLen) { - z_stream* stream = (z_stream*) env->GetLongField(obj, ctxID); - - stream->next_in = (unsigned char*) srcAddr; - stream->avail_in = srcLen; -} - -__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeInflater_output(JNIEnv* env, jobject obj, jlong dstAddr, jint dstLen) { - z_stream* stream = (z_stream*) env->GetLongField(obj, ctxID); - - stream->next_out = (unsigned char*) dstAddr; - stream->avail_out = dstLen; -} - -__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeInflater_inflate(JNIEnv* env, jobject obj) { - z_stream* stream = (z_stream*) env->GetLongField(obj, ctxID); - - jint avail_in = stream->avail_in; - jint avail_out = stream->avail_out; - - //don't actually run inflate with the flush flag if the entire data isn't going to be able to be read this invocation - //of course this doesn't mean it'll be buffering 4GB of data, it just means that it won't begin flushing the data until it decides to - int ret = inflate(stream, Z_SYNC_FLUSH); - if (ret == Z_STREAM_END) { - env->SetBooleanField(obj, finishedID, (jboolean) 1); - } else if (ret != Z_OK) { - throwException(env, stream->msg == nullptr ? "Invalid return value from inflate()!" : stream->msg, ret); - return; - } - - env->SetIntField(obj, readBytesID, avail_in - stream->avail_in); - env->SetIntField(obj, writtenBytesID, avail_out - stream->avail_out); -} - -__attribute__((visibility("default"))) void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeInflater_reset(JNIEnv* env, jobject obj) { - z_stream* stream = (z_stream*) env->GetLongField(obj, ctxID); - - int ret = inflateReset(stream); - - if (ret != Z_OK) { - throwException(env, stream->msg == nullptr ? "Couldn't reset inflater!" : stream->msg, ret); - } - - env->SetIntField(obj, readBytesID, 0); - env->SetIntField(obj, writtenBytesID, 0); - env->SetBooleanField(obj, finishedID, (jboolean) 0); -} diff --git a/natives/src/main/native/zlib/source/net_daporkchop_lib_natives_zlib_NativeDeflater.h b/natives/src/main/native/zlib/source/net_daporkchop_lib_natives_zlib_NativeDeflater.h deleted file mode 100644 index 33b03eb4b..000000000 --- a/natives/src/main/native/zlib/source/net_daporkchop_lib_natives_zlib_NativeDeflater.h +++ /dev/null @@ -1,69 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class net_daporkchop_lib_natives_zlib_NativeDeflater */ - -#ifndef _Included_net_daporkchop_lib_natives_zlib_NativeDeflater -#define _Included_net_daporkchop_lib_natives_zlib_NativeDeflater -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: net_daporkchop_lib_natives_zlib_NativeDeflater - * Method: load - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeDeflater_load - (JNIEnv *, jclass); - -/* - * Class: net_daporkchop_lib_natives_zlib_NativeDeflater - * Method: init - * Signature: (II)J - */ -JNIEXPORT jlong JNICALL Java_net_daporkchop_lib_natives_zlib_NativeDeflater_init - (JNIEnv *, jclass, jint, jint); - -/* - * Class: net_daporkchop_lib_natives_zlib_NativeDeflater - * Method: end - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeDeflater_end - (JNIEnv *, jclass, jlong); - -/* - * Class: net_daporkchop_lib_natives_zlib_NativeDeflater - * Method: input - * Signature: (JI)V - */ -JNIEXPORT void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeDeflater_input - (JNIEnv *, jobject, jlong, jint); - -/* - * Class: net_daporkchop_lib_natives_zlib_NativeDeflater - * Method: output - * Signature: (JI)V - */ -JNIEXPORT void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeDeflater_output - (JNIEnv *, jobject, jlong, jint); - -/* - * Class: net_daporkchop_lib_natives_zlib_NativeDeflater - * Method: deflate - * Signature: (Z)V - */ -JNIEXPORT void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeDeflater_deflate - (JNIEnv *, jobject, jboolean); - -/* - * Class: net_daporkchop_lib_natives_zlib_NativeDeflater - * Method: reset - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeDeflater_reset - (JNIEnv *, jobject); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/natives/src/main/native/zlib/source/net_daporkchop_lib_natives_zlib_NativeInflater.h b/natives/src/main/native/zlib/source/net_daporkchop_lib_natives_zlib_NativeInflater.h deleted file mode 100644 index d6a8bd8aa..000000000 --- a/natives/src/main/native/zlib/source/net_daporkchop_lib_natives_zlib_NativeInflater.h +++ /dev/null @@ -1,69 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class net_daporkchop_lib_natives_zlib_NativeInflater */ - -#ifndef _Included_net_daporkchop_lib_natives_zlib_NativeInflater -#define _Included_net_daporkchop_lib_natives_zlib_NativeInflater -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: net_daporkchop_lib_natives_zlib_NativeInflater - * Method: load - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeInflater_load - (JNIEnv *, jclass); - -/* - * Class: net_daporkchop_lib_natives_zlib_NativeInflater - * Method: init - * Signature: (I)J - */ -JNIEXPORT jlong JNICALL Java_net_daporkchop_lib_natives_zlib_NativeInflater_init - (JNIEnv *, jclass, jint); - -/* - * Class: net_daporkchop_lib_natives_zlib_NativeInflater - * Method: end - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeInflater_end - (JNIEnv *, jclass, jlong); - -/* - * Class: net_daporkchop_lib_natives_zlib_NativeInflater - * Method: input - * Signature: (JI)V - */ -JNIEXPORT void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeInflater_input - (JNIEnv *, jobject, jlong, jint); - -/* - * Class: net_daporkchop_lib_natives_zlib_NativeInflater - * Method: output - * Signature: (JI)V - */ -JNIEXPORT void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeInflater_output - (JNIEnv *, jobject, jlong, jint); - -/* - * Class: net_daporkchop_lib_natives_zlib_NativeInflater - * Method: inflate - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeInflater_inflate - (JNIEnv *, jobject); - -/* - * Class: net_daporkchop_lib_natives_zlib_NativeInflater - * Method: reset - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_net_daporkchop_lib_natives_zlib_NativeInflater_reset - (JNIEnv *, jobject); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/natives/toolchain/aarch64-linux-gnu b/natives/toolchain/aarch64-linux-gnu new file mode 100644 index 000000000..ea7f23ea8 --- /dev/null +++ b/natives/toolchain/aarch64-linux-gnu @@ -0,0 +1,18 @@ +ifndef PORKLIB_NATIVES_DEBUG +# release mode +export CFLAGS := $(CFLAGS) -flto=$(NPROC) -fno-fat-lto-objects +export CXXFLAGS := $(CXXFLAGS) -flto=$(NPROC) -fno-fat-lto-objects +export LDFLAGS := $(LDFLAGS) -flto=$(NPROC) -fno-fat-lto-objects +else +# debug mode +export CFLAGS := $(CFLAGS) +export CXXFLAGS := $(CXXFLAGS) +export LDFLAGS := $(LDFLAGS) +endif + +export CC := aarch64-linux-gnu-gcc +export CXX := aarch64-linux-gnu-g++ +export LD := aarch64-linux-gnu-g++ +export STRIP := aarch64-linux-gnu-strip + +export EXT := so diff --git a/natives/toolchain/x86-linux-gnu b/natives/toolchain/x86-linux-gnu deleted file mode 100644 index 0980fea81..000000000 --- a/natives/toolchain/x86-linux-gnu +++ /dev/null @@ -1,9 +0,0 @@ -export CFLAGS := $(CFLAGS) -m32 -flto=auto -fno-fat-lto-objects -export CXXFLAGS := $(CXXFLAGS) -m32 -flto=auto -fno-fat-lto-objects -export LDFLAGS := $(LDFLAGS) -m32 -flto=auto -fno-fat-lto-objects - -export CC := x86_64-linux-gnu-gcc -export CXX := x86_64-linux-gnu-g++ -export LD := x86_64-linux-gnu-g++ - -export EXT := so diff --git a/natives/toolchain/x86_64-linux-gnu b/natives/toolchain/x86_64-linux-gnu index 02f01d63d..6a5a5c917 100644 --- a/natives/toolchain/x86_64-linux-gnu +++ b/natives/toolchain/x86_64-linux-gnu @@ -1,9 +1,18 @@ -export CFLAGS := $(CFLAGS) -flto=auto -fno-fat-lto-objects -export CXXFLAGS := $(CXXFLAGS) -flto=auto -fno-fat-lto-objects -export LDFLAGS := $(LDFLAGS) -flto=auto -fno-fat-lto-objects +ifndef PORKLIB_NATIVES_DEBUG +# release mode +export CFLAGS := $(CFLAGS) -flto=$(NPROC) -fno-fat-lto-objects +export CXXFLAGS := $(CXXFLAGS) -flto=$(NPROC) -fno-fat-lto-objects +export LDFLAGS := $(LDFLAGS) -flto=$(NPROC) -fno-fat-lto-objects +else +# debug mode +export CFLAGS := $(CFLAGS) +export CXXFLAGS := $(CXXFLAGS) +export LDFLAGS := $(LDFLAGS) +endif -export CC := x86_64-linux-gnu-gcc -export CXX := x86_64-linux-gnu-g++ -export LD := x86_64-linux-gnu-g++ +export CC := x86_64-linux-gnu-gcc +export CXX := x86_64-linux-gnu-g++ +export LD := x86_64-linux-gnu-g++ +export STRIP := x86_64-linux-gnu-strip export EXT := so diff --git a/natives/toolchain/x86_64-w64-mingw32 b/natives/toolchain/x86_64-w64-mingw32 index e8e19b49e..9da701d24 100644 --- a/natives/toolchain/x86_64-w64-mingw32 +++ b/natives/toolchain/x86_64-w64-mingw32 @@ -5,8 +5,9 @@ export CFLAGS := $(CFLAGS) export CXXFLAGS := $(CXXFLAGS) export LDFLAGS := $(LDFLAGS) -export CC := x86_64-w64-mingw32-gcc -export CXX := x86_64-w64-mingw32-g++ -export LD := x86_64-w64-mingw32-g++ +export CC := x86_64-w64-mingw32-gcc +export CXX := x86_64-w64-mingw32-g++ +export LD := x86_64-w64-mingw32-g++ +export STRIP := x86_64-w64-mingw32-strip export EXT := dll diff --git a/network/tcp/src/example/java/http/TestHTTPGET.java b/network/tcp/src/example/java/http/TestHTTPGET.java index dd24f091e..4e5fa9192 100644 --- a/network/tcp/src/example/java/http/TestHTTPGET.java +++ b/network/tcp/src/example/java/http/TestHTTPGET.java @@ -22,7 +22,6 @@ import lombok.NonNull; import net.daporkchop.lib.binary.stream.DataIn; import net.daporkchop.lib.logging.LogAmount; -import net.daporkchop.lib.logging.Logging; import net.daporkchop.lib.network.endpoint.PClient; import net.daporkchop.lib.network.endpoint.builder.ClientBuilder; import net.daporkchop.lib.network.netty.LoopPool; @@ -85,7 +84,7 @@ public void onReceive(@NonNull DataIn in, @NonNull PacketMetadata metadata) thro if (this.headers != null) { throw new IllegalStateException("Headers already read!"); } else { - this.headers = Arrays.stream(new String(in.readAllAvailableBytes(), StandardCharsets.UTF_8).split("\r\n")) + this.headers = Arrays.stream(new String(in.toByteArray(), StandardCharsets.UTF_8).split("\r\n")) .map(s -> s.split(": ", 2)) .filter(a -> a.length == 2) .collect(Collectors.toMap(a -> a[0], a -> a[1])); diff --git a/primitive/generator/src/main/java/net/daporkchop/lib/primitive/generator/Generator.java b/primitive/generator/src/main/java/net/daporkchop/lib/primitive/generator/Generator.java index bccc2d074..544170d3b 100644 --- a/primitive/generator/src/main/java/net/daporkchop/lib/primitive/generator/Generator.java +++ b/primitive/generator/src/main/java/net/daporkchop/lib/primitive/generator/Generator.java @@ -21,10 +21,10 @@ import com.google.gson.JsonParser; import lombok.NonNull; import lombok.RequiredArgsConstructor; +import net.daporkchop.lib.binary.oio.StreamUtil; import net.daporkchop.lib.common.misc.file.PFiles; import net.daporkchop.lib.common.misc.InstancePool; import net.daporkchop.lib.logging.Logging; -import sun.misc.IOUtils; import java.io.File; import java.io.FileInputStream; @@ -174,7 +174,7 @@ public class Generator { try (InputStream is = new FileInputStream(new File(".", "../../LICENSE"))) { LICENSE = String.format("/*\n * %s\n */", - new String(IOUtils.readFully(is, -1, false)) + new String(StreamUtil.toByteArray(is)) .replace("$today.year", String.valueOf(Calendar.getInstance().get(Calendar.YEAR))) /*.trim()*/.replaceAll("\n", "\n * ")); } catch (IOException e) { @@ -309,7 +309,7 @@ public void generate(@NonNull File file, @NonNull File out) { File f = files[i]; lastModified = max(lastModified, f.lastModified()); try (InputStream is = new FileInputStream(f)) { - methods[i] = new String(IOUtils.readFully(is, -1, false)); + methods[i] = new String(StreamUtil.toByteArray(is)); } catch (IOException e) { throw new RuntimeException(e); } @@ -328,7 +328,7 @@ public void generate(@NonNull File file, @NonNull File out) { } String content; try (InputStream is = new FileInputStream(file)) { - content = new String(IOUtils.readFully(is, -1, false)); + content = new String(StreamUtil.toByteArray(is)); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/settings.gradle b/settings.gradle index d2d6dd213..02b423063 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -18,10 +18,13 @@ rootProject.name = 'PorkLib' include 'ai' include 'binary' include 'collections' -include 'crypto' include 'common' +include 'compression' +include 'compression:zlib' +include 'compression:zstd' include 'concurrent' include 'concurrent:parallel' +include 'crypto' include 'db' include 'db:db-core' include 'db:db-extensions-leveldb' @@ -51,6 +54,8 @@ include 'primitive:generator' include 'reflection' include 'unsafe' +findProject(':compression:zlib')?.name = 'compression-zlib' +findProject(':compression:zstd')?.name = 'compression-zstd' findProject(':concurrent:parallel')?.name = 'parallel' findProject(':db:db-core')?.name = 'db-core' findProject(':db:db-extensions-leveldb')?.name = 'db-extensions-leveldb' diff --git a/natives/src/main/java/net/daporkchop/lib/natives/zlib/NativeInflater.java b/unsafe/src/main/java/net/daporkchop/lib/unsafe/util/AbstractReleasable.java similarity index 58% rename from natives/src/main/java/net/daporkchop/lib/natives/zlib/NativeInflater.java rename to unsafe/src/main/java/net/daporkchop/lib/unsafe/util/AbstractReleasable.java index b46054a81..aa96ba65e 100644 --- a/natives/src/main/java/net/daporkchop/lib/natives/zlib/NativeInflater.java +++ b/unsafe/src/main/java/net/daporkchop/lib/unsafe/util/AbstractReleasable.java @@ -1,7 +1,7 @@ /* * Adapted from the Wizardry License * - * Copyright (c) 2018-2019 DaPorkchop_ and contributors + * Copyright (c) 2018-2020 DaPorkchop_ and contributors * * Permission is hereby granted to any persons and/or organizations using this software to copy, modify, merge, publish, and distribute it. Said persons and/or organizations are not allowed to use the software or any derivatives of the work for commercial use or any other means to generate income, nor are they allowed to claim this software as their own. * @@ -13,59 +13,45 @@ * */ -package net.daporkchop.lib.natives.zlib; +package net.daporkchop.lib.unsafe.util; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.experimental.Accessors; -import net.daporkchop.lib.unsafe.PCleaner; +import net.daporkchop.lib.unsafe.PUnsafe; +import net.daporkchop.lib.unsafe.capability.Releasable; import net.daporkchop.lib.unsafe.util.exception.AlreadyReleasedException; /** - * Implementation of {@link PInflater} using native code. + * An abstract implementation of {@link Releasable}. * * @author DaPorkchop_ */ -@Getter -@Accessors(fluent = true) -public final class NativeInflater implements PInflater { - static native void load(); +public abstract class AbstractReleasable implements Releasable { + private static final long RELEASED_OFFSET = PUnsafe.pork_getOffset(AbstractReleasable.class, "released"); - private static native long init(int mode); - - private static native void end(long ctx); - - @Getter(AccessLevel.NONE) - private final long ctx; - - @Getter(AccessLevel.NONE) - private final PCleaner cleaner; - - private int readBytes; - private int writtenBytes; - - private boolean finished; - - NativeInflater(int mode) { - long ctx = this.ctx = init(mode); - this.cleaner = PCleaner.cleaner(this, () -> end(ctx)); - } - - @Override - public native void input(long addr, int size); + private volatile int released = 0; @Override - public native void output(long addr, int size); - - @Override - public native void inflate(); + public void release() throws AlreadyReleasedException { + if (!PUnsafe.compareAndSwapInt(this, RELEASED_OFFSET, 0, 1)) { + throw new AlreadyReleasedException(); + } - @Override - public native void reset(); + this.doRelease(); + } - @Override - public void release() throws AlreadyReleasedException { - if (!this.cleaner.tryClean()) { + /** + * Actually releases this instance's resources. + *

+ * Will only be called once. + */ + protected abstract void doRelease(); + + /** + * Asserts that this instance has not been released. + * + * @throws AlreadyReleasedException if this instance has been released + */ + protected final void assertNotReleased() throws AlreadyReleasedException { + if (this.released != 0) { throw new AlreadyReleasedException(); } }