Bug 569665 Enable parsing of compressed files created by OpenJDK >= 15

Improved CRC processing for old Gzip reader.
Tidy up help for acquire dialogs.

Change-Id: I8136f69264c421450fa05409d4a1b92cbde7573e
Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=569665
diff --git a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ChunkedGZIPRandomAccessFile.java b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ChunkedGZIPRandomAccessFile.java
index c86d506..e9a675c 100644
--- a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ChunkedGZIPRandomAccessFile.java
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ChunkedGZIPRandomAccessFile.java
@@ -549,8 +549,8 @@
             defaultHeader[5] = (byte)(lastMod >> 8);
             defaultHeader[6] = (byte)(lastMod >> 16);
             defaultHeader[7] = (byte)(lastMod >> 24);
-            String opsys = System.getProperty("os.name").toLowerCase(Locale.ENGLISH); //$NON-NLS-1$
-            if (opsys.contains("linux") || opsys.contains("unix")) //$NON-NLS-1$ //$NON-NLS-2$
+            String opsys = System.getProperty("os.name").toLowerCase(Locale.ROOT); //$NON-NLS-1$
+            if (opsys.contains("linux") || opsys.contains("unix") || opsys.contains("aix")) //$NON-NLS-1$ //$NON-NLS-2$
                 defaultHeader[9] = 3;
             else if (opsys.contains("mac")) //$NON-NLS-1$
                 defaultHeader[9] = 7;
@@ -656,6 +656,9 @@
         {
             try
             {
+                // for zero length files we still want to generate a gzip
+                if (!writtenComment)
+                    header();
                 flush();
                 def.finish();
                 dos.close();
diff --git a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/CompressedRandomAccessFile.java b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/CompressedRandomAccessFile.java
index f65e032..f1b8469 100644
--- a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/CompressedRandomAccessFile.java
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/CompressedRandomAccessFile.java
@@ -1,10 +1,10 @@
 /*******************************************************************************

- * Copyright (c) 2019,2020 IBM Corporation.

+ * Copyright (c) 2019,2021 IBM Corporation.

  * All rights reserved. This program and the accompanying materials

  * are made available under the terms of the Eclipse Public License 2.0

  * which accompanies this distribution, and is available at

- * https://www.eclipse.org/legal/epl-2.0/
- *
+ * https://www.eclipse.org/legal/epl-2.0/

+ *

  * SPDX-License-Identifier: EPL-2.0

  *

  * Contributors:

@@ -223,6 +223,12 @@
                     best = e2;

                 if (e3 >= 0 && Math.abs(e3 - estimate) < Math.abs(best - estimate))

                     best = e3;

+                /*

+                 * Attempt to detect a chunked file and round up the size

+                 * to at least 4GB so the parser doesn't throw an error with an inaccurate size 

+                 */

+                if (best < 0x100000000L && len32 <= 1024 * 1024)

+                    best = 0x100000000L;

                 return best;

             }

             else

diff --git a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/GZIPInputStream2.java b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/GZIPInputStream2.java
index 3fbab83..782dade 100644
--- a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/GZIPInputStream2.java
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/GZIPInputStream2.java
@@ -18,7 +18,6 @@
 import java.io.IOException;

 import java.io.InputStream;

 import java.nio.charset.StandardCharsets;

-import java.util.zip.CRC32;

 import java.util.zip.ZipException;

 

 import io.nayuki.deflate.InflaterInputStream;

@@ -37,7 +36,7 @@
     InputStream is;

     long uncompressedLen;

     long uncompressedLocationAtHeader;

-    CRC32 crc = new CRC32();

+    CRC32 crc;

     boolean checkcrc;

     String comment;

     String filename;

@@ -45,18 +44,17 @@
     {

         super(new InflaterInputStream((InflaterInputStream)gs.in));

         is = gs.is;

+        crc = gs.crc.clone();

         uncompressedLen = gs.uncompressedLen;

         uncompressedLocationAtHeader = gs.uncompressedLocationAtHeader;

-        // FIXME Need to initialize CRC to other value, so until then disable the CRC check

-        checkcrc = false;

     }

 

     public GZIPInputStream2(InputStream is) throws IOException

     {

         super(new InflaterInputStream(is, false));

         this.is = is;

+        crc = new CRC32();

         readHeader(is);

-        checkcrc = true;

     }

 

     private InputStream readHeader(InputStream is) throws IOException

@@ -85,6 +83,8 @@
         if (b3 < 0)

             throw new ZipException(Messages.GZIPInputStream2_TruncatedHeader);

         crc.update(b3);

+        if ((b3 & 0xe0) != 0x0)

+            throw new ZipException(Messages.GZIPInputStream2_BadHeaderFlag);

         int b4 = is.read();

         if (b4 < 0)

             throw new ZipException(Messages.GZIPInputStream2_TruncatedHeader);

@@ -143,21 +143,21 @@
                 bos.write(b);

             }

             crc.update(0);

