/* * Copyright 2004 - 2008 Christian Sprajc, Dennis Waldherr. All rights reserved. * * This file is part of PowerFolder. * * PowerFolder is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation. * * PowerFolder is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PowerFolder. If not, see <http://www.gnu.org/licenses/>. * * $Id$ */ package de.dal33t.powerfolder.util.delta; import java.security.MessageDigest; import java.util.LinkedList; import java.util.List; import java.util.zip.Checksum; import de.dal33t.powerfolder.util.Reject; import de.dal33t.powerfolder.util.Validate; /** * Creates arrays of PartInfos given the algorithms to use and a data set. * * @author Dennis "Dante" Waldherr * @version $Revision: 4280 $ */ public final class FilePartsRecordBuilder { private final Checksum chksum; private final MessageDigest partDigester, fileDigester; private final int partSize; private List<PartInfo> parts = new LinkedList<PartInfo>(); private long processed; private int partPos; public FilePartsRecordBuilder(Checksum chksumRoller, MessageDigest partDigester, MessageDigest fileDigester, int partSize) { super(); Reject.noNullElements(chksumRoller, partDigester, fileDigester); this.chksum = chksumRoller; this.partDigester = partDigester; this.fileDigester = fileDigester; this.partSize = partSize; } /** * Updates the current record with the given data. * @param data * @param off * @param len */ public void update(byte[] data, int off, int len) { Validate.notNull(data); if (off < 0 || len < 0 || off + len > data.length) { throw new IndexOutOfBoundsException("Invalid parameters!"); } processed += len; fileDigester.update(data, off, len); while (len > 0) { if (partPos + len >= partSize) { int rem = partSize - partPos; chksum.update(data, off, rem); partDigester.update(data, off, rem); parts.add(new PartInfo(parts.size(), chksum.getValue(), partDigester.digest())); // Only the checksum needs to be reset, since getValue() doesn't do that. chksum.reset(); off += rem; len -= rem; partPos = 0; } else { // System.err.println(len + " " + (data[off] & 0xff)); chksum.update(data, off, len); partDigester.update(data, off, len); partPos += len; len = 0; } } } /** * Updates the current record with the given data. * @param data */ public void update(int data) { update(new byte[] { (byte) (data & 0xFF) }); } /** * Updates the current record with the given data. * Same as calling update(data, 0, data.length). * @param data the data to update with. */ public void update(byte[] data) { update(data, 0, data.length); } /** * Performs final operations, such as padding and returns the resulting set. * The builder is reset after this call is made. * @return a record containing {@link PartInfo}s and additional information. */ public FilePartsRecord getRecord() { try { // Finalize result if (partPos > 0) { // System.err.println(partSize - partPos); for (int i = 0; i < partSize - partPos; i++) { chksum.update(0); partDigester.update((byte) 0); } parts.add(new PartInfo(parts.size(), chksum.getValue(), partDigester.digest())); } return new FilePartsRecord(processed, parts.toArray(new PartInfo[0]), partSize, fileDigester.digest()); } finally { reset(); } } /** * Resets the builder for further use. */ public void reset() { processed = 0; partPos = 0; chksum.reset(); fileDigester.reset(); partDigester.reset(); parts.clear(); } }