You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2018/02/26 10:46:44 UTC
[isis] branch master updated: ISIS-1841 Internal API: add smart
compression including tests
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/master by this push:
new 3a98872 ISIS-1841 Internal API: add smart compression including tests
3a98872 is described below
commit 3a98872616ab29bc18589c9f90c405c1695f80a5
Author: Andi Huber <ah...@apache.org>
AuthorDate: Mon Feb 26 11:46:42 2018 +0100
ISIS-1841 Internal API: add smart compression including tests
---
.../apache/isis/applib/internal/base/_Bytes.java | 60 +++++++++++++++--
.../internal/base/_Bytes_GZipCompressor.java | 8 ++-
.../internal/base/_Bytes_GZipCompressorSmart.java | 74 ++++++++++++++++++++
.../isis/applib/internal/base/BytesTest.java | 78 +++++++++++++++++++++-
4 files changed, 210 insertions(+), 10 deletions(-)
diff --git a/core/applib/src/main/java/org/apache/isis/applib/internal/base/_Bytes.java b/core/applib/src/main/java/org/apache/isis/applib/internal/base/_Bytes.java
index e3a1056..866c345 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/internal/base/_Bytes.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/internal/base/_Bytes.java
@@ -24,6 +24,8 @@ import java.util.Base64;
import java.util.Objects;
import java.util.function.UnaryOperator;
+import javax.annotation.Nullable;
+
/**
* <h1>- internal use only -</h1>
* <p>
@@ -40,6 +42,52 @@ public final class _Bytes {
private _Bytes(){}
+ // -- PREPEND/APPEND
+
+ /**
+ * Copies all bytes from {@code bytes} followed by all bytes from {@code target} into a newly-allocated byte array.
+ * @param target
+ * @param bytes to be prepended to {@code target}
+ * @return bytes + target, or null if both {@code target} and {@code bytes} are null
+ */
+ public final static byte[] prepend(@Nullable final byte[] target, @Nullable final byte ... bytes) {
+ if(target==null) {
+ if(bytes==null) {
+ return null;
+ }
+ return bytes.clone();
+ }
+ if(bytes==null) {
+ return target.clone();
+ }
+ final byte[] result = new byte[target.length + bytes.length];
+ System.arraycopy(bytes, 0, result, 0, bytes.length);
+ System.arraycopy(target, 0, result, bytes.length, target.length);
+ return result;
+ }
+
+ /**
+ * Copies all bytes from {@code target} followed by all bytes from {@code bytes} into a newly-allocated byte array.
+ * @param target
+ * @param bytes to be appended to {@code target}
+ * @return target + bytes, or null if both {@code target} and {@code bytes} are null
+ */
+ public final static byte[] append(@Nullable final byte[] target, @Nullable final byte ... bytes) {
+ if(target==null) {
+ if(bytes==null) {
+ return null;
+ }
+ return bytes.clone();
+ }
+ if(bytes==null) {
+ return target.clone();
+ }
+ final byte[] result = new byte[target.length + bytes.length];
+ System.arraycopy(target, 0, result, 0, target.length);
+ System.arraycopy(bytes, 0, result, target.length, bytes.length);
+ return result;
+ }
+
// -- BASE-64 ENCODING
/**
@@ -49,7 +97,7 @@ public final class _Bytes {
* @param input
* @return null if {@code input} is null
*/
- public final static byte[] encodeToBase64(Base64.Encoder encoder, final byte[] input) {
+ public final static byte[] encodeToBase64(Base64.Encoder encoder, @Nullable final byte[] input) {
if(input==null) {
return null;
}
@@ -64,7 +112,7 @@ public final class _Bytes {
* @param base64
* @return null if {@code base64} is null
*/
- public final static byte[] decodeBase64(Base64.Decoder decoder, final byte[] base64) {
+ public final static byte[] decodeBase64(Base64.Decoder decoder, @Nullable final byte[] base64) {
if(base64==null) {
return null;
}
@@ -81,7 +129,7 @@ public final class _Bytes {
* @param input
* @return null if {@code input} is null
*/
- public static final byte[] compress(byte[] input) {
+ public static final byte[] compress(@Nullable byte[] input) {
if(input==null) {
return null;
}
@@ -89,7 +137,7 @@ public final class _Bytes {
return input;
}
try {
- return _Bytes_GZipCompressor.compress(input);
+ return _Bytes_GZipCompressorSmart.compress(input);
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
@@ -102,7 +150,7 @@ public final class _Bytes {
* @param compressed
* @return null if {@code compressed} is null
*/
- public static final byte[] decompress(byte[] compressed) {
+ public static final byte[] decompress(@Nullable byte[] compressed) {
if(compressed==null) {
return null;
}
@@ -110,7 +158,7 @@ public final class _Bytes {
return compressed;
}
try {
- return _Bytes_GZipCompressor.decompress(compressed);
+ return _Bytes_GZipCompressorSmart.decompress(compressed);
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
diff --git a/core/applib/src/main/java/org/apache/isis/applib/internal/base/_Bytes_GZipCompressor.java b/core/applib/src/main/java/org/apache/isis/applib/internal/base/_Bytes_GZipCompressor.java
index 6820da6..8a17814 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/internal/base/_Bytes_GZipCompressor.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/internal/base/_Bytes_GZipCompressor.java
@@ -32,9 +32,11 @@ import java.util.zip.GZIPOutputStream;
*/
final class _Bytes_GZipCompressor {
- static byte[] compress(final byte[] input) throws IOException {
+ static byte[] compress(final byte[] input) throws IOException {
- final ByteArrayOutputStream os = new ByteArrayOutputStream(input.length);
+ final int BUFFER_SIZE = Math.max(256, input.length); // at least 256
+
+ final ByteArrayOutputStream os = new ByteArrayOutputStream(BUFFER_SIZE);
final GZIPOutputStream gos = new GZIPOutputStream(os);
gos.write(input);
gos.close();
@@ -57,5 +59,7 @@ final class _Bytes_GZipCompressor {
gis.close();
return baos.toByteArray();
}
+
+
}
diff --git a/core/applib/src/main/java/org/apache/isis/applib/internal/base/_Bytes_GZipCompressorSmart.java b/core/applib/src/main/java/org/apache/isis/applib/internal/base/_Bytes_GZipCompressorSmart.java
new file mode 100644
index 0000000..bb40430
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/internal/base/_Bytes_GZipCompressorSmart.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.isis.applib.internal.base;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.apache.isis.applib.internal.exceptions._Exceptions;
+
+/**
+ *
+ * package private mixin for utility class {@link _Bytes}
+ *
+ */
+final class _Bytes_GZipCompressorSmart {
+
+ private static final int INPUT_LENGTH_THRESHOLD_FOR_SMART_COMPRESSION = 256;
+ private static final int GZIP_MIN_OVERHEAD = 18;
+ private static final byte COMPRESSION_NONE = 0;
+ private static final byte COMPRESSION_GZIP = 1;
+
+ static byte[] compress(byte[] input) throws IOException {
+ if(input.length<GZIP_MIN_OVERHEAD) {
+ return input;
+ }
+ if(input.length<INPUT_LENGTH_THRESHOLD_FOR_SMART_COMPRESSION) {
+ return _Bytes.prepend(input, COMPRESSION_NONE); // prefix the input
+ } else {
+ return _Bytes.prepend(_Bytes_GZipCompressor.compress(input), COMPRESSION_GZIP); // prefix the input
+ }
+ }
+
+ static byte[] decompress(byte[] input) throws IOException {
+ if(input==null || input.length<GZIP_MIN_OVERHEAD)
+ return input;
+
+ final byte[] inputWithoutPrefix = Arrays.copyOfRange(input, 1, input.length);
+
+ return isCompressed(input) ? _Bytes_GZipCompressor.decompress(inputWithoutPrefix) : inputWithoutPrefix;
+
+ }
+
+ // -- HELPER
+
+ private static boolean isCompressed(byte[] input) {
+ switch (input[0]) {
+ case COMPRESSION_NONE:
+ return false;
+ case COMPRESSION_GZIP:
+ return true;
+ default:
+ throw _Exceptions.unmatchedCase(input[0]);
+ }
+
+ }
+
+}
diff --git a/core/applib/src/test/java/org/apache/isis/applib/internal/base/BytesTest.java b/core/applib/src/test/java/org/apache/isis/applib/internal/base/BytesTest.java
index c653284..2933a2f 100644
--- a/core/applib/src/test/java/org/apache/isis/applib/internal/base/BytesTest.java
+++ b/core/applib/src/test/java/org/apache/isis/applib/internal/base/BytesTest.java
@@ -24,16 +24,21 @@ import static org.hamcrest.Matchers.lessThan;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
+import org.apache.isis.applib.internal._Constants;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
public class BytesTest {
final int n = 256;
private final byte[] allBytes = new byte[n];
- private final byte[] testimonial =
+ private final static byte[] testimonial =
_Strings.toBytes(
"https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html#basic?"+
"0-theme-entityPageContainer-entity-rows-2-rowContents-1-col-tabGroups-1-panel-"
@@ -49,7 +54,37 @@ public class BytesTest {
}
}
- // -- COMPRESSION
+ // -- PREPEND/APPEND
+
+ @Test
+ public void concatNullWithNull() throws Exception {
+ Assert.assertNull(_Bytes.append(null, null));
+ Assert.assertNull(_Bytes.prepend(null, null));
+ }
+
+ @Test
+ public void concatNullWithEmpty() throws Exception {
+ Assert.assertArrayEquals(_Constants.emptyBytes, _Bytes.append(null));
+ Assert.assertArrayEquals(_Constants.emptyBytes, _Bytes.prepend(null));
+ }
+
+ @Test
+ public void concatWithNull() throws Exception {
+ assertArrayEqualsButNotSame(allBytes, _Bytes.append(allBytes, null));
+ assertArrayEqualsButNotSame(allBytes, _Bytes.prepend(allBytes, null));
+ }
+
+ @Test
+ public void concatWithEmpty() throws Exception {
+ assertArrayEqualsButNotSame(allBytes, _Bytes.append(allBytes));
+ assertArrayEqualsButNotSame(allBytes, _Bytes.prepend(allBytes));
+ }
+
+ @Test
+ public void concatHappyCase() throws Exception {
+ assertArrayEqualsButNotSame(new byte[] {1,2,3,4,5}, _Bytes.append(new byte[] {1,2,3}, (byte)4, (byte)5));
+ assertArrayEqualsButNotSame(new byte[] {4,5,1,2,3}, _Bytes.prepend(new byte[] {1,2,3}, (byte)4, (byte)5));
+ }
@Test
public void compressIdentityWithNull() throws Exception {
@@ -75,6 +110,38 @@ public class BytesTest {
Assert.assertThat(compressionRatio, lessThan(0.7));
}
+
+ // -- COMPRESSION
+
+ @RunWith(Parameterized.class)
+ public static class CompressionTest {
+
+
+ @Parameters
+ public static Object[] data() {
+ return new Object[] {
+ (byte[]) null,
+ new byte[] { },
+ new byte[] { 0 },
+ new byte[] { 0, 1 },
+ new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 17
+ new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 18
+ new byte[] { 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 19
+ };
+ }
+
+ @Parameter
+ public byte[] input;
+
+
+ @Test
+ public void compressIdentity() throws Exception {
+ Assert.assertArrayEquals(input,
+ _Bytes.decompress(_Bytes.compress(input)));
+ }
+
+ }
+
// -- BASE-64
@Test
@@ -124,4 +191,11 @@ public class BytesTest {
_Bytes.asCompressedUrlBase64.apply(testimonial)));
}
+ // -- HELPER
+
+ private void assertArrayEqualsButNotSame(byte[] a, byte[] b) {
+ Assert.assertFalse(a == b);
+ Assert.assertArrayEquals(a, b);
+ }
+
}
--
To stop receiving notification emails like this one, please contact
ahuber@apache.org.