001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.io.delta;
016    
017    import java.io.IOException;
018    
019    import java.nio.ByteBuffer;
020    import java.nio.channels.ReadableByteChannel;
021    
022    import java.security.MessageDigest;
023    import java.security.NoSuchAlgorithmException;
024    
025    /**
026     * @author Connor McKay
027     */
028    public class RollingChecksum {
029    
030            public RollingChecksum(
031                            ReadableByteChannel readableByteChannel, int blockLength)
032                    throws IOException {
033    
034                    _blockLength = blockLength;
035                    _byteChannelReader = new ByteChannelReader(
036                            readableByteChannel, _blockLength * DeltaUtil.BUFFER_FACTOR);
037    
038                    generateWeakChecksum();
039            }
040    
041            public int currentBlockLength() {
042                    return Math.min(_byteChannelReader.remaining(), _blockLength);
043            }
044    
045            /**
046             * Returns the first byte of data in the current block.
047             */
048            public byte getFirstByte() {
049                    return _byteChannelReader.get(0);
050            }
051    
052            /**
053             * Returns the position of the start of the current block in the file.
054             */
055            public int getPosition() {
056                    return _filePosition;
057            }
058    
059            public boolean hasNext() throws IOException {
060                    _byteChannelReader.maybeRead(1);
061    
062                    if (_byteChannelReader.remaining() >= 1) {
063                            return true;
064                    }
065                    else {
066                            return false;
067                    }
068            }
069    
070            public void nextBlock() throws IOException {
071                    _filePosition += _byteChannelReader.skip(_blockLength);
072    
073                    generateWeakChecksum();
074            }
075    
076            public void nextByte() throws IOException {
077                    int blockLength = currentBlockLength();
078                    int x = _byteChannelReader.get();
079    
080                    _filePosition++;
081    
082                    _a -= x;
083                    _b -= blockLength * x;
084    
085                    _byteChannelReader.maybeRead(_blockLength);
086    
087                    if (_byteChannelReader.remaining() >= _blockLength) {
088                            x = _byteChannelReader.get(_blockLength - 1);
089    
090                            _a += x;
091                            _b += _a;
092                    }
093            }
094    
095            /**
096             * Returns the strong checksum of the current block.
097             */
098            public byte[] strongChecksum() {
099                    ByteBuffer buffer = _byteChannelReader.getBuffer();
100    
101                    int oldPosition = buffer.position();
102                    int oldLimit = buffer.limit();
103    
104                    buffer.limit(buffer.position() + currentBlockLength());
105    
106                    _messageDigest.update(buffer);
107    
108                    buffer.limit(oldLimit);
109                    buffer.position(oldPosition);
110    
111                    return _messageDigest.digest();
112            }
113    
114            /**
115             * Returns the weak checksum of the current block.
116             */
117            public int weakChecksum() {
118                    return (_a & 0xffff) | (_b << 16);
119            }
120    
121            protected void generateWeakChecksum() throws IOException {
122                    _byteChannelReader.maybeRead(_blockLength);
123    
124                    _a = 0;
125                    _b = 0;
126    
127                    for (int i = 0; i < currentBlockLength(); i++) {
128                            _a += _byteChannelReader.get(i);
129                            _b += _a;
130                    }
131            }
132    
133            private static MessageDigest _messageDigest;
134    
135            private int _a;
136            private int _b;
137            private int _blockLength;
138            private ByteChannelReader _byteChannelReader;
139            private int _filePosition;
140    
141            static {
142                    try {
143                            _messageDigest = MessageDigest.getInstance("MD5");
144                    }
145                    catch (NoSuchAlgorithmException nsae) {
146                            throw new ExceptionInInitializerError(nsae);
147                    }
148            }
149    
150    }