From cf2305d55fd88fe463d0eafecedfc9dfd55a7112 Mon Sep 17 00:00:00 2001 From: Michael Vorburger Date: Sat, 4 Jan 2025 19:48:07 +0100 Subject: [PATCH] clean(core): Factor out ResourceHasher --- ...IntegrityValidatingDelegatingResource.java | 31 ++++------- .../io/hashbrown/MultihashWithMultibase.java | 4 ++ .../common/io/hashbrown/Multihashes.java | 3 +- .../common/io/hashbrown/ResourceHasher.java | 52 +++++++++++++++++++ 4 files changed, 66 insertions(+), 24 deletions(-) create mode 100644 java/dev/enola/common/io/hashbrown/ResourceHasher.java diff --git a/java/dev/enola/common/io/hashbrown/IntegrityValidatingDelegatingResource.java b/java/dev/enola/common/io/hashbrown/IntegrityValidatingDelegatingResource.java index 56d36a3bf..f2dcc4bfd 100644 --- a/java/dev/enola/common/io/hashbrown/IntegrityValidatingDelegatingResource.java +++ b/java/dev/enola/common/io/hashbrown/IntegrityValidatingDelegatingResource.java @@ -17,7 +17,6 @@ */ package dev.enola.common.io.hashbrown; -import com.google.common.hash.Hasher; import com.google.common.io.ByteSource; import com.google.common.io.CharSource; @@ -72,31 +71,19 @@ public CharSource charSource() { private synchronized void validate() { if (validated) return; - var delegateByteSource = delegate.byteSource(); - var hashFunction = Multihashes.toGuavaHashFunction(expectedHash.multihash()); - - Hasher hasher; - var optSize = delegateByteSource.sizeIfKnown(); - if (optSize.isPresent()) hasher = hashFunction.newHasher(Math.toIntExact(optSize.get())); - else hasher = hashFunction.newHasher(); - - try (var is = delegateByteSource.openBufferedStream()) { - var read = is.read(); - while (read != -1) { - hasher.putByte((byte) read); - read = is.read(); + var resourceHasher = new ResourceHasher(); + try { + var actualHash = resourceHasher.hash(delegate, expectedHash.multihash().getType()); + if (!expectedHash.multihash().equals(actualHash)) { + throw new IntegrityViolationException( + "Expected " + + expectedHash + + " but got " + + Multihashes.toString(actualHash, expectedHash.multibase())); } } catch (IOException e) { throw new UncheckedIOException(e); } - var hashCode = hasher.hash(); - var actualBytes = hashCode.asBytes(); - var actualHash = expectedHash.copy(actualBytes); - - if (!expectedHash.equals(actualHash)) { - throw new IntegrityViolationException( - "Expected " + expectedHash + " but got " + actualHash); - } validated = true; } diff --git a/java/dev/enola/common/io/hashbrown/MultihashWithMultibase.java b/java/dev/enola/common/io/hashbrown/MultihashWithMultibase.java index 1931b5f25..a4c14b238 100644 --- a/java/dev/enola/common/io/hashbrown/MultihashWithMultibase.java +++ b/java/dev/enola/common/io/hashbrown/MultihashWithMultibase.java @@ -56,6 +56,10 @@ public Multihash multihash() { return multihash; } + public Multibase.Base multibase() { + return multibase; + } + @Override public boolean equals(Object o) { if (!(o instanceof MultihashWithMultibase other)) return false; diff --git a/java/dev/enola/common/io/hashbrown/Multihashes.java b/java/dev/enola/common/io/hashbrown/Multihashes.java index de7cfca9d..82e3f7914 100644 --- a/java/dev/enola/common/io/hashbrown/Multihashes.java +++ b/java/dev/enola/common/io/hashbrown/Multihashes.java @@ -26,8 +26,7 @@ /** Extension methods for {@link io.ipfs.multihash.Multihash}. */ public final class Multihashes { - public static HashFunction toGuavaHashFunction(Multihash multihash) { - var type = multihash.getType(); + public static HashFunction toGuavaHashFunction(Multihash.Type type) { return switch (type) { // NB: Please BEWARE of what types are added here; the (sub)selection is intentional! diff --git a/java/dev/enola/common/io/hashbrown/ResourceHasher.java b/java/dev/enola/common/io/hashbrown/ResourceHasher.java new file mode 100644 index 000000000..df3e090ec --- /dev/null +++ b/java/dev/enola/common/io/hashbrown/ResourceHasher.java @@ -0,0 +1,52 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright 2025 The Enola Authors + * + * Licensed 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 + * + * https://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 dev.enola.common.io.hashbrown; + +import com.google.common.hash.Hasher; + +import dev.enola.common.io.resource.Resource; + +import io.ipfs.multihash.Multihash; + +import java.io.IOException; + +public class ResourceHasher { + + @SuppressWarnings("UnstableApiUsage") + public Multihash hash(Resource resource, Multihash.Type hashType) throws IOException { + var hashFunction = Multihashes.toGuavaHashFunction(hashType); + var byteSource = resource.byteSource(); + + Hasher hasher; + var optSize = byteSource.sizeIfKnown(); + if (optSize.isPresent()) hasher = hashFunction.newHasher(Math.toIntExact(optSize.get())); + else hasher = hashFunction.newHasher(); + + try (var is = byteSource.openBufferedStream()) { + var read = is.read(); + while (read != -1) { + hasher.putByte((byte) read); + read = is.read(); + } + } + + var hashCode = hasher.hash(); + var actualBytes = hashCode.asBytes(); + return new Multihash(hashType, actualBytes); + } +}