/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.cassandra.hints; import java.io.IOException; import java.nio.ByteBuffer; import org.apache.cassandra.io.FSReadError; import org.apache.cassandra.io.compress.ICompressor; import org.apache.cassandra.io.util.ChannelProxy; import org.apache.cassandra.utils.memory.BufferPool; public final class CompressedChecksummedDataInput extends ChecksummedDataInput { private final ICompressor compressor; private volatile long filePosition = 0; private volatile ByteBuffer compressedBuffer = null; private final ByteBuffer metadataBuffer = ByteBuffer.allocate(CompressedHintsWriter.METADATA_SIZE); public CompressedChecksummedDataInput(Builder builder) { super(builder); assert regions == null; //mmapped regions are not supported compressor = builder.compressor; filePosition = builder.position; } /** * Since an entire block of compressed data is read off of disk, not just a hint at a time, * we don't report EOF until the decompressed data has also been read completely */ public boolean isEOF() { return filePosition == channel.size() && buffer.remaining() == 0; } protected void reBufferStandard() { metadataBuffer.clear(); channel.read(metadataBuffer, filePosition); filePosition += CompressedHintsWriter.METADATA_SIZE; metadataBuffer.rewind(); int uncompressedSize = metadataBuffer.getInt(); int compressedSize = metadataBuffer.getInt(); if (compressedBuffer == null || compressedSize > compressedBuffer.capacity()) { int bufferSize = compressedSize + (compressedSize / 20); // allocate +5% to cover variability in compressed size if (compressedBuffer != null) { BufferPool.put(compressedBuffer); } compressedBuffer = allocateBuffer(bufferSize, compressor.preferredBufferType()); } compressedBuffer.clear(); compressedBuffer.limit(compressedSize); channel.read(compressedBuffer, filePosition); compressedBuffer.rewind(); filePosition += compressedSize; bufferOffset += buffer.position(); if (buffer.capacity() < uncompressedSize) { int bufferSize = uncompressedSize + (uncompressedSize / 20); BufferPool.put(buffer); buffer = allocateBuffer(bufferSize, compressor.preferredBufferType()); } buffer.clear(); buffer.limit(uncompressedSize); try { compressor.uncompress(compressedBuffer, buffer); buffer.flip(); } catch (IOException e) { throw new FSReadError(e, getPath()); } } protected void releaseBuffer() { super.releaseBuffer(); if (compressedBuffer != null) { BufferPool.put(compressedBuffer); compressedBuffer = null; } } protected void reBufferMmap() { throw new UnsupportedOperationException(); } public static final class Builder extends ChecksummedDataInput.Builder { private long position; private ICompressor compressor; public Builder(ChannelProxy channel) { super(channel); bufferType = null; } public CompressedChecksummedDataInput build() { assert position >= 0; assert compressor != null; return new CompressedChecksummedDataInput(this); } public Builder withCompressor(ICompressor compressor) { this.compressor = compressor; bufferType = compressor.preferredBufferType(); return this; } public Builder withPosition(long position) { this.position = position; return this; } } // Closing the CompressedChecksummedDataInput will close the underlying channel. @SuppressWarnings("resource") public static final CompressedChecksummedDataInput upgradeInput(ChecksummedDataInput input, ICompressor compressor) { long position = input.getPosition(); input.close(); Builder builder = new Builder(new ChannelProxy(input.getPath())); builder.withPosition(position); builder.withCompressor(compressor); return builder.build(); } }