All Downloads are FREE. Search and download functionalities are using the official Maven repository.

software.coley.llzip.util.BufferData Maven / Gradle / Ivy

package software.coley.llzip.util;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * File that is backed by a byte buffer.
 *
 * @author xDark
 */
public final class BufferData implements ByteData {
	private final ByteBuffer buffer;
	private final AtomicBoolean cleaned;

	private BufferData(ByteBuffer buffer, AtomicBoolean cleaned) {
		this.buffer = buffer;
		this.cleaned = cleaned;
	}

	@Override
	public int getInt(long position) {
		ensureOpen();
		return buffer.getInt(validate(position));
	}

	@Override
	public long getLong(long position) {
		ensureOpen();
		return buffer.getLong(validate(position));
	}

	@Override
	public short getShort(long position) {
		ensureOpen();
		return buffer.getShort(validate(position));
	}

	@Override
	public byte get(long position) {
		ensureOpen();
		return buffer.get(validate(position));
	}

	@Override
	@SuppressWarnings("all")
	public void get(long position, byte[] b, int off, int len) {
		ensureOpen();
		// Left intentionally as unchained calls due to API differences across Java versions
		// and how the compiler changes output.
		ByteBuffer buffer = this.buffer.slice();
		buffer.order(buffer.order());
		((Buffer) buffer).position(validate(position));
		buffer.get(b, off, len);
	}

	@Override
	public void transferTo(OutputStream out, byte[] buf) throws IOException {
		ensureOpen();
		ByteBuffer buffer = this.buffer;
		int remaining = buffer.remaining();
		if (buffer.hasArray()) {
			out.write(buffer.array(), buffer.arrayOffset() + buffer.position(), remaining);
		} else {
			buffer.mark();
			int copyThreshold = buf.length;
			while (remaining != 0) {
				int length = Math.min(copyThreshold, remaining);
				buffer.get(buf, 0, length);
				out.write(buf, 0, length);
				remaining -= length;
			}
			buffer.reset();
		}
	}

	@Override
	public ByteData slice(long startIndex, long endIndex) {
		ensureOpen();
		return new BufferData(ByteDataUtil.sliceExact(buffer, validate(startIndex), validate(endIndex)), cleaned);
	}

	@Override
	public long length() {
		ensureOpen();
		return ByteDataUtil.length(buffer);
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		if (!(o instanceof BufferData)) return false;
		BufferData that = (BufferData) o;
		return buffer.equals(that.buffer);
	}

	@Override
	public int hashCode() {
		return buffer.hashCode();
	}

	@Override
	public void close() {
		if (!cleaned.get()) {
			synchronized (this) {
				if (cleaned.get())
					return;
				cleaned.set(true);
				ByteBuffer buffer = this.buffer;
				if (buffer.isDirect()) {
					CleanerUtil.invokeCleaner(buffer);
				}
			}
		}
	}

	@Override
	public boolean isClosed() {
		return cleaned.get();
	}

	private void ensureOpen() {
		if (cleaned.get())
			throw new IllegalStateException("Cannot access data after close");
	}

	private static int validate(long v) {
		if (v < 0L || v > Integer.MAX_VALUE) {
			throw new IllegalArgumentException(Long.toString(v));
		}
		return (int) v;
	}

	/**
	 * @param buffer
	 * 		Byte buffer to wrap.
	 *
	 * @return Buffer data.
	 */
	public static BufferData wrap(ByteBuffer buffer) {
		return new BufferData(buffer, new AtomicBoolean());
	}

	/**
	 * @param array
	 * 		Byte array to wrap.
	 *
	 * @return Buffer data.
	 */
	public static BufferData wrap(byte[] array) {
		return new BufferData(ByteBuffer.wrap(array).order(ByteOrder.LITTLE_ENDIAN), new AtomicBoolean());
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy