638 lines
20 KiB
Java
638 lines
20 KiB
Java
/**
|
|
* Calculate, add or merge rd5 delta files
|
|
*
|
|
* @author ab
|
|
*/
|
|
package btools.mapaccess;
|
|
|
|
import java.io.BufferedInputStream;
|
|
import java.io.BufferedOutputStream;
|
|
import java.io.DataInputStream;
|
|
import java.io.DataOutputStream;
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.util.Arrays;
|
|
|
|
import btools.codec.DataBuffers;
|
|
import btools.codec.MicroCache;
|
|
import btools.codec.MicroCache2;
|
|
import btools.codec.StatCoderContext;
|
|
import btools.util.Crc32;
|
|
import btools.util.ProgressListener;
|
|
|
|
final public class Rd5DiffTool implements ProgressListener {
|
|
public static void main(String[] args) throws Exception {
|
|
if (args.length == 2) {
|
|
reEncode(new File(args[0]), new File(args[1]));
|
|
return;
|
|
}
|
|
|
|
if (args[1].endsWith(".df5")) {
|
|
if (args[0].endsWith(".df5")) {
|
|
addDeltas(new File(args[0]), new File(args[1]), new File(args[2]));
|
|
} else {
|
|
recoverFromDelta(new File(args[0]), new File(args[1]), new File(args[2]), new Rd5DiffTool() /*, new File( args[3] ) */);
|
|
}
|
|
} else {
|
|
diff2files(new File(args[0]), new File(args[1]), new File(args[2]));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void updateProgress(String task, int progress) {
|
|
System.out.println(task + ": " + progress + "%");
|
|
}
|
|
|
|
@Override
|
|
public boolean isCanceled() {
|
|
return false;
|
|
}
|
|
|
|
private static long[] readFileIndex(DataInputStream dis, DataOutputStream dos) throws IOException {
|
|
long[] fileIndex = new long[25];
|
|
for (int i = 0; i < 25; i++) {
|
|
long lv = dis.readLong();
|
|
fileIndex[i] = lv & 0xffffffffffffL;
|
|
if (dos != null) {
|
|
dos.writeLong(lv);
|
|
}
|
|
}
|
|
return fileIndex;
|
|
}
|
|
|
|
private static long getTileStart(long[] index, int tileIndex) {
|
|
return tileIndex > 0 ? index[tileIndex - 1] : 200L;
|
|
}
|
|
|
|
private static long getTileEnd(long[] index, int tileIndex) {
|
|
return index[tileIndex];
|
|
}
|
|
|
|
private static int[] readPosIndex(DataInputStream dis, DataOutputStream dos) throws IOException {
|
|
int[] posIndex = new int[1024];
|
|
for (int i = 0; i < 1024; i++) {
|
|
int iv = dis.readInt();
|
|
posIndex[i] = iv;
|
|
if (dos != null) {
|
|
dos.writeInt(iv);
|
|
}
|
|
}
|
|
return posIndex;
|
|
}
|
|
|
|
private static int getPosIdx(int[] posIdx, int idx) {
|
|
return idx == -1 ? 4096 : posIdx[idx];
|
|
}
|
|
|
|
private static byte[] createMicroCache(int[] posIdx, int tileIdx, DataInputStream dis, boolean deltaMode) throws IOException {
|
|
if (posIdx == null) {
|
|
return null;
|
|
}
|
|
int size = getPosIdx(posIdx, tileIdx) - getPosIdx(posIdx, tileIdx - 1);
|
|
if (size == 0) {
|
|
return null;
|
|
}
|
|
if (deltaMode) {
|
|
size = dis.readInt();
|
|
}
|
|
byte[] ab = new byte[size];
|
|
dis.readFully(ab);
|
|
return ab;
|
|
}
|
|
|
|
private static MicroCache createMicroCache(byte[] ab, DataBuffers dataBuffers) {
|
|
if (ab == null || ab.length == 0) {
|
|
return MicroCache.emptyCache();
|
|
}
|
|
StatCoderContext bc = new StatCoderContext(ab);
|
|
return new MicroCache2(bc, dataBuffers, 0, 0, 32, null, null);
|
|
}
|
|
|
|
/**
|
|
* Compute the delta between 2 RD5 files and
|
|
* show statistics on the expected size of the delta file
|
|
*/
|
|
public static void diff2files(File f1, File f2, File outFile) throws Exception {
|
|
byte[] abBuf1 = new byte[10 * 1024 * 1024];
|
|
byte[] abBuf2 = new byte[10 * 1024 * 1024];
|
|
|
|
int nodesDiff = 0;
|
|
int diffedTiles = 0;
|
|
|
|
long bytesDiff = 0L;
|
|
|
|
DataInputStream dis1 = new DataInputStream(new BufferedInputStream(new FileInputStream(f1)));
|
|
DataInputStream dis2 = new DataInputStream(new BufferedInputStream(new FileInputStream(f2)));
|
|
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(outFile)));
|
|
MCOutputStream mcOut = new MCOutputStream(dos, abBuf1);
|
|
|
|
// copy header to outfile
|
|
long[] fileIndex1 = readFileIndex(dis1, null);
|
|
long[] fileIndex2 = readFileIndex(dis2, dos);
|
|
|
|
long t0 = System.currentTimeMillis();
|
|
|
|
try {
|
|
DataBuffers dataBuffers = new DataBuffers();
|
|
for (int subFileIdx = 0; subFileIdx < 25; subFileIdx++) {
|
|
boolean hasData1 = getTileStart(fileIndex1, subFileIdx) < getTileEnd(fileIndex1, subFileIdx);
|
|
boolean hasData2 = getTileStart(fileIndex2, subFileIdx) < getTileEnd(fileIndex2, subFileIdx);
|
|
|
|
int[] posIdx1 = hasData1 ? readPosIndex(dis1, null) : null;
|
|
int[] posIdx2 = hasData2 ? readPosIndex(dis2, dos) : null;
|
|
|
|
for (int tileIdx = 0; tileIdx < 1024; tileIdx++) {
|
|
byte[] ab1 = createMicroCache(posIdx1, tileIdx, dis1, false);
|
|
byte[] ab2 = createMicroCache(posIdx2, tileIdx, dis2, false);
|
|
|
|
MicroCache mc;
|
|
if (Arrays.equals(ab1, ab2)) {
|
|
mc = MicroCache.emptyCache(); // empty diff
|
|
} else // calc diff of the 2 tiles
|
|
{
|
|
MicroCache mc1 = createMicroCache(ab1, dataBuffers);
|
|
MicroCache mc2 = createMicroCache(ab2, dataBuffers);
|
|
mc = new MicroCache2(mc1.getSize() + mc2.getSize(), abBuf2, 0, 0, 32);
|
|
mc.calcDelta(mc1, mc2);
|
|
}
|
|
|
|
int len = mcOut.writeMC(mc);
|
|
if (len > 0) {
|
|
bytesDiff += len;
|
|
nodesDiff += mc.getSize();
|
|
diffedTiles++;
|
|
|
|
/* // do some consistemcy checks on the encoding
|
|
|
|
byte[] bytes = new byte[len];
|
|
System.arraycopy( abBuf1, 0, bytes, 0, len );
|
|
|
|
// cross-check the encoding: decode again
|
|
MicroCache mcCheck = new MicroCache2( new StatCoderContext( bytes ), new DataBuffers( null ), 0, 0, 32, null, null );
|
|
|
|
// due to link-order ambiguity, for decoded we can only compare node-count and datasize
|
|
if ( mc.size() != mcCheck.size() )
|
|
{
|
|
throw new IllegalArgumentException( "re-decoded data-size mismatch!" );
|
|
}
|
|
if ( mc.getSize() != mcCheck.getSize() )
|
|
{
|
|
throw new IllegalArgumentException( "re-decoded node-count mismatch!" );
|
|
}
|
|
|
|
// .... so re-encode again
|
|
int len2 = mcCheck.encodeMicroCache( abBuf1 );
|
|
byte[] bytes2 = new byte[len2];
|
|
System.arraycopy( abBuf1, 0, bytes2, 0, len2 );
|
|
|
|
// and here we can compare byte-by-byte
|
|
if ( len != len2 )
|
|
{
|
|
throw new IllegalArgumentException( "decoded size mismatch!" );
|
|
}
|
|
for( int i=0; i<len; i++ )
|
|
{
|
|
if ( bytes[i] != bytes2[i] )
|
|
{
|
|
throw new IllegalArgumentException( "decoded data mismatch at i=" + i );
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
mcOut.finish();
|
|
}
|
|
|
|
// write any remaining data to the output file
|
|
for (; ; ) {
|
|
int len = dis2.read(abBuf1);
|
|
if (len < 0) {
|
|
break;
|
|
}
|
|
dos.write(abBuf1, 0, len);
|
|
}
|
|
long t1 = System.currentTimeMillis();
|
|
System.out.println("nodesDiff=" + nodesDiff + " bytesDiff=" + bytesDiff + " diffedTiles=" + diffedTiles + " took " + (t1 - t0) + "ms");
|
|
} finally {
|
|
if (dis1 != null) {
|
|
try {
|
|
dis1.close();
|
|
} catch (Exception ee) {
|
|
}
|
|
}
|
|
if (dis2 != null) {
|
|
try {
|
|
dis2.close();
|
|
} catch (Exception ee) {
|
|
}
|
|
}
|
|
if (dos != null) {
|
|
try {
|
|
dos.close();
|
|
} catch (Exception ee) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public static void recoverFromDelta(File f1, File f2, File outFile, ProgressListener progress /* , File cmpFile */) throws IOException {
|
|
if (f2.length() == 0L) {
|
|
copyFile(f1, outFile, progress);
|
|
return;
|
|
}
|
|
|
|
byte[] abBuf1 = new byte[10 * 1024 * 1024];
|
|
byte[] abBuf2 = new byte[10 * 1024 * 1024];
|
|
|
|
boolean canceled = false;
|
|
|
|
long t0 = System.currentTimeMillis();
|
|
|
|
DataInputStream dis1 = new DataInputStream(new BufferedInputStream(new FileInputStream(f1)));
|
|
DataInputStream dis2 = new DataInputStream(new BufferedInputStream(new FileInputStream(f2)));
|
|
// DataInputStream disCmp = new DataInputStream( new BufferedInputStream( new FileInputStream( cmpFile ) ) );
|
|
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(outFile)));
|
|
|
|
// copy header to outfile
|
|
long[] fileIndex1 = readFileIndex(dis1, null);
|
|
long[] fileIndex2 = readFileIndex(dis2, dos);
|
|
// long[] fileIndexCmp = readFileIndex( disCmp, null );
|
|
|
|
int lastPct = -1;
|
|
|
|
try {
|
|
DataBuffers dataBuffers = new DataBuffers();
|
|
MCInputStream mcIn = new MCInputStream(dis2, dataBuffers);
|
|
|
|
for (int subFileIdx = 0; subFileIdx < 25; subFileIdx++) {
|
|
boolean hasData1 = getTileStart(fileIndex1, subFileIdx) < getTileEnd(fileIndex1, subFileIdx); // has the basefile data
|
|
boolean hasData2 = getTileStart(fileIndex2, subFileIdx) < getTileEnd(fileIndex2, subFileIdx); // has the *result* data
|
|
|
|
// boolean hasDataCmp = getTileStart( fileIndexCmp, subFileIdx ) < getTileEnd( fileIndexCmp, subFileIdx );
|
|
|
|
int[] posIdx1 = hasData1 ? readPosIndex(dis1, null) : null;
|
|
int[] posIdx2 = hasData2 ? readPosIndex(dis2, dos) : null;
|
|
|
|
// int[] posIdxCmp = hasDataCmp ? readPosIndex( disCmp, null ) : null;
|
|
|
|
for (int tileIdx = 0; tileIdx < 1024; tileIdx++) {
|
|
if (progress.isCanceled()) {
|
|
canceled = true;
|
|
return;
|
|
}
|
|
double bytesProcessed = getTileStart(fileIndex1, subFileIdx) + (posIdx1 == null ? 0 : getPosIdx(posIdx1, tileIdx - 1));
|
|
int pct = (int) (100. * bytesProcessed / getTileEnd(fileIndex1, 24) + 0.5);
|
|
if (pct != lastPct) {
|
|
progress.updateProgress("Applying delta", pct);
|
|
lastPct = pct;
|
|
}
|
|
|
|
byte[] ab1 = createMicroCache(posIdx1, tileIdx, dis1, false);
|
|
MicroCache mc2 = mcIn.readMC();
|
|
int targetSize = posIdx2 == null ? 0 : getPosIdx(posIdx2, tileIdx) - getPosIdx(posIdx2, tileIdx - 1);
|
|
|
|
/* int targetSizeCmp = getPosIdx( posIdxCmp, tileIdx ) - getPosIdx( posIdxCmp, tileIdx-1 );
|
|
if ( targetSizeCmp != targetSize ) throw new IllegalArgumentException( "target size mismatch: "+ targetSize + "," + targetSizeCmp );
|
|
byte[] abCmp = new byte[targetSizeCmp];
|
|
disCmp.readFully( abCmp );
|
|
*/
|
|
|
|
// no-delta shortcut: just copy base data
|
|
if (mc2.getSize() == 0) {
|
|
if (ab1 != null) {
|
|
dos.write(ab1);
|
|
}
|
|
int newTargetSize = ab1 == null ? 0 : ab1.length;
|
|
if (targetSize != newTargetSize) {
|
|
throw new RuntimeException("size mismatch at " + subFileIdx + "/" + tileIdx + " " + targetSize + "!=" + newTargetSize);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// this is the real delta case (using decode->delta->encode )
|
|
|
|
MicroCache mc1 = createMicroCache(ab1, dataBuffers);
|
|
|
|
MicroCache mc = new MicroCache2(mc1.getSize() + mc2.getSize(), abBuf2, 0, 0, 32);
|
|
mc.addDelta(mc1, mc2, false);
|
|
|
|
if (mc.size() == 0) {
|
|
if (targetSize != 0) {
|
|
throw new RuntimeException("size mismatch at " + subFileIdx + "/" + tileIdx + " " + targetSize + ">0");
|
|
}
|
|
continue;
|
|
}
|
|
|
|
int len = mc.encodeMicroCache(abBuf1);
|
|
|
|
/* System.out.println( "comparing for subFileIdx=" + subFileIdx + " tileIdx=" + tileIdx );
|
|
boolean isequal = true;
|
|
for( int i=0; i<len;i++ )
|
|
{
|
|
if ( isequal && abCmp[i] != abBuf1[i] )
|
|
{
|
|
System.out.println( "data mismatch at i=" + i + " " + abCmp[i] + "!=" + abBuf1[i] + " targetSize=" + targetSize );
|
|
isequal = false;
|
|
|
|
MicroCache.debug = true;
|
|
System.out.println( "**** decoding original cache ****" );
|
|
createMicroCache( abCmp, dataBuffers );
|
|
System.out.println( "**** decoding reconstructed cache ****" );
|
|
createMicroCache( abBuf1, dataBuffers );
|
|
System.exit(1);
|
|
}
|
|
}
|
|
*/
|
|
dos.write(abBuf1, 0, len);
|
|
dos.writeInt(Crc32.crc(abBuf1, 0, len) ^ 2);
|
|
if (targetSize != len + 4) {
|
|
throw new RuntimeException("size mismatch at " + subFileIdx + "/" + tileIdx + " " + targetSize + "<>" + (len + 4));
|
|
}
|
|
|
|
}
|
|
mcIn.finish();
|
|
}
|
|
// write any remaining data to the output file
|
|
for (; ; ) {
|
|
int len = dis2.read(abBuf1);
|
|
if (len < 0) {
|
|
break;
|
|
}
|
|
dos.write(abBuf1, 0, len);
|
|
}
|
|
long t1 = System.currentTimeMillis();
|
|
System.out.println("recovering from diffs took " + (t1 - t0) + "ms");
|
|
} finally {
|
|
if (dis1 != null) {
|
|
try {
|
|
dis1.close();
|
|
} catch (Exception ee) {
|
|
}
|
|
}
|
|
if (dis2 != null) {
|
|
try {
|
|
dis2.close();
|
|
} catch (Exception ee) {
|
|
}
|
|
}
|
|
if (dos != null) {
|
|
try {
|
|
dos.close();
|
|
} catch (Exception ee) {
|
|
}
|
|
if (canceled) {
|
|
outFile.delete();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void copyFile(File f1, File outFile, ProgressListener progress) throws IOException {
|
|
boolean canceled = false;
|
|
DataInputStream dis1 = new DataInputStream(new BufferedInputStream(new FileInputStream(f1)));
|
|
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(outFile)));
|
|
int lastPct = -1;
|
|
long sizeTotal = f1.length();
|
|
long sizeRead = 0L;
|
|
try {
|
|
byte buf[] = new byte[65536];
|
|
for (; ; ) {
|
|
if (progress.isCanceled()) {
|
|
canceled = true;
|
|
return;
|
|
}
|
|
int pct = (int) ((100. * sizeRead) / (sizeTotal + 1) + 0.5);
|
|
if (pct != lastPct) {
|
|
progress.updateProgress("Copying", pct);
|
|
lastPct = pct;
|
|
}
|
|
int len = dis1.read(buf);
|
|
if (len <= 0) {
|
|
break;
|
|
}
|
|
sizeRead += len;
|
|
dos.write(buf, 0, len);
|
|
}
|
|
} finally {
|
|
if (dis1 != null) {
|
|
try {
|
|
dis1.close();
|
|
} catch (Exception ee) {
|
|
}
|
|
}
|
|
if (dos != null) {
|
|
try {
|
|
dos.close();
|
|
} catch (Exception ee) {
|
|
}
|
|
if (canceled) {
|
|
outFile.delete();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void addDeltas(File f1, File f2, File outFile) throws Exception {
|
|
byte[] abBuf1 = new byte[10 * 1024 * 1024];
|
|
byte[] abBuf2 = new byte[10 * 1024 * 1024];
|
|
|
|
DataInputStream dis1 = new DataInputStream(new BufferedInputStream(new FileInputStream(f1)));
|
|
DataInputStream dis2 = new DataInputStream(new BufferedInputStream(new FileInputStream(f2)));
|
|
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(outFile)));
|
|
|
|
// copy subfile-header to outfile
|
|
long[] fileIndex1 = readFileIndex(dis1, null);
|
|
long[] fileIndex2 = readFileIndex(dis2, dos);
|
|
|
|
long t0 = System.currentTimeMillis();
|
|
|
|
try {
|
|
DataBuffers dataBuffers = new DataBuffers();
|
|
MCInputStream mcIn1 = new MCInputStream(dis1, dataBuffers);
|
|
MCInputStream mcIn2 = new MCInputStream(dis2, dataBuffers);
|
|
MCOutputStream mcOut = new MCOutputStream(dos, abBuf1);
|
|
|
|
for (int subFileIdx = 0; subFileIdx < 25; subFileIdx++) {
|
|
// copy tile-header to outfile
|
|
boolean hasData1 = getTileStart(fileIndex1, subFileIdx) < getTileEnd(fileIndex1, subFileIdx);
|
|
boolean hasData2 = getTileStart(fileIndex2, subFileIdx) < getTileEnd(fileIndex2, subFileIdx);
|
|
int[] posIdx1 = hasData1 ? readPosIndex(dis1, null) : null;
|
|
int[] posIdx2 = hasData2 ? readPosIndex(dis2, dos) : null;
|
|
|
|
for (int tileIdx = 0; tileIdx < 1024; tileIdx++) {
|
|
MicroCache mc1 = mcIn1.readMC();
|
|
MicroCache mc2 = mcIn2.readMC();
|
|
MicroCache mc;
|
|
if (mc1.getSize() == 0 && mc2.getSize() == 0) {
|
|
mc = mc1;
|
|
} else {
|
|
mc = new MicroCache2(mc1.getSize() + mc2.getSize(), abBuf2, 0, 0, 32);
|
|
mc.addDelta(mc1, mc2, true);
|
|
}
|
|
mcOut.writeMC(mc);
|
|
}
|
|
mcIn1.finish();
|
|
mcIn2.finish();
|
|
mcOut.finish();
|
|
}
|
|
// write any remaining data to the output file
|
|
for (; ; ) {
|
|
int len = dis2.read(abBuf1);
|
|
if (len < 0) {
|
|
break;
|
|
}
|
|
dos.write(abBuf1, 0, len);
|
|
}
|
|
long t1 = System.currentTimeMillis();
|
|
System.out.println("adding diffs took " + (t1 - t0) + "ms");
|
|
} finally {
|
|
if (dis1 != null) {
|
|
try {
|
|
dis1.close();
|
|
} catch (Exception ee) {
|
|
}
|
|
}
|
|
if (dis2 != null) {
|
|
try {
|
|
dis2.close();
|
|
} catch (Exception ee) {
|
|
}
|
|
}
|
|
if (dos != null) {
|
|
try {
|
|
dos.close();
|
|
} catch (Exception ee) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public static void reEncode(File f1, File outFile) throws Exception {
|
|
byte[] abBuf1 = new byte[10 * 1024 * 1024];
|
|
|
|
DataInputStream dis1 = new DataInputStream(new BufferedInputStream(new FileInputStream(f1)));
|
|
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(outFile)));
|
|
|
|
// copy header to outfile
|
|
long[] fileIndex1 = readFileIndex(dis1, dos);
|
|
|
|
long t0 = System.currentTimeMillis();
|
|
|
|
try {
|
|
DataBuffers dataBuffers = new DataBuffers();
|
|
for (int subFileIdx = 0; subFileIdx < 25; subFileIdx++) {
|
|
boolean hasData1 = getTileStart(fileIndex1, subFileIdx) < getTileEnd(fileIndex1, subFileIdx);
|
|
|
|
int[] posIdx1 = hasData1 ? readPosIndex(dis1, dos) : null;
|
|
|
|
for (int tileIdx = 0; tileIdx < 1024; tileIdx++) {
|
|
byte[] ab1 = createMicroCache(posIdx1, tileIdx, dis1, false);
|
|
|
|
if (ab1 == null) continue;
|
|
|
|
MicroCache mc1 = createMicroCache(ab1, dataBuffers);
|
|
|
|
int len = mc1.encodeMicroCache(abBuf1);
|
|
|
|
dos.write(abBuf1, 0, len);
|
|
dos.writeInt(Crc32.crc(abBuf1, 0, len) ^ 2);
|
|
}
|
|
}
|
|
// write any remaining data to the output file
|
|
for (; ; ) {
|
|
int len = dis1.read(abBuf1);
|
|
if (len < 0) {
|
|
break;
|
|
}
|
|
dos.write(abBuf1, 0, len);
|
|
}
|
|
long t1 = System.currentTimeMillis();
|
|
System.out.println("re-encoding took " + (t1 - t0) + "ms");
|
|
} finally {
|
|
if (dis1 != null) {
|
|
try {
|
|
dis1.close();
|
|
} catch (Exception ee) {
|
|
}
|
|
}
|
|
if (dos != null) {
|
|
try {
|
|
dos.close();
|
|
} catch (Exception ee) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static class MCOutputStream {
|
|
private DataOutputStream dos;
|
|
private byte[] buffer;
|
|
private short skips = 0;
|
|
|
|
public MCOutputStream(DataOutputStream dos, byte[] buffer) {
|
|
this.dos = dos;
|
|
this.buffer = buffer;
|
|
}
|
|
|
|
public int writeMC(MicroCache mc) throws Exception {
|
|
if (mc.getSize() == 0) {
|
|
skips++;
|
|
return 0;
|
|
}
|
|
dos.writeShort(skips);
|
|
skips = 0;
|
|
int len = mc.encodeMicroCache(buffer);
|
|
if (len == 0) {
|
|
throw new IllegalArgumentException("encoded buffer of non-empty micro-cache cannot be empty");
|
|
}
|
|
dos.writeInt(len);
|
|
dos.write(buffer, 0, len);
|
|
return len;
|
|
}
|
|
|
|
public void finish() throws Exception {
|
|
if (skips > 0) {
|
|
dos.writeShort(skips);
|
|
skips = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static class MCInputStream {
|
|
private short skips = -1;
|
|
private DataInputStream dis;
|
|
private DataBuffers dataBuffers;
|
|
private MicroCache empty = MicroCache.emptyCache();
|
|
|
|
public MCInputStream(DataInputStream dis, DataBuffers dataBuffers) {
|
|
this.dis = dis;
|
|
this.dataBuffers = dataBuffers;
|
|
}
|
|
|
|
public MicroCache readMC() throws IOException {
|
|
if (skips < 0) {
|
|
skips = dis.readShort();
|
|
}
|
|
MicroCache mc = empty;
|
|
if (skips == 0) {
|
|
int size = dis.readInt();
|
|
byte[] ab = new byte[size];
|
|
dis.readFully(ab);
|
|
StatCoderContext bc = new StatCoderContext(ab);
|
|
mc = new MicroCache2(bc, dataBuffers, 0, 0, 32, null, null);
|
|
}
|
|
skips--;
|
|
return mc;
|
|
}
|
|
|
|
public void finish() {
|
|
skips = -1;
|
|
}
|
|
}
|
|
|
|
}
|