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

org.jruby.ext.ffi.Buffer Maven / Gradle / Ivy

There is a newer version: 0.8.14
Show newest version

package org.jruby.ext.ffi;


import java.nio.ByteOrder;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyFixnum;
import org.jruby.RubyModule;
import org.jruby.RubyString;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.internal.runtime.methods.CallConfiguration;
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.runtime.*;
import org.jruby.runtime.builtin.IRubyObject;
import static org.jruby.runtime.Visibility.*;


@JRubyClass(name = "FFI::Buffer", parent = "FFI::" + AbstractMemory.ABSTRACT_MEMORY_RUBY_CLASS)
public final class Buffer extends AbstractMemory {
    /** Indicates that the Buffer is used for data copied IN to native memory */
    public static final int IN = 0x1;

    /** Indicates that the Buffer is used for data copied OUT from native memory */
    public static final int OUT = 0x2;
    
    private int inout;

    public static RubyClass createBufferClass(Ruby runtime, RubyModule module) {
        RubyClass result = module.defineClassUnder("Buffer",
                module.getClass(AbstractMemory.ABSTRACT_MEMORY_RUBY_CLASS),
                BufferAllocator.INSTANCE);
        result.defineAnnotatedMethods(Buffer.class);
        result.defineAnnotatedConstants(Buffer.class);
//        result.getSingletonClass().addMethod("new", new NewInstanceMethod(result.getRealClass()));

        return result;
    }

