001
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
024 import java.util.HashMap;
025 import java.util.Map;
026
027
030 public class Differ {
031
032 public void delta(
033 ReadableByteChannel modifiedReadableByteChannel,
034 ByteChannelReader checksumsByteChannelReader,
035 ByteChannelWriter deltaByteChannelWriter)
036 throws IOException {
037
038 _modifiedReadableByteChannel = modifiedReadableByteChannel;
039 _checksumsByteChannelReader = checksumsByteChannelReader;
040 _deltaByteChannelWriter = deltaByteChannelWriter;
041
042 _checksumsByteChannelReader.resizeBuffer(
043 DeltaUtil.BUFFER_FACTOR * 20);
044
045 _checksumsByteBuffer = _checksumsByteChannelReader.getBuffer();
046
047 readChecksumsHeader();
048 readChecksums();
049
050 _rollingChecksum = new RollingChecksum(
051 _modifiedReadableByteChannel, _blockLength);
052
053 _deltaByteChannelWriter.resizeBuffer(
054 _blockLength * DeltaUtil.BUFFER_FACTOR + 5);
055
056 _deltaByteBuffer = _deltaByteChannelWriter.getBuffer();
057
058 if ((_dataByteBuffer == null) ||
059 (_dataByteBuffer.capacity() <
060 (_blockLength * DeltaUtil.BUFFER_FACTOR))) {
061
062 _dataByteBuffer = ByteBuffer.allocate(
063 _blockLength * DeltaUtil.BUFFER_FACTOR);
064 }
065
066 writeDeltaHeader();
067 writeDeltaBlocks();
068 }
069
070 protected void readChecksums() throws IOException {
071 _blockDatas = new HashMap<Integer, BlockData>(_blocksCount);
072
073 for (int blockNumber = 0; blockNumber < _blocksCount; blockNumber++) {
074 _checksumsByteChannelReader.ensureData(20);
075
076 int weakChecksum = _checksumsByteBuffer.getInt();
077
078 byte[] strongChecksum = new byte[16];
079
080 _checksumsByteBuffer.get(strongChecksum);
081
082
083
084
085
086
087
088 _blockDatas.put(
089 weakChecksum, new BlockData(blockNumber, strongChecksum));
090 }
091 }
092
093 protected void readChecksumsHeader() throws IOException {
094 _checksumsByteChannelReader.ensureData(9);
095
096 if (DeltaUtil.PROTOCOL_VERSION != _checksumsByteBuffer.get()) {
097 throw new IOException("Unknown protocol version");
098 }
099
100 _blockLength = _checksumsByteBuffer.getInt();
101 _blocksCount = _checksumsByteBuffer.getInt();
102 }
103
104 protected void writeDataBlock() throws IOException {
105 if (_dataByteBuffer.position() == 0) {
106
107
108
109 return;
110 }
111
112 _deltaByteChannelWriter.ensureSpace(_dataByteBuffer.position() + 5);
113
114 _deltaByteBuffer.put(DeltaUtil.DATA_KEY);
115 _deltaByteBuffer.putInt(_dataByteBuffer.position());
116
117 _dataByteBuffer.flip();
118
119 _deltaByteBuffer.put(_dataByteBuffer);
120
121 _dataByteBuffer.clear();
122 }
123
124 protected void writeDeltaBlocks() throws IOException {
125 _firstBlockNumber = -1;
126 _lastBlockNumber = -1;
127
128 while (_rollingChecksum.hasNext()) {
129 BlockData blockData = _blockDatas.get(
130 _rollingChecksum.weakChecksum());
131
132 if ((blockData != null) &&
133 MessageDigest.isEqual(
134 blockData.getStrongChecksum(),
135 _rollingChecksum.strongChecksum())) {
136
137 int blockNumber = blockData.getBlockNumber();
138
139 if (_firstBlockNumber == -1) {
140 writeDataBlock();
141
142 _firstBlockNumber = blockNumber;
143 _lastBlockNumber = blockNumber;
144 }
145 else if (_lastBlockNumber + 1 == blockNumber) {
146
147
148
149 _lastBlockNumber = blockNumber;
150 }
151 else {
152 writeReferenceBlock();
153
154 _firstBlockNumber = blockNumber;
155 _lastBlockNumber = blockNumber;
156 }
157
158 _rollingChecksum.nextBlock();
159 }
160 else {
161 writeReferenceBlock();
162
163 if (!_dataByteBuffer.hasRemaining()) {
164 writeDataBlock();
165 }
166
167 _dataByteBuffer.put(_rollingChecksum.getFirstByte());
168
169 _rollingChecksum.nextByte();
170 }
171 }
172
173
174
175
176 writeReferenceBlock();
177 writeDataBlock();
178
179 _deltaByteChannelWriter.ensureSpace(1);
180
181 _deltaByteBuffer.put(DeltaUtil.EOF_KEY);
182 }
183
184 protected void writeDeltaHeader() throws IOException {
185 _deltaByteChannelWriter.ensureSpace(5);
186
187 _deltaByteBuffer.put(DeltaUtil.PROTOCOL_VERSION);
188 _deltaByteBuffer.putInt(_blockLength);
189 }
190
191 protected void writeReferenceBlock() throws IOException {
192 if (_firstBlockNumber == -1) {
193 return;
194 }
195
196 if (_lastBlockNumber == _firstBlockNumber) {
197 _deltaByteChannelWriter.ensureSpace(5);
198
199 _deltaByteBuffer.put(DeltaUtil.REFERENCE_KEY);
200 _deltaByteBuffer.putInt(_firstBlockNumber);
201 }
202 else {
203 _deltaByteChannelWriter.ensureSpace(9);
204
205 _deltaByteBuffer.put(DeltaUtil.REFERENCE_RANGE_KEY);
206 _deltaByteBuffer.putInt(_firstBlockNumber);
207 _deltaByteBuffer.putInt(_lastBlockNumber);
208 }
209
210 _firstBlockNumber = -1;
211 _lastBlockNumber = -1;
212 }
213
214 private Map<Integer, BlockData> _blockDatas;
215 private int _blockLength;
216 private int _blocksCount;
217 private ByteBuffer _checksumsByteBuffer;
218 private ByteChannelReader _checksumsByteChannelReader;
219 private ByteBuffer _dataByteBuffer;
220 private ByteBuffer _deltaByteBuffer;
221 private ByteChannelWriter _deltaByteChannelWriter;
222 private int _firstBlockNumber;
223 private int _lastBlockNumber;
224 private ReadableByteChannel _modifiedReadableByteChannel;
225 private RollingChecksum _rollingChecksum;
226
227 private class BlockData {
228
229 public BlockData(int blockNumber, byte[] strongChecksum) {
230 _blockNumber = blockNumber;
231 _strongChecksum = strongChecksum;
232 }
233
234 public int getBlockNumber() {
235 return _blockNumber;
236 }
237
238 public byte[] getStrongChecksum() {
239 return _strongChecksum;
240 }
241
242 private int _blockNumber;
243 private byte[] _strongChecksum;
244
245 }
246
247 }