/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2015, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotoolkit.internal.tree; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.Channel; import java.nio.channels.ClosedChannelException; import java.nio.channels.SeekableByteChannel; import java.util.Arrays; import org.apache.sis.util.ArgumentChecks; /** * A readable and writable channel backed by an array. * * @author Remi Marechal (Geomatys). */ public strictfp class SeekableByteArrayChannel implements SeekableByteChannel { /** * Bytes array where to write the data. * The length of this array is the capacity. */ private byte[] data; /** * Number of valid bytes in the {@link #data} array. */ private int limit; /** * Current position in the {@link #data} array. */ private int position; /** * Sets to {@code true} after {@link #close()} has been invoked. */ private boolean isClosed; /** * Creates a channel which will store all written data in a byte array which have length initialized at 1 mb.<br><br> * * <strong>Usually use this constructor in writing mode.</strong> */ public SeekableByteArrayChannel() { this(new byte[1000000], 0);//-- set limit at zero to simulate empty channel. } /** * Creates a channel which will store all written data in the given array.<br> * Moreover the length of this internal stored data array may be increase * in case where you try to write more data than this array length, and also initialize channel limit at data array size.<br><br> * * <strong>Usually use this constructor in reading mode.</strong> * * @param data Bytes array where to write the data. */ public SeekableByteArrayChannel(final byte[] data) { this(data, data.length); } /** * Creates a channel which will store all written data in the given array.<br> * Moreover the length of this internal stored data array may be increase * in case where you try to write more data than this array length. * * @param data empty or already filled Tree informations. * @param limit size of this channel. Usually, 0 for writting treeAccess mode and data.size or lesser for reading treeAccess mode. */ public SeekableByteArrayChannel(final byte[] data, final int limit) { ArgumentChecks.ensureNonNull("SeekableByteArrayChannel : data", data); ArgumentChecks.ensureBetween("SeekableByteArrayChannel : limit", 0, data.length, limit); this.data = data; this.limit = limit; } /** * Reads a sequence of bytes from this channel into the given buffer. * * @param dst {@link ByteBuffer} which will be filled by reading action. * * @return number of reading byte. * @throws java.io.IOException if this {@link Channel} is closed. */ @Override public int read(final ByteBuffer dst) throws IOException { ensureOpen(); if (position >= limit) { return -1; } final int length = StrictMath.min(dst.remaining(), limit - position); dst.put(data, position, length); position += length; return length; } /** * Writes a sequence of bytes to this channel from the given buffer. * * @param src {@link ByteBuffer} which will be written. * @return number of written byte. * @throws java.io.IOException if this {@link Channel} is closed. */ @Override public int write(final ByteBuffer src) throws IOException { ensureOpen(); final int length = src.remaining(); ensureDataContain(length); src.get(data, position, length); position += length; limit = StrictMath.max(limit, position); return length; } /** * Returns this channel position. * * @throws java.io.IOException if this {@link Channel} is closed. */ @Override public long position() throws IOException { ensureOpen(); return position; } /** * Sets this channel position. * * @param newPosition the new position * @throws java.io.IOException if this {@link Channel} is closed. */ @Override public SeekableByteChannel position(final long newPosition) throws IOException { ensureOpen(); ArgumentChecks.ensureBetween("position", 0, size(), newPosition); position = (int) newPosition; return this; } /** * Returns the current size * * @throws java.io.IOException if this {@link Channel} is closed. */ @Override public long size() throws IOException { ensureOpen(); return limit; } /** * Truncates the data to the given size. * * @throws java.io.IOException if this {@link Channel} is closed. */ @Override public SeekableByteChannel truncate(final long size) throws IOException { ensureOpen(); ArgumentChecks.ensureBetween("position", 0, limit, size); limit = (int) size; return this; } /** * Tells whether or not this channel is open. */ @Override public boolean isOpen() { return !isClosed; } /** * Closes this channel. * * @throws java.io.IOException if problem to close this {@link Channel}. */ @Override public void close() throws IOException { isClosed = true; } /** * Verifies that the channel is open. */ private void ensureOpen() throws IOException { if (isClosed) throw new ClosedChannelException(); } /** * Verify that internal byte data array own enough space to store length byte. * If it has already enough space, do nothing, else multiply current data array size by 2. * * @param length byte number which will be written. */ private void ensureDataContain(final int length) { ArgumentChecks.ensurePositive("ensure data contain : length", length); if (length > data.length - limit) data = Arrays.copyOf(data, StrictMath.max(1000000, data.length << 1)); } /** * Returns internal stored datas array truncated at {@link #limit} size. * * @return datas array truncated at {@link #limit} size. */ public byte[] getData() { return Arrays.copyOf(data, limit); } }