-            filename = new String(bos.toByteArray(), StandardCharsets.UTF_8);

+            filename = new String(bos.toByteArray(), StandardCharsets.ISO_8859_1);

         }

         // Comment

         if ((b3 & FLG_FCOMMENT) != 0)

         {

             int b;

-            StringBuilder sb = new StringBuilder();

+            ByteArrayOutputStream bos = new ByteArrayOutputStream();

             while ((b = is.read()) != 0) {

                 if (b == -1)

                     throw new ZipException(Messages.GZIPInputStream2_TruncatedComment);

                 crc.update(b);

-                sb.append((char)b);

+                bos.write(b);

             }

             crc.update(0);

-            comment = sb.toString();

+            comment = new String(bos.toByteArray(), StandardCharsets.ISO_8859_1);

         }

         // CRC16

         if ((b3 & FLG_FHCRC) != 0)

@@ -197,7 +197,7 @@
                 ((InflaterInputStream)in).attach();

             }

         } while (r < 0);

-        if (r >= 0);

+        if (r >= 0)

         {

             crc.update(r);

             ++uncompressedLen;

@@ -274,7 +274,7 @@
         // Unsigned

         long crc32 = b0 & 0xff | (b1 & 0xff) << 8 | (b2 & 0xff) << 16 | (b3 & 0xffL) << 24;

         long crc32v = crc.getValue();

-        if (checkcrc && crc32v != crc32)

+        if (crc32v != crc32)

             throw new ZipException(Messages.GZIPInputStream2_BadTrailerCRC);

         // uncompressed length

         int b4 = is.read();

@@ -305,4 +305,63 @@
     {

         super.close();

     }

+

+    /**

+     * A CRC32 implementation - which

+     * allows the state to be cloned.

+     */

+    private static class CRC32 implements Cloneable

+    {

+        int value;

+        private static final int table[] = new int[256];

+        static

+        {

+            for (int i = 0; i < 256; ++i)

+            {

+                int v = i;

+                for (int j = 0; j < 8; ++j)

+                {

+                    if ((v & 1) != 0)

+                        v = 0xedb88320 ^ (v >>> 1);

+                    else

+                        v >>>= 1;

+                }

+                table[i] = v;

+            }

+        }

+        public CRC32()

+        {

+            reset();

+        }

+        @Override

+        public CRC32 clone()

+        {

+            CRC32 c = new CRC32();

+            c.value = value;

+            return c;

+        }

+        public void reset()

+        {

+            value = 0xffffffff;

+        }

+        public void update(int b)

+        {

+            value = table[(value ^ (b & 0xff)) & 0xff] ^ (value >>> 8);

+        }

+        public void update(byte b[], int offset, int len)

+        {

+            for (int i = 0; i < len; ++i)

+            {

+                update(b[offset + i]);

+            }

+        }

+        public void update(byte b[])

+        {

+            update(b, 0, b.length);

+        }

+        public long getValue()

+        {

+            return (value ^ 0xffffffff) & 0xffffffffL;

+        }

+    }

 }

