mirror of
https://github.com/packwiz/packwiz-installer.git
synced 2025-04-19 13:06:30 +02:00
Add murmur2 support
This commit is contained in:
parent
d986b39aa5
commit
165c8cc172
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user