kopia lustrzana https://github.com/onthegomap/planetiler
222 wiersze
6.2 KiB
Java
222 wiersze
6.2 KiB
Java
/*
|
|
* 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 com.onthegomap.planetiler.util;
|
|
|
|
import java.io.IOException;
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.channels.ClosedChannelException;
|
|
import java.nio.channels.SeekableByteChannel;
|
|
import java.util.Arrays;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
/**
|
|
* A {@link SeekableByteChannel} implementation that wraps a byte[].
|
|
*
|
|
* <p>
|
|
* When this channel is used for writing an internal buffer grows to accommodate incoming data. The natural size limit
|
|
* is the value of {@link Integer#MAX_VALUE} and it is not possible to {@link #position(long) set the position} or
|
|
* {@link #truncate truncate} to a value bigger than that. Internal buffer can be accessed via
|
|
* {@link SeekableInMemoryByteChannel#array()}. Adapted from <a href=
|
|
* "https://commons.apache.org/proper/commons-compress/jacoco/org.apache.commons.compress.utils/SeekableInMemoryByteChannel.java.html">Apache
|
|
* Commons Compress</a>.
|
|
* </p>
|
|
*
|
|
* @since 1.13
|
|
* @NotThreadSafe
|
|
*/
|
|
public class SeekableInMemoryByteChannel implements SeekableByteChannel {
|
|
|
|
private static final int NAIVE_RESIZE_LIMIT = Integer.MAX_VALUE >> 1;
|
|
|
|
private byte[] data;
|
|
private final AtomicBoolean closed = new AtomicBoolean();
|
|
private int position, size;
|
|
|
|
/**
|
|
* Constructor taking a byte array.
|
|
*
|
|
* <p>
|
|
* This constructor is intended to be used with pre-allocated buffer or when reading from a given byte array.
|
|
* </p>
|
|
*
|
|
* @param data input data or pre-allocated array.
|
|
*/
|
|
public SeekableInMemoryByteChannel(final byte[] data) {
|
|
this.data = data;
|
|
size = data.length;
|
|
}
|
|
|
|
/**
|
|
* Constructor taking a size of storage to be allocated.
|
|
*
|
|
* <p>
|
|
* Creates a channel and allocates internal storage of a given size.
|
|
* </p>
|
|
*
|
|
* @param size size of internal buffer to allocate, in bytes.
|
|
*/
|
|
public SeekableInMemoryByteChannel(final int size) {
|
|
this(new byte[size]);
|
|
}
|
|
|
|
/**
|
|
* Returns this channel's position.
|
|
*
|
|
* <p>
|
|
* This method violates the contract of {@link SeekableByteChannel#position()} as it will not throw any exception when
|
|
* invoked on a closed channel. Instead it will return the position the channel had when close has been called.
|
|
* </p>
|
|
*/
|
|
@Override
|
|
public long position() {
|
|
return position;
|
|
}
|
|
|
|
@Override
|
|
public SeekableByteChannel position(final long newPosition) throws IOException {
|
|
ensureOpen();
|
|
if (newPosition < 0L || newPosition > Integer.MAX_VALUE) {
|
|
throw new IOException("Position has to be in range 0.. " + Integer.MAX_VALUE);
|
|
}
|
|
position = (int) newPosition;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Returns the current size of entity to which this channel is connected.
|
|
*
|
|
* <p>
|
|
* This method violates the contract of {@link SeekableByteChannel#size} as it will not throw any exception when
|
|
* invoked on a closed channel. Instead it will return the size the channel had when close has been called.
|
|
* </p>
|
|
*/
|
|
@Override
|
|
public long size() {
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
* Truncates the entity, to which this channel is connected, to the given size.
|
|
*
|
|
* <p>
|
|
* This method violates the contract of {@link SeekableByteChannel#truncate} as it will not throw any exception when
|
|
* invoked on a closed channel.
|
|
* </p>
|
|
*
|
|
* @throws IllegalArgumentException if size is negative or bigger than the maximum of a Java integer
|
|
*/
|
|
@Override
|
|
public SeekableByteChannel truncate(final long newSize) {
|
|
if (newSize < 0L || newSize > Integer.MAX_VALUE) {
|
|
throw new IllegalArgumentException("Size has to be in range 0.. " + Integer.MAX_VALUE);
|
|
}
|
|
if (size > newSize) {
|
|
size = (int) newSize;
|
|
}
|
|
if (position > newSize) {
|
|
position = (int) newSize;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public int read(final ByteBuffer buf) throws IOException {
|
|
ensureOpen();
|
|
int wanted = buf.remaining();
|
|
final int possible = size - position;
|
|
if (possible <= 0) {
|
|
return -1;
|
|
}
|
|
if (wanted > possible) {
|
|
wanted = possible;
|
|
}
|
|
buf.put(data, position, wanted);
|
|
position += wanted;
|
|
return wanted;
|
|
}
|
|
|
|
@Override
|
|
public void close() {
|
|
closed.set(true);
|
|
}
|
|
|
|
@Override
|
|
public boolean isOpen() {
|
|
return !closed.get();
|
|
}
|
|
|
|
@Override
|
|
public int write(final ByteBuffer b) throws IOException {
|
|
ensureOpen();
|
|
int wanted = b.remaining();
|
|
final int possibleWithoutResize = size - position;
|
|
if (wanted > possibleWithoutResize) {
|
|
final int newSize = position + wanted;
|
|
if (newSize < 0) { // overflow
|
|
resize(Integer.MAX_VALUE);
|
|
wanted = Integer.MAX_VALUE - position;
|
|
} else {
|
|
resize(newSize);
|
|
}
|
|
}
|
|
b.get(data, position, wanted);
|
|
position += wanted;
|
|
if (size < position) {
|
|
size = position;
|
|
}
|
|
return wanted;
|
|
}
|
|
|
|
/**
|
|
* Obtains the array backing this channel.
|
|
*
|
|
* <p>
|
|
* NOTE: The returned buffer is not aligned with containing data, use {@link #size()} to obtain the size of data
|
|
* stored in the buffer.
|
|
* </p>
|
|
*
|
|
* @return internal byte array.
|
|
*/
|
|
public byte[] array() {
|
|
return data;
|
|
}
|
|
|
|
private void resize(final int newLength) {
|
|
int len = data.length;
|
|
if (len <= 0) {
|
|
len = 1;
|
|
}
|
|
if (newLength < NAIVE_RESIZE_LIMIT) {
|
|
while (len < newLength) {
|
|
len <<= 1;
|
|
}
|
|
} else { // avoid overflow
|
|
len = newLength;
|
|
}
|
|
data = Arrays.copyOf(data, len);
|
|
}
|
|
|
|
private void ensureOpen() throws ClosedChannelException {
|
|
if (!isOpen()) {
|
|
throw new ClosedChannelException();
|
|
}
|
|
}
|
|
|
|
}
|