Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMykola Nikishov2018-03-06 19:50:16 +0000
committerMykola Nikishov2018-03-12 11:23:36 +0000
commita2dbd434889870ceecde595bee0143c898c5eb41 (patch)
treedb0a15ebd7401933cfa15e385306cc03716dc758
parent5a00dcccfbbe404bca8c620b2fbe45304f611d6f (diff)
downloadrt.equinox.p2-a2dbd434889870ceecde595bee0143c898c5eb41.tar.gz
rt.equinox.p2-a2dbd434889870ceecde595bee0143c898c5eb41.tar.xz
rt.equinox.p2-a2dbd434889870ceecde595bee0143c898c5eb41.zip
Bug 532118 - Reduce number of calls to java.security.MessageDigest.update(byte)
MD5Verifier and ChecksumVerifier are responsible for calculating artifact's checksum and must extend ProcessingStep to fulfill API requirements. ProcessingStep is effectively an unbuffered OutputStream with a method to process a single byte of artifact's data. In respect to MessageDigest, this means updating digest with every new single byte of data. So, for 16 Kb of data, both classes will call MessageDigest's update(byte) 16K times while only 1 call is enough. Introduce MessageDigestProcessingStep that accumulates incoming bytes in the internal buffer and immediately forwards them to the destination stream. As soon as buffer is full, it will update MessageDigest. AbstractBufferingStep could be used as a base class instead but it is too risky to adapt it. java.nio.ByteBuffer fits the bill almost ideally. One caveat is it communicates 'buffer is full' by throwing BufferOverflowException. There are no specific reasons for buffer's size of 16K except an assumption that it should not cause any problems: installing 1000 artifacts with 2 checksums per artifact would increase memory footprint for about 3200K. Similar approach has been used already [1], [2], claiming 5x speedup. This should make [3] less of a problem. [1] 5b2f061cd8ecf96a37783657e7ee7a0110c9d26d [2] Bug 405716 [3] Bug 532036 Change-Id: I1ba0d36267b3f07103c22b93f92c41765e33c8cc Signed-off-by: Mykola Nikishov <mn@mn.com.ua>
-rw-r--r--bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/checksum/ChecksumVerifier.java21
-rw-r--r--bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/checksum/MessageDigestProcessingStep.java69
-rw-r--r--bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/md5/MD5Verifier.java26
3 files changed, 80 insertions, 36 deletions
diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/checksum/ChecksumVerifier.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/checksum/ChecksumVerifier.java
index 2aa55ea9c..ed0430350 100644
--- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/checksum/ChecksumVerifier.java
+++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/checksum/ChecksumVerifier.java
@@ -10,7 +10,6 @@
*******************************************************************************/
package org.eclipse.equinox.internal.p2.artifact.processors.checksum;
-import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Optional;
@@ -18,17 +17,15 @@ import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.equinox.internal.p2.artifact.repository.Activator;
import org.eclipse.equinox.internal.p2.repository.helpers.ChecksumHelper;
-import org.eclipse.equinox.internal.provisional.p2.artifact.repository.processing.ProcessingStep;
import org.eclipse.equinox.p2.core.IProvisioningAgent;
import org.eclipse.equinox.p2.core.ProvisionException;
import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor;
import org.eclipse.equinox.p2.repository.artifact.IProcessingStepDescriptor;
import org.eclipse.osgi.util.NLS;
-final public class ChecksumVerifier extends ProcessingStep {
+final public class ChecksumVerifier extends MessageDigestProcessingStep {
private String expectedChecksum;
- private MessageDigest messageDigest;
private String algorithmName;
private String algorithmId;
@@ -66,21 +63,11 @@ final public class ChecksumVerifier extends ProcessingStep {
}
@Override
- final public void write(int b) throws IOException {
- messageDigest.update((byte) b);
- getDestination().write(b);
- }
-
- @Override
- final public void close() throws IOException {
- byte[] hashBytes = messageDigest.digest();
- String hash = ChecksumHelper.toHexString(hashBytes);
-
+ final protected void onClose(String digestString) {
// if the hashes don't line up set the status to error.
- if (!hash.equals(expectedChecksum))
+ if (!digestString.equals(expectedChecksum))
// TODO like ProvisionException.ARTIFACT_MD5_NOT_MATCH but for any checksum
- setStatus(new Status(IStatus.ERROR, Activator.ID, ProvisionException.ARTIFACT_MD5_NOT_MATCH, NLS.bind(Messages.Error_unexpected_checksum, new Object[] {algorithmName, expectedChecksum, hash}), null));
- super.close();
+ setStatus(new Status(IStatus.ERROR, Activator.ID, ProvisionException.ARTIFACT_MD5_NOT_MATCH, NLS.bind(Messages.Error_unexpected_checksum, new Object[] {algorithmName, expectedChecksum, digestString}), null));
}
}
diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/checksum/MessageDigestProcessingStep.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/checksum/MessageDigestProcessingStep.java
new file mode 100644
index 000000000..3fef57d03
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/checksum/MessageDigestProcessingStep.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2018, 2018 Mykola Nikishov.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Mykola Nikishov - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.artifact.processors.checksum;
+
+import java.io.IOException;
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.security.MessageDigest;
+import org.eclipse.equinox.internal.p2.repository.helpers.ChecksumHelper;
+import org.eclipse.equinox.internal.provisional.p2.artifact.repository.processing.ProcessingStep;
+
+/**
+ * @noreference This class is not intended to be referenced by clients.
+ */
+public abstract class MessageDigestProcessingStep extends ProcessingStep {
+
+ protected MessageDigest messageDigest;
+ private static final int BUFFER_SIZE = 16 * 1024;
+ private ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
+
+ @Override
+ // TODO template method, should be final but MD5Verifier prevents this
+ public void write(int b) throws IOException {
+ getDestination().write(b);
+
+ // TODO exceptions are expensive
+ try {
+ buffer.put((byte) b);
+ } catch (BufferOverflowException e) {
+ processBufferredBytes();
+ buffer.put((byte) b);
+ }
+ }
+
+ private void processBufferredBytes() {
+ buffer.flip();
+ updateDigest();
+ buffer.clear();
+ }
+
+ private void updateDigest() {
+ messageDigest.update(buffer);
+ }
+
+ @Override
+ // TODO should be final but MD5Verifier prevents this
+ public void close() throws IOException {
+ processBufferredBytes();
+ String digestString = digest();
+ onClose(digestString);
+ super.close();
+ }
+
+ private String digest() {
+ byte[] digestBytes = messageDigest.digest();
+ return ChecksumHelper.toHexString(digestBytes);
+ }
+
+ protected abstract void onClose(String digestString);
+
+}
diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/md5/MD5Verifier.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/md5/MD5Verifier.java
index 50d2ff0d3..3d867b135 100644
--- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/md5/MD5Verifier.java
+++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/md5/MD5Verifier.java
@@ -8,17 +8,16 @@
* Contributors:
* compeople AG (Stefan Liebig) - initial API and implementation
* IBM Corporation - ongoing development
+ * Mykola Nikishov - ongoing development
*******************************************************************************/
package org.eclipse.equinox.internal.p2.artifact.processors.md5;
-import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
+import org.eclipse.equinox.internal.p2.artifact.processors.checksum.MessageDigestProcessingStep;
import org.eclipse.equinox.internal.p2.artifact.repository.Activator;
-import org.eclipse.equinox.internal.p2.repository.helpers.ChecksumHelper;
-import org.eclipse.equinox.internal.provisional.p2.artifact.repository.processing.ProcessingStep;
import org.eclipse.equinox.p2.core.IProvisioningAgent;
import org.eclipse.equinox.p2.core.ProvisionException;
import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor;
@@ -26,10 +25,9 @@ import org.eclipse.equinox.p2.repository.artifact.IProcessingStepDescriptor;
import org.eclipse.osgi.util.NLS;
@Deprecated
-public class MD5Verifier extends ProcessingStep {
+public class MD5Verifier extends MessageDigestProcessingStep {
protected String expectedMD5;
- private MessageDigest md5;
public MD5Verifier() {
super();
@@ -60,26 +58,16 @@ public class MD5Verifier extends ProcessingStep {
if (expectedMD5 == null || expectedMD5.length() != 32)
setStatus(new Status(code, Activator.ID, NLS.bind(Messages.Error_invalid_hash, expectedMD5)));
try {
- md5 = MessageDigest.getInstance("MD5"); //$NON-NLS-1$
+ messageDigest = MessageDigest.getInstance("MD5"); //$NON-NLS-1$
} catch (NoSuchAlgorithmException e) {
setStatus(new Status(code, Activator.ID, Messages.Error_MD5_unavailable, e));
}
}
@Override
- public void write(int b) throws IOException {
- md5.update((byte) b);
- getDestination().write(b);
- }
-
- @Override
- public void close() throws IOException {
- byte[] digest = md5.digest();
- String buf = ChecksumHelper.toHexString(digest);
-
+ protected void onClose(String digestString) {
// if the hashes don't line up set the status to error.
- if (!buf.equals(expectedMD5))
- setStatus(new Status(IStatus.ERROR, Activator.ID, ProvisionException.ARTIFACT_MD5_NOT_MATCH, NLS.bind(Messages.Error_unexpected_hash, expectedMD5, buf), null));
- super.close();
+ if (!digestString.equals(expectedMD5))
+ setStatus(new Status(IStatus.ERROR, Activator.ID, ProvisionException.ARTIFACT_MD5_NOT_MATCH, NLS.bind(Messages.Error_unexpected_hash, expectedMD5, digestString), null));
}
}

Back to the top