Add murmur2 support

This commit is contained in:
comp500 2019-06-24 03:23:26 +01:00
parent d986b39aa5
commit 165c8cc172
No known key found for this signature in database
GPG Key ID: 214C822FFEC586B5
7 changed files with 282 additions and 14 deletions

View File

@ -266,17 +266,21 @@ public class UpdateManager {
}
try {
Object hash;
String fileHashFormat;
if (f.linkedFile != null) {
hash = f.linkedFile.getHash();
fileHashFormat = f.linkedFile.download.hashFormat;
} else {
hash = f.getHash();
fileHashFormat = f.hashFormat;
}
Source src = f.getSource(indexUri);
GeneralHashingSource fileSource = HashUtils.getHasher(f.hashFormat).getHashingSource(src);
GeneralHashingSource fileSource = HashUtils.getHasher(fileHashFormat).getHashingSource(src);
Buffer data = new Buffer();
Okio.buffer(fileSource).readAll(data);
Object hash;
if (f.linkedFile != null) {
hash = f.linkedFile.getHash();
} else {
hash = f.getHash();
}
if (fileSource.hashIsEqual(hash)) {
Files.createDirectories(Paths.get(opts.packFolder, f.getDestURI().toString()).getParent());
Files.copy(data.inputStream(), Paths.get(opts.packFolder, f.getDestURI().toString()), StandardCopyOption.REPLACE_EXISTING);
@ -338,6 +342,7 @@ public class UpdateManager {
} else {
progress = "Failed to download: " + ret.err.getMessage();
}
ret.err.printStackTrace();
} else if (ret.file != null) {
progress = "Downloaded " + ret.file.getName();
} else {

View File

@ -96,7 +96,8 @@ public class IndexFile {
public URI getDestURI() {
if (metafile && linkedFile != null) {
return file.resolve(linkedFile.filename);
// TODO: URIs are bad
return file.resolve(linkedFile.filename.replace(" ", "%20"));
} else {
return file;
}

View File

@ -60,7 +60,7 @@ public class ModFile {
if (download.hashFormat == null) {
throw new Exception("Metadata file doesn't have a hash format");
}
return HashUtils.getHash(download.hash, download.hashFormat);
return HashUtils.getHash(download.hashFormat, download.hash);
}
public boolean isOptional() {

View File

@ -6,13 +6,14 @@ import java.util.Map;
public class HashUtils {
private static final Map<String, IHasher> hashTypeConversion = new HashMap<String, IHasher>();
static {
hashTypeConversion.put("sha256", new HasherHashingSource("sha256"));
hashTypeConversion.put("sha256", new HashingSourceHasher("sha256"));
hashTypeConversion.put("murmur2", new Murmur2Hasher());
}
public static IHasher getHasher(String type) throws Exception {
IHasher hasher = hashTypeConversion.get(type);
if (hasher == null) {
throw new Exception("Hash type not supported!");
throw new Exception("Hash type not supported: " + type);
}
return hasher;
}
@ -21,7 +22,8 @@ public class HashUtils {
if (hashTypeConversion.containsKey(type)) {
return hashTypeConversion.get(type).getHash(value);
}
throw new Exception("Hash type not supported!");
throw new Exception("Hash type not supported: " + type);
}
}

View File

@ -3,10 +3,10 @@ package link.infra.packwiz.installer.metadata.hash;
import okio.HashingSource;
import okio.Source;
public class HasherHashingSource implements IHasher {
public class HashingSourceHasher implements IHasher {
String type;
public HasherHashingSource(String type) {
public HashingSourceHasher(String type) {
this.type = type;
}

View File

@ -0,0 +1,94 @@
package link.infra.packwiz.installer.metadata.hash;
import java.io.IOException;
import okio.Buffer;
import okio.Source;
public class Murmur2Hasher implements IHasher {
private class Murmur2GeneralHashingSource extends GeneralHashingSource {
Murmur2Hash value;
Buffer internalBuffer = new Buffer();
Buffer tempBuffer = new Buffer();
Source delegate;
public Murmur2GeneralHashingSource(Source delegate) {
super(delegate);
this.delegate = delegate;
}
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long out = delegate.read(tempBuffer, byteCount);
if (out > -1) {
sink.write(tempBuffer.clone(), out);
internalBuffer.write(tempBuffer, out);
}
return out;
}
@Override
public Object getHash() {
if (value == null) {
byte[] data = computeNormalizedArray(internalBuffer.readByteArray());
value = new Murmur2Hash(Murmur2Lib.hash32(data, data.length, 1));
}
return value;
}
// Credit to https://github.com/modmuss50/CAV2/blob/master/murmur.go
private byte[] computeNormalizedArray(byte[] input) {
byte[] output = new byte[input.length];
int num = 0;
for (int i = 0; i < input.length; i++) {
byte b = input[i];
if (!(b == 9 || b == 10 || b == 13 || b == 32)) {
output[num] = b;
num++;
}
}
byte[] outputTrimmed = new byte[num];
System.arraycopy(output, 0, outputTrimmed, 0, num);
return outputTrimmed;
}
}
private class Murmur2Hash {
int value;
private Murmur2Hash(String value) {
// Parsing as long then casting to int converts values gt int max value but lt uint max value
// into negatives. I presume this is how the murmur2 code handles this.
this.value = (int)Long.parseLong(value);
}
private Murmur2Hash(int value) {
this.value = value;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Murmur2Hash)) {
return false;
}
Murmur2Hash objHash = (Murmur2Hash) obj;
return value == objHash.value;
}
@Override
public String toString() {
return "murmur2: " + value;
}
}
@Override
public GeneralHashingSource getHashingSource(Source delegate) {
return new Murmur2GeneralHashingSource(delegate);
}
@Override
public Object getHash(String value) {
return new Murmur2Hash(value);
}
}

View File

@ -0,0 +1,166 @@
// Obtained from https://github.com/prasanthj/hasher/blob/master/src/main/java/hasher/Murmur2.java
/**
* Copyright 2014 Prasanth Jayachandran
*
* 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
*
* 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 link.infra.packwiz.installer.metadata.hash;
/**
* Murmur2 32 and 64 bit variants.
* 32-bit Java port of https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash2.cpp#37
* 64-bit Java port of https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash2.cpp#96
*/
public class Murmur2Lib {
// Constants for 32-bit variant
private static final int M_32 = 0x5bd1e995;
private static final int R_32 = 24;
// Constants for 64-bit variant
private static final long M_64 = 0xc6a4a7935bd1e995L;
private static final int R_64 = 47;
private static final int DEFAULT_SEED = 0;
/**
* Murmur2 32-bit variant.
*
* @param data - input byte array
* @return - hashcode
*/
public static int hash32(byte[] data) {
return hash32(data, data.length, DEFAULT_SEED);
}
/**
* Murmur2 32-bit variant.
*
* @param data - input byte array
* @param length - length of array
* @param seed - seed. (default 0)
* @return - hashcode
*/
public static int hash32(byte[] data, int length, int seed) {
int h = seed ^ length;
int len_4 = length >> 2;
// body
for (int i = 0; i < len_4; i++) {
int i_4 = i << 2;
int k = (data[i_4] & 0xff)
| ((data[i_4 + 1] & 0xff) << 8)
| ((data[i_4 + 2] & 0xff) << 16)
| ((data[i_4 + 3] & 0xff) << 24);
// mix functions
k *= M_32;
k ^= k >>> R_32;
k *= M_32;
h *= M_32;
h ^= k;
}
// tail
int len_m = len_4 << 2;
int left = length - len_m;
if (left != 0) {
if (left >= 3) {
h ^= (int) data[length - 3] << 16;
}
if (left >= 2) {
h ^= (int) data[length - 2] << 8;
}
if (left >= 1) {
h ^= (int) data[length - 1];
}
h *= M_32;
}
// finalization
h ^= h >>> 13;
h *= M_32;
h ^= h >>> 15;
return h;
}
/**
* Murmur2 64-bit variant.
*
* @param data - input byte array
* @return - hashcode
*/
public static long hash64(final byte[] data) {
return hash64(data, data.length, DEFAULT_SEED);
}
/**
* Murmur2 64-bit variant.
*
* @param data - input byte array
* @param length - length of array
* @param seed - seed. (default 0)
* @return - hashcode
*/
public static long hash64(final byte[] data, int length, int seed) {
long h = (seed & 0xffffffffl) ^ (length * M_64);
int length8 = length >> 3;
// body
for (int i = 0; i < length8; i++) {
final int i8 = i << 3;
long k = ((long) data[i8] & 0xff)
| (((long) data[i8 + 1] & 0xff) << 8)
| (((long) data[i8 + 2] & 0xff) << 16)
| (((long) data[i8 + 3] & 0xff) << 24)
| (((long) data[i8 + 4] & 0xff) << 32)
| (((long) data[i8 + 5] & 0xff) << 40)
| (((long) data[i8 + 6] & 0xff) << 48)
| (((long) data[i8 + 7] & 0xff) << 56);
// mix functions
k *= M_64;
k ^= k >>> R_64;
k *= M_64;
h ^= k;
h *= M_64;
}
// tail
int tailStart = length8 << 3;
switch (length - tailStart) {
case 7:
h ^= (long) (data[tailStart + 6] & 0xff) << 48;
case 6:
h ^= (long) (data[tailStart + 5] & 0xff) << 40;
case 5:
h ^= (long) (data[tailStart + 4] & 0xff) << 32;
case 4:
h ^= (long) (data[tailStart + 3] & 0xff) << 24;
case 3:
h ^= (long) (data[tailStart + 2] & 0xff) << 16;
case 2:
h ^= (long) (data[tailStart + 1] & 0xff) << 8;
case 1:
h ^= (long) (data[tailStart] & 0xff);
h *= M_64;
}
// finalization
h ^= h >>> R_64;
h *= M_64;
h ^= h >>> R_64;
return h;
}
}