001    /**
002     * Copyright (c) 2000-2012 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            public void nextByte() throws IOException {
076                    int blockLength = currentBlockLength();
077                    int x = _byteChannelReader.get();
078    
079                    _filePosition++;
080    
081                    _a -= x;
082                    _b -= blockLength * x;
083    
084                    _byteChannelReader.maybeRead(_blockLength);
085    
086                    if (_byteChannelReader.remaining() >= _blockLength) {
087                            x = _byteChannelReader.get(_blockLength - 1);
088    
089                            _a += x;
090                            _b += _a;
091                    }
092            }
093    
094            /**
095             * Returns the strong checksum of the current block.
096             */
097            public byte[] strongChecksum() {
098                    ByteBuffer buffer = _byteChannelReader.getBuffer();
099    
100                    int oldPosition = buffer.position();
101                    int oldLimit = buffer.limit();
102    
103                    buffer.limit(buffer.position() + currentBlockLength());
104    
105                    _messageDigest.update(buffer);
106    
107                    buffer.limit(oldLimit);
108                    buffer.position(oldPosition);
109    
110                    return _messageDigest.digest();
111            }
112    
113            /**
114             * Returns the weak checksum of the current block.
115             */
116            public int weakChecksum() {
117                    return (_a & 0xffff) | (_b << 16);
118            }
119    
120            protected void generateWeakChecksum() throws IOException {
121                    _byteChannelReader.maybeRead(_blockLength);
122    
123                    _a = 0;
124                    _b = 0;
125    
126                    for (int i = 0; i < currentBlockLength(); i++) {
127                            _a += _byteChannelReader.get(i);
128                            _b += _a;
129                    }
130            }
131    
132            private static MessageDigest _messageDigest;
133    
134            private int _a;
135            private int _b;
136            private int _blockLength;
137            private ByteChannelReader _byteChannelReader;
138            private int _filePosition;
139    
140            static {
141                    try {
142                            _messageDigest = MessageDigest.getInstance("MD5");
143                    }
144                    catch (NoSuchAlgorithmException nsae) {
145                            throw new ExceptionInInitializerError(nsae);
146                    }
147            }
148    
149    }