/*
MD5FilterAlgorithms.java
(c) 2015-2016 Edward Swartz
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
*/
package v9t9.common.files;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import v9t9.common.files.IMD5SumFilter.FilterSegment;
import ejs.base.utils.HexUtils;
/**
* @author ejs
*
*/
public class MD5FilterAlgorithms {
private MD5FilterAlgorithms() { }
public static String ALGORITHM_FULL = "full";
public static String ALGORITHM_SEGMENT = "segment";
public static String ALGORITHM_GROM = "grom";
public static class FullContentFilter implements IMD5SumFilter {
public static final FullContentFilter INSTANCE = new FullContentFilter();
private FullContentFilter() { }
@Override
public String getId() {
return ALGORITHM_FULL;
}
@Override
public void fillSegments(int contentLength, Collection<FilterSegment> segments) {
int fragment = contentLength & 0x0fff;
if (fragment > 0 && fragment < 0x400 && contentLength >= 0x400) {
// remove cruft bolted to the end
contentLength -= fragment;
}
segments.add(new FilterSegment(0, contentLength));
}
}
public static class FileSegmentFilter implements IMD5SumFilter {
private String id;
private FilterSegment segment;
public FileSegmentFilter(int offset, int length) {
this(new FilterSegment(offset, length));
}
public FileSegmentFilter(FilterSegment segment) {
this.segment = segment;
this.id = ALGORITHM_SEGMENT + ":"
+ HexUtils.toHex4(segment.offset)
+ (segment.length >= 0 ? "+" + HexUtils.toHex4(segment.length) : "");
}
@Override
public String getId() {
return this.id;
}
@Override
public void fillSegments(int contentLength, Collection<FilterSegment> segments) {
segments.add(segment);
}
public int getLength() {
return segment.length;
}
public int getOffset() {
return segment.offset;
}
}
public static class MultiFileSegmentFilter implements IMD5SumFilter {
private String id;
private Collection<FilterSegment> segments;
public MultiFileSegmentFilter(Collection<FilterSegment> segments) {
this.segments = segments;
StringBuilder sb = new StringBuilder();
for (FilterSegment segment : segments) {
if (sb.length() > 0)
sb.append(':');
sb.append(HexUtils.toHex4(segment.offset)).append('+').append(HexUtils.toHex4(segment.length));
}
this.id = sb.toString();
}
@Override
public String getId() {
return id;
}
@Override
public void fillSegments(int contentLength, Collection<FilterSegment> segments) {
segments.addAll(this.segments);
}
}
/**
* Consider only every 6k of each 8k segment of GROM
*/
public static class GromFilter implements IMD5SumFilter {
public static final GromFilter INSTANCE = new GromFilter();
private GromFilter() { }
@Override
public String getId() {
return ALGORITHM_GROM;
}
@Override
public void fillSegments(int contentLength, Collection<FilterSegment> segments) {
int fragment = contentLength & 0x0fff;
if (fragment > 0 && fragment < 0x400 && contentLength > 0x400) {
// remove cruft bolted to the end
contentLength -= fragment;
}
for (int offs = 0; offs < contentLength; offs += 0x2000) {
int limit = Math.min(offs + 0x1800, contentLength);
segments.add(new FilterSegment(offs, limit - offs));
}
}
}
/**
* @param algorithm
* @return
*/
public static IMD5SumFilter create(String algorithm) {
if (algorithm == null)
return null;
if (ALGORITHM_FULL.equals(algorithm))
return FullContentFilter.INSTANCE;
if (ALGORITHM_GROM.equals(algorithm))
return GromFilter.INSTANCE;
if (ALGORITHM_SEGMENT.equals(algorithm)) {
assert false;
}
if (algorithm.startsWith(ALGORITHM_SEGMENT + ':')) {
String[] pieces = algorithm.substring(ALGORITHM_SEGMENT.length()+1).split(":");
return createFromSegments(pieces);
}
return null;
}
private static FilterSegment parse(String segment) {
int offset = 0;
int length = -1;
int colIdx = segment.indexOf('+');
if (colIdx < 0)
return null;
offset = Integer.parseInt(segment.substring(0, colIdx), 16);
length = Integer.parseInt(segment.substring(colIdx+1), 16);
return new FilterSegment(offset, length);
}
public static IMD5SumFilter createFromSegments(String... segments) {
if (segments.length == 0)
return FullContentFilter.INSTANCE;
if (segments.length == 1) {
// simple version
FilterSegment segment = parse(segments[0]);
if (segment == null)
return null;
return new FileSegmentFilter(segment);
} else {
// multiple entries
List<FilterSegment> segs = new ArrayList<FilterSegment>();
for (String segment : segments) {
FilterSegment seg = parse(segment);
if (seg == null)
return null;
segs.add(seg);
}
return new MultiFileSegmentFilter(segs);
}
}
}