/*
* Copyright 2012 b1.org
*
* 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.b1.pack.standard.writer;
import com.google.common.base.Objects;
import org.b1.pack.api.builder.Writable;
import org.b1.pack.api.common.PackEntry;
import org.b1.pack.api.writer.WriterProvider;
import org.b1.pack.standard.common.Constants;
import org.b1.pack.standard.common.Numbers;
import org.b1.pack.standard.common.PbRecordPointer;
import org.b1.pack.standard.common.RecordPointer;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.ExecutorService;
class PackOutputStream extends OutputStream {
private final WriterProvider provider;
private final LzmaMethod lzmaMethod;
private final BlockWriter blockWriter;
private final ExecutorService executorService;
private final int volumeNumberSize;
private final int blockOffsetSize;
private final int recordOffsetSize;
private LzmaWriter lzmaWriter;
public PackOutputStream(WriterProvider provider) {
this.provider = provider;
lzmaMethod = LzmaMethod.valueOf(provider.getCompressionMethod());
blockWriter = new BlockWriter(provider, lzmaMethod == null ? null : lzmaMethod.getName());
executorService = lzmaMethod == null ? null : provider.getExecutorService();
volumeNumberSize = Numbers.getSerializedSize(provider.getMaxVolumeSize() == Long.MAX_VALUE ? 1 : provider.getMaxVolumeCount());
blockOffsetSize = Numbers.getSerializedSize(provider.getMaxVolumeSize());
recordOffsetSize = Numbers.getSerializedSize(lzmaMethod == null ? Constants.MAX_CHUNK_SIZE : lzmaMethod.getSolidBlockSize());
}
public boolean isSeekable() {
return provider.isSeekable();
}
public PbRecordPointer createEmptyPointer() {
return new PbRecordPointer(volumeNumberSize, blockOffsetSize, recordOffsetSize);
}
public void setObjectCount(Long objectCount) {
blockWriter.setObjectCount(objectCount);
}
public RecordPointer startCatalog(boolean compressed) throws IOException {
disableCompression();
if (compressed) enableCompression();
return blockWriter.saveCatalogPointer();
}
public void switchCompression(PackEntry entry, Long size) throws IOException {
if (lzmaMethod != null) {
setCompressible(lzmaMethod.isCompressible(entry, size));
}
}
public void setCompressible(boolean compressible) throws IOException {
if (compressible && lzmaMethod != null) {
if (lzmaWriter != null && lzmaWriter.getCount() >= lzmaMethod.getSolidBlockSize()) {
disableCompression();
}
enableCompression();
} else {
disableCompression();
}
}
public RecordPointer getCurrentPointer() throws IOException {
return getChunkWriter().getCurrentPointer();
}
@Override
public void write(int b) throws IOException {
getChunkWriter().write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
getChunkWriter().write(b, off, len);
}
public void write(Writable value) throws IOException {
getChunkWriter().write(value);
}
public void save() throws IOException {
disableCompression();
blockWriter.save();
}
@Override
public void close() throws IOException {
disableCompression();
blockWriter.close();
shutdownExecutor();
}
public void cleanup() {
if (lzmaWriter != null) {
lzmaWriter.cleanup();
}
blockWriter.cleanup();
shutdownExecutor();
}
private void shutdownExecutor() {
if (executorService != null) {
executorService.shutdown();
}
}
private ChunkWriter getChunkWriter() {
return Objects.firstNonNull(lzmaWriter, blockWriter);
}
private void enableCompression() throws IOException {
if (lzmaWriter != null || lzmaMethod == null) return;
blockWriter.setCompressed(true);
lzmaWriter = new LzmaWriter(lzmaMethod, blockWriter, executorService);
}
private void disableCompression() throws IOException {
if (lzmaWriter == null) return;
lzmaWriter.close();
blockWriter.setCompressed(false);
lzmaWriter = null;
}
}