package org.jcodec.common.io;
import static java.lang.Math.min;
import static org.jcodec.platform.Platform.stringFromBytes;
import org.jcodec.common.ArrayUtil;
import org.jcodec.common.AutoFileChannelWrapper;
import org.jcodec.platform.Platform;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* @author The JCodec project
*
*/
public class NIOUtils {
public static ByteBuffer search(ByteBuffer buffer, int n, byte[] param) {
ByteBuffer result = buffer.duplicate();
int step = 0, rem = buffer.position();
while (buffer.hasRemaining()) {
int b = buffer.get();
if (b == param[step]) {
++step;
if (step == param.length) {
if (n == 0) {
buffer.position(rem);
result.limit(buffer.position());
break;
}
n--;
step = 0;
}
} else {
if (step != 0) {
step = 0;
++rem;
buffer.position(rem);
} else
rem = buffer.position();
}
}
return result;
}
public static final ByteBuffer read(ByteBuffer buffer, int count) {
ByteBuffer slice = buffer.duplicate();
int limit = buffer.position() + count;
slice.limit(limit);
buffer.position(limit);
return slice;
}
public static ByteBuffer fetchFromFile(File file) throws IOException {
return NIOUtils.fetchFromFileL(file, (int) file.length());
}
public static ByteBuffer fetchFromChannel(ReadableByteChannel ch, int size) throws IOException {
ByteBuffer buf = ByteBuffer.allocate(size);
NIOUtils.readFromChannel(ch, buf);
buf.flip();
return buf;
}
public static ByteBuffer fetchFromChannel(SeekableByteChannel ch) throws IOException {
List<ByteBuffer> buffers = new ArrayList<ByteBuffer>();
ByteBuffer buf;
do {
buf = fetchFromChannel(ch, 1 << 20);
buffers.add(buf);
} while(buf.hasRemaining());
return combineBuffers(buffers);
}
/**
* Reads size amount of bytes from ch into a new ByteBuffer allocated from a
* buffer buf
*
* @param buf
* @param ch
* @param size
* @return
* @throws IOException
*/
public static ByteBuffer fetchFrom(ByteBuffer buf, ReadableByteChannel ch, int size) throws IOException {
ByteBuffer result = buf.duplicate();
result.limit(size);
NIOUtils.readFromChannel(ch, result);
result.flip();
return result;
}
public static ByteBuffer fetchFromFileL(File file, int length) throws IOException {
FileChannel is = null;
try {
is = new FileInputStream(file).getChannel();
return fetchFromChannel(is, length);
} finally {
closeQuietly(is);
}
}
public static void writeTo(ByteBuffer buffer, File file) throws IOException {
FileChannel out = null;
try {
out = new FileOutputStream(file).getChannel();
out.write(buffer);
} finally {
closeQuietly(out);
}
}
public static byte[] toArray(ByteBuffer buffer) {
byte[] result = new byte[buffer.remaining()];
buffer.duplicate().get(result);
return result;
}
public static byte[] toArrayL(ByteBuffer buffer, int count) {
byte[] result = new byte[Math.min(buffer.remaining(), count)];
buffer.duplicate().get(result);
return result;
}
public static int readL(ReadableByteChannel channel, ByteBuffer buffer, int length) throws IOException {
ByteBuffer fork = buffer.duplicate();
fork.limit(min(fork.position() + length, fork.limit()));
int read;
while ((read = channel.read(fork)) != -1 && fork.hasRemaining())
;
if (read == -1)
return -1;
buffer.position(fork.position());
return read;
}
public static int readFromChannel(ReadableByteChannel channel, ByteBuffer buffer) throws IOException {
int rem = buffer.position();
while (channel.read(buffer) != -1 && buffer.hasRemaining())
;
return buffer.position() - rem;
}
public static void write(ByteBuffer to, ByteBuffer from) {
if (from.hasArray()) {
to.put(from.array(), from.arrayOffset() + from.position(), Math.min(to.remaining(), from.remaining()));
} else {
to.put(toArrayL(from, to.remaining()));
}
}
public static void writeL(ByteBuffer to, ByteBuffer from, int count) {
if (from.hasArray()) {
to.put(from.array(), from.arrayOffset() + from.position(), Math.min(from.remaining(), count));
} else {
to.put(toArrayL(from, count));
}
}
public static void fill(ByteBuffer buffer, byte val) {
while (buffer.hasRemaining())
buffer.put(val);
}
public static final MappedByteBuffer map(String fileName) throws IOException {
return mapFile(new File(fileName));
}
public static final MappedByteBuffer mapFile(File file) throws IOException {
FileInputStream is = new FileInputStream(file);
MappedByteBuffer map = is.getChannel().map(MapMode.READ_ONLY, 0, file.length());
is.close();
return map;
}
public static int skip(ByteBuffer buffer, int count) {
int toSkip = Math.min(buffer.remaining(), count);
buffer.position(buffer.position() + toSkip);
return toSkip;
}
public static ByteBuffer from(ByteBuffer buffer, int offset) {
ByteBuffer dup = buffer.duplicate();
dup.position(dup.position() + offset);
return dup;
}
public static ByteBuffer combineBuffers(Iterable<ByteBuffer> picture) {
int size = 0;
for (ByteBuffer byteBuffer : picture) {
size += byteBuffer.remaining();
}
ByteBuffer result = ByteBuffer.allocate(size);
for (ByteBuffer byteBuffer : picture) {
write(result, byteBuffer);
}
result.flip();
return result;
}
public static boolean combineBuffersInto(ByteBuffer dup, List<ByteBuffer> buffers) {
throw new RuntimeException("Stan");
}
public static ByteBuffer combine(ByteBuffer... arguments) {
return combineBuffers(Arrays.asList(arguments));
}
public static String readString(ByteBuffer buffer, int len) {
return stringFromBytes(toArray(read(buffer, len)));
}
public static String readPascalStringL(ByteBuffer buffer, int maxLen) {
ByteBuffer sub = read(buffer, maxLen + 1);
return stringFromBytes(toArray(NIOUtils.read(sub, Math.min(sub.get() & 0xff, maxLen))));
}
public static void writePascalStringL(ByteBuffer buffer, String string, int maxLen) {
buffer.put((byte) string.length());
buffer.put(asciiString(string));
skip(buffer, maxLen - string.length());
}
public static byte[] asciiString(String fourcc) {
return Platform.getBytes(fourcc);
}
public static void writePascalString(ByteBuffer buffer, String name) {
buffer.put((byte) name.length());
buffer.put(asciiString(name));
}
public static String readPascalString(ByteBuffer buffer) {
return readString(buffer, buffer.get() & 0xff);
}
public static String readNullTermString(ByteBuffer buffer) {
return readNullTermStringCharset(buffer, Charset.defaultCharset());
}
public static String readNullTermStringCharset(ByteBuffer buffer, Charset charset) {
ByteBuffer fork = buffer.duplicate();
while (buffer.hasRemaining() && buffer.get() != 0)
;
if (buffer.hasRemaining())
fork.limit(buffer.position() - 1);
return Platform.stringFromCharset(toArray(fork), charset);
}
public static ByteBuffer readBuf(ByteBuffer buffer) {
ByteBuffer result = buffer.duplicate();
buffer.position(buffer.limit());
return result;
}
public static void copy(ReadableByteChannel _in, WritableByteChannel out, long amount) throws IOException {
ByteBuffer buf = ByteBuffer.allocate(0x10000);
int read;
do {
buf.position(0);
buf.limit((int) Math.min(amount, buf.capacity()));
read = _in.read(buf);
if (read != -1) {
buf.flip();
out.write(buf);
amount -= read;
}
} while (read != -1 && amount > 0);
}
public static void closeQuietly(Closeable channel) {
if (channel == null)
return;
try {
channel.close();
} catch (IOException e) {
}
}
public static byte readByte(ReadableByteChannel channel) throws IOException {
ByteBuffer buf = ByteBuffer.allocate(1);
channel.read(buf);
buf.flip();
return buf.get();
}
public static byte[] readNByte(ReadableByteChannel channel, int n) throws IOException {
byte[] result = new byte[n];
channel.read(ByteBuffer.wrap(result));
return result;
}
public static int readInt(ReadableByteChannel channel) throws IOException {
ByteBuffer buf = ByteBuffer.allocate(4);
channel.read(buf);
buf.flip();
return buf.getInt();
}
public static int readIntOrder(ReadableByteChannel channel, ByteOrder order) throws IOException {
ByteBuffer buf = (ByteBuffer) ByteBuffer.allocate(4).order(order);
channel.read(buf);
buf.flip();
return buf.getInt();
}
public static void writeByte(WritableByteChannel channel, byte value) throws IOException {
channel.write((ByteBuffer) ByteBuffer.allocate(1).put(value).flip());
}
public static void writeIntOrder(WritableByteChannel channel, int value, ByteOrder order) throws IOException {
ByteBuffer order2 = (ByteBuffer) ByteBuffer.allocate(4).order(order);
channel.write((ByteBuffer) order2.putInt(value).flip());
}
public static void writeIntLE(WritableByteChannel channel, int value) throws IOException {
ByteBuffer allocate = ByteBuffer.allocate(4);
allocate.order(ByteOrder.LITTLE_ENDIAN);
channel.write((ByteBuffer) allocate.putInt(value).flip());
}
public static void writeInt(WritableByteChannel channel, int value) throws IOException {
channel.write((ByteBuffer) ByteBuffer.allocate(4).putInt(value).flip());
}
public static void writeLong(WritableByteChannel channel, long value) throws IOException {
channel.write((ByteBuffer) ByteBuffer.allocate(8).putLong(value).flip());
}
public static FileChannelWrapper readableChannel(File file) throws FileNotFoundException {
return new FileChannelWrapper(new FileInputStream(file).getChannel());
}
public static FileChannelWrapper writableChannel(File file) throws FileNotFoundException {
return new FileChannelWrapper(new FileOutputStream(file).getChannel());
}
public static FileChannelWrapper rwChannel(File file) throws FileNotFoundException {
return new FileChannelWrapper(new RandomAccessFile(file, "rw").getChannel());
}
public static FileChannelWrapper readableFileChannel(String file) throws FileNotFoundException {
return new FileChannelWrapper(new FileInputStream(file).getChannel());
}
public static FileChannelWrapper writableFileChannel(String file) throws FileNotFoundException {
return new FileChannelWrapper(new FileOutputStream(file).getChannel());
}
public static FileChannelWrapper rwFileChannel(String file) throws FileNotFoundException {
return new FileChannelWrapper(new RandomAccessFile(file, "rw").getChannel());
}
public static AutoFileChannelWrapper autoChannel(File file) throws IOException {
return new AutoFileChannelWrapper(file);
}
public static ByteBuffer duplicate(ByteBuffer bb) {
ByteBuffer out = ByteBuffer.allocate(bb.remaining());
out.put(bb.duplicate());
out.flip();
return out;
}
public static int find(List<ByteBuffer> catalog, ByteBuffer key) {
byte[] keyA = toArray(key);
for (int i = 0; i < catalog.size(); i++) {
if (Platform.arrayEqualsByte(toArray(catalog.get(i)), keyA))
return i;
}
return -1;
}
public static interface FileReaderListener {
void progress(int percentDone);
}
public static abstract class FileReader {
private int oldPd;
protected abstract void data(ByteBuffer data, long filePos);
protected abstract void done();
public void readChannel(SeekableByteChannel ch, int bufferSize, FileReaderListener listener) throws IOException {
ByteBuffer buf = ByteBuffer.allocate(bufferSize);
long size = ch.size();
for (long pos = ch.position(); ch.read(buf) != -1; pos = ch.position()) {
buf.flip();
data(buf, pos);
buf.flip();
if (listener != null) {
int newPd = (int) (100 * pos / size);
if (newPd != oldPd)
listener.progress(newPd);
oldPd = newPd;
}
}
done();
}
public void readFile(File source, int bufferSize, FileReaderListener listener) throws IOException {
SeekableByteChannel ch = null;
try {
ch = NIOUtils.readableChannel(source);
readChannel(ch, bufferSize, listener);
} finally {
NIOUtils.closeQuietly(ch);
}
}
}
public static byte getRel(ByteBuffer bb, int rel) {
return bb.get(bb.position() + rel);
}
public static ByteBuffer cloneBuffer(ByteBuffer pesBuffer) {
ByteBuffer res = ByteBuffer.allocate(pesBuffer.remaining());
res.put(pesBuffer.duplicate());
res.clear();
return res;
}
public static ByteBuffer clone(ByteBuffer byteBuffer) {
ByteBuffer result = ByteBuffer.allocate(byteBuffer.remaining());
result.put(byteBuffer.duplicate());
result.flip();
return result;
}
public static ByteBuffer asByteBuffer(byte ... arguments) {
return ByteBuffer.wrap(arguments);
}
public static ByteBuffer asByteBufferInt(int ... arguments) {
return asByteBuffer(ArrayUtil.toByteArray(arguments));
}
}