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.