package io.eguan.nrs; /* * #%L * Project eguan * %% * Copyright (C) 2012 - 2017 Oodrive * %% * 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. * #L% */ import io.eguan.proto.nrs.NrsRemote.NrsFileUpdate.NrsKey; import io.eguan.utils.mapper.FileMapper; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; /** * A NrsFileBlock stores blocks of data corresponding to the contents of a <i>large</i> file. It is optimized for sparse * files. * <p> * Note: this class supposes that the position, mark and limit of the {@link ByteBuffer} can be freely changed as * needed. * * @author oodrive * @author llambert * */ final class NrsFileBlock extends NrsAbstractFile<ByteBuffer, NrsFileBlock> { /** Value returned by read() when a block have been trimmed */ public static final ByteBuffer BLOCK_TRIMMED = ByteBuffer.allocate(0); NrsFileBlock(final FileMapper fileMapper, final NrsFileHeader<NrsFileBlock> header, final NrsMsgPostOffice postOffice) { super(header.getBlockSize(), fileMapper, header, postOffice, BLOCK_TRIMMED); } @Override final ByteBuffer newElement() { return NrsByteBufferCache.allocate(getElementSize()); } @Override final void checkValueLength(final ByteBuffer value) throws NullPointerException, IllegalArgumentException { final int elementSize = getElementSize(); final int len = value.limit() - value.position(); if (len != elementSize) { throw new IllegalArgumentException("Invalid block len=" + len + " instead of " + elementSize); } } @Override final void appendDebugString(final StringBuilder stringBuilder, final ByteBuffer value) { // Read at the current position and restore the position final int position = value.position(); try { stringBuilder.append(value.get()).append(value.get()).append(value.get()).append(value.get()); } finally { value.position(position); } } @Override final ByteBuffer decodeValue(final NrsKey value) { return value.getKey().asReadOnlyByteBuffer(); } @Override final void readFully(final MappedByteBuffer src, final ByteBuffer result) { final int elementSize = getElementSize(); // Read at the current position and restore the position final int position = result.position(); try { result.limit(position + elementSize); src.limit(src.position() + elementSize); try { result.put(src); } finally { // Restore previous limit src.limit(src.capacity()); } } finally { result.position(position); } } @Override final void readFully(final FileChannel src, final ByteBuffer result) throws IOException { final int elementSize = getElementSize(); // Read at the current position and restore the position final int position = result.position(); try { result.limit(position + elementSize); int readLen = 0; while (readLen < elementSize) { final int read = src.read(result); if (read == -1) { throw new IOException("Unexpected end of file '" + getFile() + "' readOffset=" + src.position()); } readLen += read; } } finally { result.position(position); } } @Override final void writeFully(final FileChannel dst, final ByteBuffer value) throws IOException { final int elementSize = getElementSize(); // Write from the current position and restore the position final int position = value.position(); try { value.limit(position + elementSize); int writeLen = 0; while (writeLen < elementSize) { writeLen += dst.write(value); } } finally { value.position(position); } } }