Skip to content

Commit

Permalink
Reduce the odds of a collision in image keys
Browse files Browse the repository at this point in the history
Fixes #27
  • Loading branch information
Sam Judd committed Oct 20, 2013
1 parent 775639a commit 9600642
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 11 deletions.
91 changes: 87 additions & 4 deletions library/src/com/bumptech/glide/resize/ImageManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
Expand Down Expand Up @@ -68,6 +74,7 @@ public class ImageManager {
private final MemoryCache memoryCache;
private final ImageResizer resizer;
private final DiskCache diskCache;
private final SafeKeyGenerator safeKeyGenerator = new SafeKeyGenerator();

//special downsampler that doesn't check exif, and assumes inWidth and inHeight == outWidth and outHeight so it
//doesn't need to read the image header for size information
Expand Down Expand Up @@ -389,7 +396,7 @@ public void onImageRemoved(Bitmap removed) {
public ImageManagerJob getImage(String id, StreamLoader streamLoader, Transformation transformation, Downsampler downsampler, int width, int height, LoadedCallback cb) {
if (shutdown) return null;

final String key = getKey(id, transformation.getId(), downsampler, width, height);
final String key = safeKeyGenerator.getSafeKey(id, transformation, downsampler, width, height);

ImageManagerJob job = null;
if (!returnFromCache(key, cb)) {
Expand Down Expand Up @@ -631,8 +638,84 @@ private void putInMemoryCache(String key, final Bitmap bitmap) {
}
}

private static String getKey(String id, String transformationId, Downsampler downsampler, int width, int height) {
return String.valueOf(Util.hash(id.hashCode(), downsampler.getId().hashCode(),
transformationId.hashCode(), width, height));
private static class SafeKeyGenerator {
private final Map<LoadId, String> loadIdToSafeHash = new HashMap<LoadId, String>();
private final ByteBuffer byteBuffer = ByteBuffer.allocate(8);
private MessageDigest messageDigest;

public SafeKeyGenerator() {
try {
messageDigest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}

public String getSafeKey(String id, Transformation transformation, Downsampler downsampler, int width, int height) {
LoadId loadId = new LoadId(id, transformation.getId(), downsampler.getId(), width, height);
String safeKey = loadIdToSafeHash.get(loadId);
if (safeKey == null) {
try {
safeKey = loadId.generateSafeKey();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
loadIdToSafeHash.put(loadId, safeKey);
}
return safeKey;
}

private class LoadId {
private final String id;
private final String transformationId;
private final String downsamplerId;
private final int width;
private final int height;

public LoadId(String id, String transformationId, String downsamplerId, int width, int height) {
this.id = id;
this.transformationId = transformationId;
this.downsamplerId = downsamplerId;
this.width = width;
this.height = height;
}

public String generateSafeKey() throws UnsupportedEncodingException {
messageDigest.update(id.getBytes("UTF-8"));
messageDigest.update(transformationId.getBytes("UTF-8"));
messageDigest.update(downsamplerId.getBytes("UTF-8"));
byteBuffer.position(0);
byteBuffer.putInt(width);
byteBuffer.putInt(height);
messageDigest.update(byteBuffer.array());
return Util.sha256BytesToHex(messageDigest.digest());
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

LoadId loadId = (LoadId) o;

if (height != loadId.height) return false;
if (width != loadId.width) return false;
if (!downsamplerId.equals(loadId.downsamplerId)) return false;
if (!id.equals(loadId.id)) return false;
if (!transformationId.equals(loadId.transformationId)) return false;

return true;
}

@Override
public int hashCode() {
int result = id.hashCode();
result = 31 * result + transformationId.hashCode();
result = 31 * result + downsamplerId.hashCode();
result = 31 * result + width;
result = 31 * result + height;
return result;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
*/
public class DiskLruCacheWrapper implements DiskCache {

private static final int APP_VERSION = 1;
private static final int VALUE_COUNT = 1;
private static DiskLruCache CACHE = null;
private static DiskLruCacheWrapper WRAPPER = null;

private synchronized static DiskLruCache getDiskLruCache(File directory, int maxSize) throws IOException {
if (CACHE == null) {
CACHE = DiskLruCache.open(directory, 0, 1, maxSize);
CACHE = DiskLruCache.open(directory, APP_VERSION, VALUE_COUNT, maxSize);
}
return CACHE;
}
Expand Down
21 changes: 15 additions & 6 deletions library/src/com/bumptech/glide/util/Util.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
package com.bumptech.glide.util;

public class Util {
private static final int PRIME = 31;
private static final char[] hexArray = "0123456789abcdef".toCharArray();
private static final char[] sha256Chars = new char[64]; //32 bytes from sha-256 -> 64 hex chars

public static int hash(int... hashes) {
int result = 1;
for (int hash : hashes) {
result *= PRIME * hash;
public static String sha256BytesToHex(byte[] bytes) {
return bytesToHex(bytes, sha256Chars);
}

// Taken from:
// http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java/9655275#9655275
private static String bytesToHex(byte[] bytes, char[] hexChars) {
int v;
for ( int j = 0; j < bytes.length; j++ ) {
v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return result;
return new String(hexChars);
}
}

0 comments on commit 9600642

Please sign in to comment.