Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ByteBuffers #95

Merged
merged 2 commits into from
Nov 5, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
413 changes: 413 additions & 0 deletions ext/nio4r/bytebuffer.c

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions ext/nio4r/nio4r.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ struct NIO_Monitor
struct NIO_Selector *selector;
};

struct NIO_ByteBuffer
{
int size, offset, limit, position, mark;
char *buffer;
VALUE self;
};


#ifdef GetReadFile
# define FPTR_TO_FD(fptr) (fileno(GetReadFile(fptr)))
#else
Expand Down
2 changes: 2 additions & 0 deletions ext/nio4r/nio4r_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@

void Init_NIO_Selector();
void Init_NIO_Monitor();
void Init_NIO_ByteBuffer();

void Init_nio4r_ext()
{
Init_NIO_Selector();
Init_NIO_Monitor();
Init_NIO_ByteBuffer();
}
284 changes: 284 additions & 0 deletions ext/nio4r/org/nio4r/ByteBuffer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
package org.nio4r;

import org.jruby.*;
import org.jruby.anno.JRubyMethod;
import org.jruby.javasupport.JavaUtil;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
import java.util.ArrayList;

/*
created by Upekshej
*/
public class ByteBuffer extends RubyObject {

private java.nio.ByteBuffer byteBuffer;
private String currentWritePath = "";
private String currentReadPath = "";

private FileChannel currentWriteFileChannel;
private FileOutputStream fileOutputStream;

private FileInputStream currentReadChannel;
private FileChannel inChannel;

public ByteBuffer(final Ruby ruby, RubyClass rubyClass) {
super(ruby, rubyClass);
}

@JRubyMethod
public IRubyObject initialize(ThreadContext context, IRubyObject value, IRubyObject offset, IRubyObject length) {
Ruby ruby = context.getRuntime();
if (value == ruby.getNil())
throw new context.runtime.newTypeError("not a valid input");

if (value instanceof RubyString) {
if (offset != ruby.getNil() && length != ruby.getNil()) {
int arrayOffset = RubyNumeric.num2int(offset);
int arrayLimit = RubyNumeric.num2int(length);
byteBuffer = java.nio.ByteBuffer.wrap(value.asJavaString().getBytes(), arrayOffset, arrayLimit);
} else
byteBuffer = java.nio.ByteBuffer.wrap(value.asJavaString().getBytes());
} else if (value instanceof RubyInteger) {
int allocationSize = RubyNumeric.num2int(value);
byteBuffer = java.nio.ByteBuffer.allocate(allocationSize);
} else {
throw new context.runtime.newRuntimeError("Invalid Arguiments Exception");
}
return this;
}

/**
* Currently assuming only strings will come..
*
* @param context
* @return
*/
@JRubyMethod(name = "<<")
public IRubyObject put(ThreadContext context, IRubyObject str) {
String string = str.asJavaString();

if (byteBuffer == null)
byteBuffer = java.nio.ByteBuffer.wrap(string.getBytes());
byteBuffer.put(string.getBytes());
return this;
}

//https://www.ruby-forum.com/topic/3731325
@JRubyMethod(name = "get")
public IRubyObject get(ThreadContext context) {
ArrayList<Byte> temp = new ArrayList<Byte>();
while (byteBuffer.hasRemaining()) {
temp.add(byteBuffer.get());
}
// String returnString = new String(toPrimitives(temp));

return JavaUtil.convertJavaToRuby(context.getRuntime(), new String(toPrimitives(temp)));
}

@JRubyMethod(name = "read_next")
public IRubyObject readNext(ThreadContext context, IRubyObject count) {
int c = RubyNumeric.num2int(count);
if (c < 1)
throw new IllegalArgumentException();
if (c <= byteBuffer.remaining()) {
org.jruby.util.ByteList temp = new ByteList(c);
while (c > 0) {
temp.append(byteBuffer.get());
c = c - 1;
}
return context.runtime.newString(temp);
}
return RubyString.newEmptyString(context.runtime); //Empty String
}

private byte[] toPrimitives(ArrayList<Byte> oBytes) {
byte[] bytes = new byte[oBytes.size()];
for (int i = 0; i < oBytes.size(); i++) {
bytes[i] = (oBytes.get(i) == null) ? " ".getBytes()[0] : oBytes.get(i);
}
return bytes;
}

@JRubyMethod(name = "write_to")
public IRubyObject writeTo(ThreadContext context, IRubyObject f) {
try {
File file = (File) JavaUtil.unwrapJavaObject(f);
if (!isTheSameFile(file, false)) {
currentWritePath = file.getAbsolutePath();
if (currentWriteFileChannel != null) currentWriteFileChannel.close();
if (fileOutputStream != null) fileOutputStream.close();

fileOutputStream = new FileOutputStream(file, true);
currentWriteFileChannel = fileOutputStream.getChannel();
}
currentWriteFileChannel.write(byteBuffer);
} catch (Exception e) {
throw new IllegalArgumentException("File Write Operation Error: " + e.getLocalizedMessage());
}
return this;
}

@JRubyMethod(name = "read_from")
public IRubyObject readFrom(ThreadContext context, IRubyObject f) {
try {
File file = (File) JavaUtil.unwrapJavaObject(f);
if (!isTheSameFile(file, true)) {
inChannel.close();
currentReadChannel.close();
currentReadPath = file.getAbsolutePath();
currentReadChannel = new FileInputStream(file);
inChannel = currentReadChannel.getChannel();
}
inChannel.read(byteBuffer);
} catch (Exception e) {
throw new IllegalArgumentException("File Read Operation Error: " + e.getLocalizedMessage());
}
return this;
}

private boolean isTheSameFile(File f, boolean read) {
if (read)
return (currentReadPath == f.getAbsolutePath());
return currentWritePath == f.getAbsolutePath();
}

@JRubyMethod(name = "remaining")
public IRubyObject remainingPositions(ThreadContext context) {
int count = byteBuffer.remaining();
return context.getRuntime().newFixnum(count);
}

@JRubyMethod(name = "remaining?")
public IRubyObject hasRemaining(ThreadContext context) {
if (byteBuffer.hasRemaining())
return context.getRuntime().getTrue();

return context.getRuntime().getFalse();
}

@JRubyMethod(name = "offset?")
public IRubyObject getOffset(ThreadContext context) {
int offset = byteBuffer.arrayOffset();
return context.getRuntime().newFixnum(offset);
}

/**
* Check whether the two ByteBuffers are the same.
*
* @param context
* @param ob : The RubyObject which needs to be check
* @return
*/
@JRubyMethod(name = "equals?")
public IRubyObject equals(ThreadContext context, IRubyObject ob) {
Object o = JavaUtil.convertRubyToJava(ob);
if(!(o instanceof ByteBuffer)) return context.getRuntime().getFalse();

boolean equal = this.byteBuffer.equals(((ByteBuffer) ).getBuffer());
if (equal)
return context.getRuntime().getTrue();
return context.getRuntime().getFalse();
}

/**
* Flip capability provided by the java nio.ByteBuffer
* buf.put(magic); // Prepend header
* in.read(buf); // Read data into rest of buffer
* buf.flip(); // Flip buffer
* out.write(buf); // Write header + data to channel
*
* @param context
* @return
*/
@JRubyMethod
public IRubyObject flip(ThreadContext context) {
byteBuffer.flip();
return this;
}

/**
* Rewinds the buffer. Usage in java is like
* out.write(buf); // Write remaining data
* buf.rewind(); // Rewind buffer
* buf.get(array); // Copy data into array
*
* @param context
* @return
*/
@JRubyMethod
public IRubyObject rewind(ThreadContext context) {
byteBuffer.rewind();
return this;
}

@JRubyMethod
public IRubyObject reset(ThreadContext context) {
byteBuffer.reset();
return this;
}

@JRubyMethod
public IRubyObject mark(ThreadContext context) {
byteBuffer.mark();
return this;
}

/**
* Removes all the content in the byteBuffer
*
* @param context
* @return
*/
@JRubyMethod
public IRubyObject clear(ThreadContext context) {
byteBuffer.clear();
return this;
}

@JRubyMethod
public IRubyObject compact(ThreadContext context) {
byteBuffer.compact();
return this;
}

@JRubyMethod(name = "capacity")
public IRubyObject capacity(ThreadContext context) {
int cap = byteBuffer.capacity();
return context.getRuntime().newFixnum(cap);
}

@JRubyMethod
public IRubyObject position(ThreadContext context, IRubyObject newPosition) {
int position = RubyNumeric.num2int(newPosition);
byteBuffer.position(position);
return this;
}

@JRubyMethod(name = "limit")
public IRubyObject limit(ThreadContext context, IRubyObject newLimit) {
int limit = RubyNumeric.num2int(newLimit);
byteBuffer.limit(limit);
return this;
}

@JRubyMethod(name = "limit?")
public IRubyObject limit(ThreadContext context) {
int lmt = byteBuffer.limit();
return context.getRuntime().newFixnum(lmt);
}

@JRubyMethod(name = "to_s")
public IRubyObject to_String(ThreadContext context) {
return JavaUtil.convertJavaToRuby(context.getRuntime(), byteBuffer.toString());
}

public java.nio.ByteBuffer getBuffer() {
return byteBuffer;
}
}
9 changes: 9 additions & 0 deletions ext/nio4r/org/nio4r/Nio4r.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.jruby.runtime.load.Library;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.Block;
import org.nio4r.ByteBuffer;

public class Nio4r implements Library {
private Ruby ruby;
Expand All @@ -45,6 +46,14 @@ public IRubyObject allocate(Ruby ruby, RubyClass rc) {
}, nio);

monitor.defineAnnotatedMethods(Monitor.class);

RubyClass byteBuffer = ruby.defineClassUnder("ByteBuffer", ruby.getObject(), new ObjectAllocator() {
public IRubyObject allocate(Ruby ruby, RubyClass rc) {
return new ByteBuffer(ruby, rc);
}
}, nio);

byteBuffer.defineAnnotatedMethods(ByteBuffer.class);
}

public static int symbolToInterestOps(Ruby ruby, SelectableChannel channel, IRubyObject interest) {
Expand Down
1 change: 1 addition & 0 deletions lib/nio.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def self.engine
if ENV["NIO4R_PURE"] == "true" || (Gem.win_platform? && !defined?(JRUBY_VERSION))
require "nio/monitor"
require "nio/selector"
require "nio/bytebuffer"
NIO::ENGINE = "ruby".freeze
else
require "nio4r_ext"
Expand Down
Loading