diff --git a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Messages.java b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Messages.java
index c4e600f..e2cf76b 100644
--- a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Messages.java
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Messages.java
@@ -1,10 +1,10 @@
 /*******************************************************************************

- * Copyright (c) 2010, 2020 SAP AG and others.

+ * Copyright (c) 2010, 2021 SAP AG and others.

  * All rights reserved. This program and the accompanying materials

  * are made available under the terms of the Eclipse Public License 2.0

  * which accompanies this distribution, and is available at

- * https://www.eclipse.org/legal/epl-2.0/
- *
+ * https://www.eclipse.org/legal/epl-2.0/

+ *

  * SPDX-License-Identifier: EPL-2.0

  *

  * Contributors:

@@ -41,6 +41,7 @@
     public static String ExportHprof_SegmentSizeMismatch;

     public static String ExportHprof_SegmentTooLong;

     public static String GZIPInputStream2_BadHeaderCRC;

+    public static String GZIPInputStream2_BadHeaderFlag;

     public static String GZIPInputStream2_BadTrailerCRC;

     public static String GZIPInputStream2_BadTrailerLength;

     public static String GZIPInputStream2_NotAGzip;

diff --git a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/messages.properties b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/messages.properties
index 2381af1..faa87d1 100644
--- a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/messages.properties
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/messages.properties
@@ -35,6 +35,7 @@
 ExportHprof_SegmentSizeMismatch=Heap dump segment {0} expected size {1} actual size {2} at 0x{3}

 ExportHprof_SegmentTooLong=Heap dump segment {0} at 0x{1} is too long: {2}, continuing...

 GZIPInputStream2_BadHeaderCRC=Bad header CRC

+GZIPInputStream2_BadHeaderFlag=Bad header flag

 GZIPInputStream2_BadTrailerCRC=Bad CRC trailer

 GZIPInputStream2_BadTrailerLength=bad length

 GZIPInputStream2_NotAGzip=Not a Gzip

diff --git a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/internal/acquire/AcquireDialog.java b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/internal/acquire/AcquireDialog.java
index 5dbe524..9b3957d 100644
--- a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/internal/acquire/AcquireDialog.java
+++ b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/internal/acquire/AcquireDialog.java
@@ -217,8 +217,7 @@
         tableComposite.layout();

         tableComposite.pack();

 

-        Control control = localVMsTable.getParent();

-        PlatformUI.getWorkbench().getHelpSystem().setHelp(control, "org.eclipse.mat.ui.help.acquire_arguments"); //$NON-NLS-1$

+        PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, "org.eclipse.mat.ui.help.acquire_arguments"); //$NON-NLS-1$

 

         italicFont = resourceManager.createFont(FontDescriptor.createFrom(column.getParent().getFont()).setStyle(SWT.ITALIC));

 

@@ -583,6 +582,21 @@
         getContainer().updateButtons();

     }

 

+    @Override

+    public void performHelp()

+    {

+        if (localVMsTable.getSelectionIndex() >= 0)

+        {

+            AnnotatedObjectArgumentsSet argumentsSet = (AnnotatedObjectArgumentsSet) localVMsTable.getSelection()[0].getData();

+            String helpUrl = argumentsSet.getDescriptor().getHelpUrl();

+            if (helpUrl != null)

+            {

+                PlatformUI.getWorkbench().getHelpSystem().displayHelpResource(helpUrl);

+            }

+        }

+        PlatformUI.getWorkbench().getHelpSystem().displayHelp("org.eclipse.mat.ui.help.acquire_arguments"); //$NON-NLS-1$

+    }

+

     private static class GetVMListRunnable implements IRunnableWithProgress

 	{

 		private IStatus status;

diff --git a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/internal/acquire/ProviderArgumentsWizardPage.java b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/internal/acquire/ProviderArgumentsWizardPage.java
index 5b050fb..3c2cf49 100644
--- a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/internal/acquire/ProviderArgumentsWizardPage.java
+++ b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/internal/acquire/ProviderArgumentsWizardPage.java
@@ -70,7 +70,7 @@
         tableComposite.layout();

         tableComposite.pack();

 

-        //PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, "org.eclipse.mat.ui.help.acquire_arguments"); //$NON-NLS-1$

+        PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, "org.eclipse.mat.ui.help.acquire_arguments"); //$NON-NLS-1$

         composite.setContent(tableComposite);

         setControl(composite);

 

@@ -93,7 +93,7 @@
         if (isCurrentPage())

         {

             IPreferenceStore prefs = MemoryAnalyserPlugin.getDefault().getPreferenceStore();

-            if (!prefs.getBoolean(HIDE_QUERY_HELP))

+            if (!prefs.getBoolean(HIDE_QUERY_HELP) || helpPopup != null && helpPopup.getShell() != null)

             {

                 relocateHelp(true);

             }

diff --git a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/internal/acquire/ProviderConfigurationWizardPage.java b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/internal/acquire/ProviderConfigurationWizardPage.java
index 9a60d90..c9c46a1 100644
--- a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/internal/acquire/ProviderConfigurationWizardPage.java
+++ b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/internal/acquire/ProviderConfigurationWizardPage.java
@@ -113,7 +113,7 @@
                     table.providerSelected(argumentsSet);

                     onFocus(null);

                     IPreferenceStore prefs = MemoryAnalyserPlugin.getDefault().getPreferenceStore();

-                    if (!prefs.getBoolean(HIDE_QUERY_HELP))

+                    if (!prefs.getBoolean(HIDE_QUERY_HELP) || helpPopup != null && helpPopup.getShell() != null)

                     {

                         relocateHelp(true);

                     }

@@ -124,7 +124,7 @@
         // If a process is selected, make the configuration provider match the process

         acquireDialog.addProcessSelectionListener(this);

 

-        //PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, "org.eclipse.mat.ui.help.query_arguments"); //$NON-NLS-1$

+        PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, "org.eclipse.mat.ui.help.acquire_arguments"); //$NON-NLS-1$

 

         Listener listener = new Listener()

         {