/*
* Copyright (C) 2014 Jörg Prante
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.xbib.io.compress.lzf;
import java.io.IOException;
import java.io.OutputStream;
/**
* Helper class used to store LZF encoded segments (compressed and
* non-compressed) that can be sequenced to produce LZF files/streams.
*/
public class LZFChunk {
/**
* Maximum length of literal run for LZF encoding.
*/
public static final int MAX_LITERAL = 1 << 5; // 32
// Chunk length is limited by 2-byte length indicator, to 64k
public static final int MAX_CHUNK_LEN = 0xFFFF;
/**
* Header can be either 7 bytes (compressed) or 5 bytes (uncompressed) long
*/
public static final int MAX_HEADER_LEN = 7;
public final static byte BYTE_Z = 'Z';
public final static byte BYTE_V = 'V';
public final static int BLOCK_TYPE_NON_COMPRESSED = 0;
public final static int BLOCK_TYPE_COMPRESSED = 1;
protected final byte[] _data;
protected LZFChunk _next;
private LZFChunk(byte[] data) {
_data = data;
}
/**
* Factory method for constructing compressed chunk
*/
public static LZFChunk createCompressed(int origLen, byte[] encData, int encPtr, int encLen) {
byte[] result = new byte[encLen + 7];
result[0] = BYTE_Z;
result[1] = BYTE_V;
result[2] = BLOCK_TYPE_COMPRESSED;
result[3] = (byte) (encLen >> 8);
result[4] = (byte) encLen;
result[5] = (byte) (origLen >> 8);
result[6] = (byte) origLen;
System.arraycopy(encData, encPtr, result, 7, encLen);
return new LZFChunk(result);
}
public static int appendCompressedHeader(int origLen, int encLen, byte[] headerBuffer, int offset)
throws IOException {
headerBuffer[offset++] = BYTE_Z;
headerBuffer[offset++] = BYTE_V;
headerBuffer[offset++] = BLOCK_TYPE_COMPRESSED;
headerBuffer[offset++] = (byte) (encLen >> 8);
headerBuffer[offset++] = (byte) encLen;
headerBuffer[offset++] = (byte) (origLen >> 8);
headerBuffer[offset++] = (byte) origLen;
return offset;
}
public static void writeCompressedHeader(int origLen, int encLen, OutputStream out, byte[] headerBuffer)
throws IOException {
headerBuffer[0] = BYTE_Z;
headerBuffer[1] = BYTE_V;
headerBuffer[2] = BLOCK_TYPE_COMPRESSED;
headerBuffer[3] = (byte) (encLen >> 8);
headerBuffer[4] = (byte) encLen;
headerBuffer[5] = (byte) (origLen >> 8);
headerBuffer[6] = (byte) origLen;
out.write(headerBuffer, 0, 7);
}
/**
* Factory method for constructing compressed chunk
*/
public static LZFChunk createNonCompressed(byte[] plainData, int ptr, int len) {
byte[] result = new byte[len + 5];
result[0] = BYTE_Z;
result[1] = BYTE_V;
result[2] = BLOCK_TYPE_NON_COMPRESSED;
result[3] = (byte) (len >> 8);
result[4] = (byte) len;
System.arraycopy(plainData, ptr, result, 5, len);
return new LZFChunk(result);
}
public static int appendNonCompressedHeader(int len, byte[] headerBuffer, int offset)
throws IOException {
headerBuffer[offset++] = BYTE_Z;
headerBuffer[offset++] = BYTE_V;
headerBuffer[offset++] = BLOCK_TYPE_NON_COMPRESSED;
headerBuffer[offset++] = (byte) (len >> 8);
headerBuffer[offset++] = (byte) len;
return offset;
}
public static void writeNonCompressedHeader(int len, OutputStream out, byte[] headerBuffer)
throws IOException {
headerBuffer[0] = BYTE_Z;
headerBuffer[1] = BYTE_V;
headerBuffer[2] = BLOCK_TYPE_NON_COMPRESSED;
headerBuffer[3] = (byte) (len >> 8);
headerBuffer[4] = (byte) len;
out.write(headerBuffer, 0, 5);
}
public void setNext(LZFChunk next) {
_next = next;
}
public LZFChunk next() {
return _next;
}
public int length() {
return _data.length;
}
public byte[] getData() {
return _data;
}
public int copyTo(byte[] dst, int ptr) {
int len = _data.length;
System.arraycopy(_data, 0, dst, ptr, len);
return ptr + len;
}
}