|
|
@@ -0,0 +1,120 @@
|
|
|
+/*
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
+ * @copyright Ranides Atterwim
|
|
|
+ * @license WTFPL
|
|
|
+ * @url http://ranides.net/projects/assira
|
|
|
+ */
|
|
|
+package net.ranides.assira.io;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+import java.nio.ByteBuffer;
|
|
|
+import java.nio.channels.ClosedChannelException;
|
|
|
+import java.nio.channels.SeekableByteChannel;
|
|
|
+import net.ranides.assira.collection.ArrayUtils;
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
+ */
|
|
|
+public class MemoryChannel implements SeekableByteChannel {
|
|
|
+
|
|
|
+ // @todo (assira # 3) MemoryChannel flexible implementation
|
|
|
+ // bytebuffer
|
|
|
+ // other buffers
|
|
|
+ // stringbuilder
|
|
|
+ // string
|
|
|
+ // byte[] array
|
|
|
+ // other primitive[] arrays
|
|
|
+ // non-seekable inputstream, outputstream, writer, reader
|
|
|
+
|
|
|
+ private byte[] data;
|
|
|
+ private boolean opened;
|
|
|
+ private int position;
|
|
|
+
|
|
|
+ @SuppressWarnings("PMD.ArrayIsStoredDirectly")
|
|
|
+ public MemoryChannel(byte[] data) {
|
|
|
+ this.data = data;
|
|
|
+ this.opened = true;
|
|
|
+ this.position = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ public MemoryChannel(int size) {
|
|
|
+ this(new byte[size]);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int read(ByteBuffer dst) throws IOException {
|
|
|
+ opencheck();
|
|
|
+ int n = Math.min(dst.remaining(), data.length - position);
|
|
|
+ dst.put(data, position, n);
|
|
|
+ return n;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int write(ByteBuffer src) throws IOException {
|
|
|
+ opencheck();
|
|
|
+ int n = src.remaining();
|
|
|
+ src.get(data, position, n);
|
|
|
+ return n;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public long position() throws IOException {
|
|
|
+ opencheck();
|
|
|
+ return position;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public SeekableByteChannel position(long newPosition) throws IOException {
|
|
|
+ opencheck();
|
|
|
+ if(newPosition<0) {
|
|
|
+ throw new IllegalArgumentException("position negative.");
|
|
|
+ }
|
|
|
+ if(newPosition > Integer.MAX_VALUE) {
|
|
|
+ throw new IOException("position limit.");
|
|
|
+ }
|
|
|
+ this.position = (int)newPosition;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public long size() throws IOException {
|
|
|
+ opencheck();
|
|
|
+ return data.length;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public SeekableByteChannel truncate(long size) throws IOException {
|
|
|
+ opencheck();
|
|
|
+ if(size <0) {
|
|
|
+ throw new IllegalArgumentException("size negative.");
|
|
|
+ }
|
|
|
+ if(size > Integer.MAX_VALUE) {
|
|
|
+ throw new IOException("size limit.");
|
|
|
+ }
|
|
|
+ if(size > data.length) {
|
|
|
+ data = ArrayUtils.clip(data, (int)size);
|
|
|
+ }
|
|
|
+ if(position > size) {
|
|
|
+ position = (int)size;
|
|
|
+ }
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean isOpen() {
|
|
|
+ return opened;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void close() throws IOException {
|
|
|
+ opened = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void opencheck() throws ClosedChannelException {
|
|
|
+ if(!opened) {
|
|
|
+ throw new ClosedChannelException();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|