    private static final class BufferAllocator implements ObjectAllocator {
        static final ObjectAllocator INSTANCE = new BufferAllocator();

        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
            return new Buffer(runtime, klazz);
        }
    }

    private static final class NewInstanceMethod extends DynamicMethod {
        private final NativeCall new1, new2, new3;
        private NewInstanceMethod(RubyModule implementationClass) {
            super(implementationClass, Visibility.PUBLIC, CallConfiguration.FrameNoneScopeNone);

            new1 = new NativeCall(Buffer.class, "newInstance", IRubyObject.class,
                    new Class[] { ThreadContext.class, IRubyObject.class, IRubyObject.class }, true, false);
            new2 = new NativeCall(Buffer.class, "newInstance", IRubyObject.class,
                    new Class[] { ThreadContext.class, IRubyObject.class, IRubyObject.class, IRubyObject.class }, true, false);
            new3 = new NativeCall(Buffer.class, "newInstance", IRubyObject.class,
                    new Class[] { ThreadContext.class, IRubyObject.class, IRubyObject.class, IRubyObject.class, IRubyObject.class }, true, false);
        }

        @Override
        public DynamicMethod dup() {
            return this;
        }

        @Override
        public Arity getArity() {
            return Arity.fixed(1);
        }

        @Override
        public NativeCall getNativeCall() {
            return new1;
        }

        @Override
        public void setHandle(Object handle) {}

        @Override
        public NativeCall getNativeCall(int args, boolean block) {
            switch (args) {
                case 1:
                    return new1;
                case 2:
                    return new2;
                case 3:
                    return new3;
            }
            return super.getNativeCall(args, block);    //To change body of overridden methods use File | Settings | File Templates.
        }

        @Override
        public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
            switch (args.length) {
                case 1:
                    return call(context, self, clazz, name, args[0]);

                case 2:
                case 3:
                    return call(context, self, clazz, name, args[0], args[1]);

                default:
                    return ((RubyClass) self).newInstance(context, args, block);
            }
        }


        @Override
        public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule klazz, String name,
                                IRubyObject arg0) {
            return Buffer.newInstance(context, self, arg0);
        }

        @Override
        public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule klazz, String name,
                                IRubyObject arg0, IRubyObject arg1) {
            return Buffer.newInstance(context, self, arg0, arg1);
        }

        @Override
        public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule klazz, String name,
                                IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
            return Buffer.newInstance(context, self, arg0, arg1);
        }
    }

    public Buffer(Ruby runtime, RubyClass klass) {
        super(runtime, klass, runtime.getFFI().getNullMemoryIO(), 0, 0);
        this.inout = IN | OUT;
    }
    
    public Buffer(Ruby runtime, int size) {
        this(runtime, size, IN | OUT);
    }
    
    public Buffer(Ruby runtime, int size, int flags) {
        this(runtime, runtime.getFFI().bufferClass,
            allocateMemoryIO(runtime, size), size, 1, flags);
    }

    private Buffer(Ruby runtime, IRubyObject klass, MemoryIO io, long size, int typeSize, int inout) {
        super(runtime, (RubyClass) klass, io, size, typeSize);
        this.inout = inout;
    }
    
    private static final int getCount(IRubyObject countArg) {
        return countArg instanceof RubyFixnum ? RubyFixnum.fix2int(countArg) : 1;
    }
    
    private static Buffer allocate(ThreadContext context, IRubyObject recv, 
            IRubyObject sizeArg, int count, int flags) {
        final int typeSize = calculateTypeSize(context, sizeArg);
        final int total = typeSize * count;
        return new Buffer(context.runtime, recv,
                allocateMemoryIO(context.runtime, total), total, typeSize, flags);
    }

    private IRubyObject init(ThreadContext context, IRubyObject rbTypeSize, int count, int flags) {
        this.typeSize = calculateTypeSize(context, rbTypeSize);
        this.size = this.typeSize * count;
        this.inout = flags;
        setMemoryIO(allocateMemoryIO(context.runtime, (int) this.size));

        return this;
    }

    @JRubyMethod(name = "new", meta = true)
    public static IRubyObject newInstance(ThreadContext context, IRubyObject klass, IRubyObject sizeArg) {
        if (klass == context.runtime.getFFI().bufferClass) {
            return allocate(context, klass, sizeArg, 1, IN | OUT);

        } else {
            return ((RubyClass) klass).newInstance(context, sizeArg, Block.NULL_BLOCK);
        }
    }

    @JRubyMethod(name = "new", meta = true)
    public static IRubyObject newInstance(ThreadContext context, IRubyObject klass, IRubyObject sizeArg,
                                          IRubyObject countArg) {

        if (klass == context.runtime.getFFI().bufferClass) {
            return allocate(context, klass, sizeArg, RubyFixnum.fix2int(countArg), IN | OUT);

        } else {
            return ((RubyClass) klass).newInstance(context, sizeArg, countArg, Block.NULL_BLOCK);
        }
    }

    @JRubyMethod(name = "new", meta = true)
    public static IRubyObject newInstance(ThreadContext context, IRubyObject klass, IRubyObject sizeArg,
                                          IRubyObject countArg, IRubyObject clear) {
        if (klass == context.runtime.getFFI().bufferClass) {
            return allocate(context, klass, sizeArg, RubyFixnum.fix2int(countArg), IN | OUT);

        } else {
            return ((RubyClass) klass).newInstance(context, sizeArg, countArg, clear, Block.NULL_BLOCK);
        }
    }

    @JRubyMethod(name = "new", meta = true, rest = true)
    public static IRubyObject newInstance(ThreadContext context, IRubyObject klass, IRubyObject[] args) {
        if (klass == context.runtime.getFFI().bufferClass) {
            switch (args.length) {
                case 1:
                    return newInstance(context, klass, args[0]);
                case 2:
                case 3:
                    return newInstance(context, klass, args[0], args[1]);

                default:
                    return ((RubyClass) klass).newInstance(context, args, Block.NULL_BLOCK);
            }
        } else {
            return ((RubyClass) klass).newInstance(context, args, Block.NULL_BLOCK);
        }
    }


    @JRubyMethod(name = "initialize", visibility = PRIVATE)
    public IRubyObject initialize(ThreadContext context, IRubyObject sizeArg) {
        return sizeArg instanceof RubyFixnum
                ? init(context, RubyFixnum.one(context.runtime),
                    RubyFixnum.fix2int(sizeArg), IN | OUT)
                : init(context, sizeArg, 1, IN | OUT);
    }

    @JRubyMethod(name = "initialize", visibility = PRIVATE)
    public IRubyObject initialize(ThreadContext context, IRubyObject sizeArg, IRubyObject arg2) {
        return init(context, sizeArg, getCount(arg2), IN | OUT);
    }

    @JRubyMethod(name = "initialize", visibility = PRIVATE)
    public IRubyObject initialize(ThreadContext context, IRubyObject sizeArg,
            IRubyObject countArg, IRubyObject clearArg) {
        return init(context, sizeArg, RubyFixnum.fix2int(countArg), IN | OUT);
    }
    
    /**
     * 
     */
    @JRubyMethod(required = 1, visibility=PRIVATE)
    public IRubyObject initialize_copy(ThreadContext context, IRubyObject other) {
        if (this == other) {
            return this;
        }
        Buffer orig = (Buffer) other;
        this.typeSize = orig.typeSize;
        this.size = orig.size;
        this.inout = orig.inout;
        
        setMemoryIO(orig.getMemoryIO().dup());
        return this;
    }

    @JRubyMethod(name = { "alloc_inout", "__alloc_inout" }, meta = true)
    public static Buffer allocateInOut(ThreadContext context, IRubyObject recv, IRubyObject sizeArg) {
        return allocate(context, recv, sizeArg, 1, IN | OUT);
    }

    @JRubyMethod(name = { "alloc_inout", "__alloc_inout" }, meta = true)
    public static Buffer allocateInOut(ThreadContext context, IRubyObject recv,
            IRubyObject sizeArg, IRubyObject arg2) {
        return allocate(context, recv, sizeArg, getCount(arg2), IN | OUT);
    }

    @JRubyMethod(name = { "alloc_inout", "__alloc_inout" }, meta = true)
    public static Buffer allocateInOut(ThreadContext context, IRubyObject recv, 
            IRubyObject sizeArg, IRubyObject countArg, IRubyObject clearArg) {
        return allocate(context, recv, sizeArg, RubyFixnum.fix2int(countArg), IN | OUT);
    }

    @JRubyMethod(name = { "new_in", "alloc_in", "__alloc_in" }, meta = true)
    public static Buffer allocateInput(ThreadContext context, IRubyObject recv, IRubyObject arg) {       
        return allocate(context, recv, arg, 1, IN);
    }

    @JRubyMethod(name = { "new_in", "alloc_in", "__alloc_in" }, meta = true)
    public static Buffer allocateInput(ThreadContext context, IRubyObject recv, IRubyObject sizeArg, IRubyObject arg2) {
        return allocate(context, recv, sizeArg, getCount(arg2), IN);
    }

    @JRubyMethod(name = { "new_in", "alloc_in", "__alloc_in" }, meta = true)
    public static Buffer allocateInput(ThreadContext context, IRubyObject recv,
            IRubyObject sizeArg, IRubyObject countArg, IRubyObject clearArg) {
        return allocate(context, recv, sizeArg, RubyFixnum.fix2int(countArg), IN);
    }

    @JRubyMethod(name = {  "new_out", "alloc_out", "__alloc_out" }, meta = true)
    public static Buffer allocateOutput(ThreadContext context, IRubyObject recv, IRubyObject sizeArg) {
        return allocate(context, recv, sizeArg, 1, OUT);
    }

    @JRubyMethod(name = {  "new_out", "alloc_out", "__alloc_out" }, meta = true)
    public static Buffer allocateOutput(ThreadContext context, IRubyObject recv, IRubyObject sizeArg, IRubyObject arg2) {
        return allocate(context, recv, sizeArg, getCount(arg2), OUT);
    }

    @JRubyMethod(name = {  "new_out", "alloc_out", "__alloc_out" }, meta = true)
    public static Buffer allocateOutput(ThreadContext context, IRubyObject recv,
            IRubyObject sizeArg, IRubyObject countArg, IRubyObject clearArg) {
        return allocate(context, recv, sizeArg, RubyFixnum.fix2int(countArg), OUT);
    }

    @JRubyMethod(name = "inspect")
    public IRubyObject inspect(ThreadContext context) {
        return RubyString.newString(context.runtime,
                String.format("#", size));
    }

    
    public final AbstractMemory order(Ruby runtime, ByteOrder order) {
        return new Buffer(runtime, getMetaClass(),
                order.equals(getMemoryIO().order()) ? getMemoryIO() : new SwappedMemoryIO(runtime, getMemoryIO()),
                size, typeSize, inout);
    }

    protected AbstractMemory slice(Ruby runtime, long offset) {
        return new Buffer(runtime, getMetaClass(), this.io.slice(offset), this.size - offset, this.typeSize, this.inout);
    }

    protected AbstractMemory slice(Ruby runtime, long offset, long size) {
        return new Buffer(runtime, getMetaClass(), this.io.slice(offset, size), size, this.typeSize, this.inout);
    }

    protected Pointer getPointer(Ruby runtime, long offset) {
        return new Pointer(runtime, getMemoryIO().getMemoryIO(offset));
    }
    public int getInOutFlags() {
        return inout;
    }
    
    private static MemoryIO allocateMemoryIO(Ruby runtime, int size) {
        return Factory.getInstance().allocateTransientDirectMemory(runtime, size, 8, true);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy