Hi,

So, I've been spending some time implementing a pure Java StringIO
(which this time actually passes the test cases... =)

I also did a little timing test, with operations taken directly from the
pick axe.
I did these 10_000 times for the old and the new implementations:
  sio = StringIO.new("time flies like an arrow")
  sio.read(5)
  sio.read(5)
  sio.pos = 18
  sio.read(5)
  sio.rewind
  sio.write("fruit")
  sio.pos=16
  sio.write("a banana")
  sio.rewind
  sio.read

And got this output:
Doing the operations with old StringIO 10000 times took 6409.0 milli seconds
Doing the operations with new StringIO 10000 times took 791.0 milli seconds

Which looks good, about 8 times increase. Should be welcome in some
places, neh?

The files attached are two classes, org.jruby.RubyStringIO, and
org.jruby.libraries.StringIOLibrary, and a patch for org.jruby.Ruby

Regards
 Ola Bini
/***** BEGIN LICENSE BLOCK *****
 * Version: CPL 1.0/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Common Public
 * License Version 1.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.eclipse.org/legal/cpl-v10.html
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * Copyright (C) 2006 Ola Bini <[EMAIL PROTECTED]>
 * 
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the CPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the CPL, the GPL or the LGPL.
 ***** END LICENSE BLOCK *****/
/**
 * $Id: $
 */
package org.jruby;

import org.jruby.runtime.CallbackFactory;
import org.jruby.runtime.builtin.IRubyObject;

import org.jruby.util.IOHandler;

/**
 * @author <a href="mailto:[EMAIL PROTECTED]">Ola Bini</a>
 * @version $Revision: $
 */
public class RubyStringIO extends RubyObject {
    public static RubyClass createStringIOClass(final IRuby runtime) {
        final RubyClass stringIOClass = runtime.defineClass("StringIO",runtime.getObject());
        final CallbackFactory callbackFactory = runtime.callbackFactory(RubyStringIO.class);
        stringIOClass.defineSingletonMethod("open", callbackFactory.getOptSingletonMethod("open"));
        stringIOClass.defineSingletonMethod("new", callbackFactory.getOptSingletonMethod("newInstance"));
        stringIOClass.defineMethod("initialize", callbackFactory.getOptMethod("initialize"));
        stringIOClass.defineMethod("<<", callbackFactory.getMethod("append",IRubyObject.class));
        stringIOClass.defineMethod("binmode", callbackFactory.getMethod("binmode"));
        stringIOClass.defineMethod("close", callbackFactory.getMethod("close"));
        stringIOClass.defineMethod("closed?", callbackFactory.getMethod("closed_p"));
        stringIOClass.defineMethod("close_read", callbackFactory.getMethod("close_read"));
        stringIOClass.defineMethod("closed_read?", callbackFactory.getMethod("closed_read_p"));
        stringIOClass.defineMethod("close_write", callbackFactory.getMethod("close_write"));
        stringIOClass.defineMethod("closed_write?", callbackFactory.getMethod("closed_write_p"));
        stringIOClass.defineMethod("each", callbackFactory.getOptMethod("each"));
        stringIOClass.defineMethod("each_byte", callbackFactory.getMethod("each_byte"));
        stringIOClass.defineMethod("each_line", callbackFactory.getMethod("each_line"));
        stringIOClass.defineMethod("eof", callbackFactory.getMethod("eof"));
        stringIOClass.defineMethod("eof?", callbackFactory.getMethod("eof_p"));
        stringIOClass.defineMethod("fcntl", callbackFactory.getMethod("fcntl"));
        stringIOClass.defineMethod("fileno", callbackFactory.getMethod("fileno"));
        stringIOClass.defineMethod("flush", callbackFactory.getMethod("flush"));
        stringIOClass.defineMethod("fsync", callbackFactory.getMethod("fsync"));
        stringIOClass.defineMethod("getc", callbackFactory.getMethod("getc"));
        stringIOClass.defineMethod("gets", callbackFactory.getOptMethod("gets"));
        stringIOClass.defineMethod("isatty", callbackFactory.getMethod("isatty"));
        stringIOClass.defineMethod("tty?", callbackFactory.getMethod("tty_p"));
        stringIOClass.defineMethod("length", callbackFactory.getMethod("length"));
        stringIOClass.defineMethod("lineno", callbackFactory.getMethod("lineno"));
        stringIOClass.defineMethod("lineno=", callbackFactory.getMethod("set_lineno",RubyFixnum.class));
        stringIOClass.defineMethod("path", callbackFactory.getMethod("path"));
        stringIOClass.defineMethod("pid", callbackFactory.getMethod("pid"));
        stringIOClass.defineMethod("pos", callbackFactory.getMethod("pos"));
        stringIOClass.defineMethod("tell", callbackFactory.getMethod("tell"));
        stringIOClass.defineMethod("pos=", callbackFactory.getMethod("set_pos",RubyFixnum.class));
        stringIOClass.defineMethod("print", callbackFactory.getOptMethod("print"));
        stringIOClass.defineMethod("printf", callbackFactory.getOptMethod("printf"));
        stringIOClass.defineMethod("putc", callbackFactory.getMethod("putc",RubyObject.class));
        stringIOClass.defineMethod("puts", callbackFactory.getOptMethod("puts"));
        stringIOClass.defineMethod("read", callbackFactory.getOptMethod("read"));
        stringIOClass.defineMethod("readchar", callbackFactory.getMethod("readchar"));
        stringIOClass.defineMethod("readline", callbackFactory.getOptMethod("readline"));
        stringIOClass.defineMethod("readlines", callbackFactory.getOptMethod("readlines"));
        stringIOClass.defineMethod("reopen", callbackFactory.getMethod("reopen",RubyObject.class));
        stringIOClass.defineMethod("rewind", callbackFactory.getMethod("rewind"));
        stringIOClass.defineMethod("seek", callbackFactory.getOptMethod("seek"));
        stringIOClass.defineMethod("size", callbackFactory.getMethod("size"));
        stringIOClass.defineMethod("string", callbackFactory.getMethod("string"));
        stringIOClass.defineMethod("string=", callbackFactory.getMethod("set_string",RubyString.class));
        stringIOClass.defineMethod("sync", callbackFactory.getMethod("sync"));
        stringIOClass.defineMethod("sync=", callbackFactory.getMethod("set_sync",RubyFixnum.class));
        stringIOClass.defineMethod("syswrite", callbackFactory.getMethod("syswrite",RubyObject.class));
        stringIOClass.defineMethod("truncate", callbackFactory.getMethod("truncate",RubyFixnum.class));
        stringIOClass.defineMethod("ungetc", callbackFactory.getMethod("ungetc",RubyFixnum.class));
        stringIOClass.defineMethod("write", callbackFactory.getMethod("write",RubyObject.class));

        return stringIOClass;
    }

    public static IRubyObject newInstance(final IRubyObject recv, final IRubyObject[] args) {
        final RubyStringIO result = new RubyStringIO(recv.getRuntime());
        result.callInit(args);
        return result;
    }

    public static IRubyObject open(final IRubyObject recv, final IRubyObject[] args) {
        RubyString str = recv.getRuntime().newString("");
        IRubyObject mode = recv.getRuntime().getNil();
        if(args.length>0) {
            str = (RubyString)args[0];
            if(args.length>1) {
                mode = args[1];
            }
        }
        final RubyStringIO strio = (RubyStringIO)newInstance(recv,new IRubyObject[]{str,mode});
        IRubyObject val = strio;
        if(recv.getRuntime().getCurrentContext().isBlockGiven()) {
            try {
                val = recv.getRuntime().getCurrentContext().yield(strio);
            } finally {
                strio.close();
            }
        }
        return val;
    }


    protected RubyStringIO(final IRuby runtime) {
        super(runtime, runtime.getClass("StringIO"));
    }

    private long pos = 0L;
    private int lineno = 0;
    private boolean eof = false;
    private StringBuffer internal;
    private boolean closedRead = false;
    private boolean closedWrite = false;

    public IRubyObject initialize(final IRubyObject[] args) {
        internal = new StringBuffer();
        if(checkArgumentCount(args, 0, 2) > 0) {
            internal.append(((RubyString)args[0]).getValue());
        }
        return this;
    }

    public IRubyObject append(final IRubyObject obj) {
        final String val = obj.toString();
        internal.replace((int)pos,(int)(pos+val.length()),val);
        pos += val.length();
        return this;
    }

    public IRubyObject binmode() {
        return getRuntime().getTrue();
    }
    
    public IRubyObject close() {
        closedRead = true;
        closedWrite = true;
        return getRuntime().getNil();
    }

    public IRubyObject closed_p() {
        return (closedRead && closedWrite) ? getRuntime().getTrue() : getRuntime().getFalse();
    }

    public IRubyObject close_read() {
        closedRead = true;
        return getRuntime().getNil();
    }

    public IRubyObject closed_read_p() {
        return closedRead ? getRuntime().getTrue() : getRuntime().getFalse();
    }

    public IRubyObject close_write() {
        closedWrite = true;
        return getRuntime().getNil();
    }

    public IRubyObject closed_write_p() {
        return closedWrite ? getRuntime().getTrue() : getRuntime().getFalse();
    }

   public IRubyObject each(final IRubyObject[] args) {
       IRubyObject line = gets(args);
       while(!line.isNil()) {
           getRuntime().getCurrentContext().yield(line);
           line = gets(args);
       }
       return this;
   }

    public IRubyObject each_byte() {
        getRuntime().newString(internal.substring((int)pos)).each_byte();
        return this;
    }

    public IRubyObject each_line() {
        return each(new RubyObject[0]);
    }

    public IRubyObject eof() {
        return (pos >= internal.length() || eof) ? getRuntime().getTrue() : getRuntime().getFalse();
    }

    public IRubyObject eof_p() {
        return (pos >= internal.length() || eof) ? getRuntime().getTrue() : getRuntime().getFalse();
    }

    public IRubyObject fcntl() {
        throw getRuntime().newNotImplementedError("fcntl not implemented");
    }

    public IRubyObject fileno() {
        return getRuntime().getNil();
    }

    public IRubyObject flush() {
        return this;
    }

    public IRubyObject fsync() {
        return RubyFixnum.zero(getRuntime());
    }

    public IRubyObject getc() {
        return getRuntime().newFixnum(internal.charAt((int)pos++));
    }

    public IRubyObject internalGets(final IRubyObject[] args) {
        if(pos < internal.length() && !eof) {
            String sep = ((RubyString)getRuntime().getGlobalVariables().get("$/")).getValue().toString();
            if(args.length>0) {
                if(args[0].isNil()) {
                    final String buf = internal.substring((int)pos);
                    pos+=buf.length();
                    return getRuntime().newString(buf);
                }
                sep = args[0].toString();
            }
            int ix = internal.indexOf(sep,(int)pos);
            String add = sep;
            if(-1 == ix) {
                ix = internal.length();
                add = "";
            }
            final String line = internal.substring((int)pos,ix)+add;
            pos = ix + add.length();
            lineno++;
            return getRuntime().newString(line);
        } else {
            return getRuntime().getNil();
        }
    }

    public IRubyObject gets(final IRubyObject[] args) {
        final IRubyObject result = internalGets(args);
        if (!result.isNil()) {
            getRuntime().getCurrentContext().setLastline(result);
        }
        return result;
    }

    public IRubyObject isatty() {
        return getRuntime().getNil();
    }

    public IRubyObject tty_p() {
        return getRuntime().getNil();
    }

    public IRubyObject length() {
        return getRuntime().newFixnum(internal.length());
    }

    public IRubyObject lineno() {
        return getRuntime().newFixnum(lineno);
    }

    public IRubyObject set_lineno(final RubyFixnum val) {
        lineno = (int)val.getLongValue();
        return getRuntime().getNil();
    }

    public IRubyObject path() {
        return getRuntime().getNil();
    }

    public IRubyObject pid() {
        return getRuntime().getNil();
    }

    public IRubyObject pos() {
        return getRuntime().newFixnum(pos);
    }

    public IRubyObject tell() {
        return getRuntime().newFixnum(pos);
    }

    public IRubyObject set_pos(final RubyFixnum val) {
        pos = (int)val.getLongValue();
        return getRuntime().getNil();
    }

    public IRubyObject print(final IRubyObject[] args) {
        if(args.length != 0) {
            for(int i=0,j=args.length;i<j;i++) {
                append(args[i]);
            }
        }
        final IRubyObject sep = getRuntime().getGlobalVariables().get("$\\");
        if(!sep.isNil()) {
            append(sep);
        }
        return getRuntime().getNil();
    }
    
    public IRubyObject printf(final IRubyObject[] args) {
        append(RubyKernel.sprintf(this,args));
        return getRuntime().getNil();
    }

    public IRubyObject putc(final RubyObject obj) {
        append(obj);
        return obj;
    }

    public IRubyObject puts(final IRubyObject[] obj) {
        for(int i=0,j=obj.length;i<j;i++) {
            append(obj[i]);
            internal.replace((int)pos,(int)(++pos),("\n"));
        }
        return getRuntime().getNil();
    }

    public IRubyObject read(final IRubyObject[] args) {
        String buf = null;
        if(!(pos >= internal.length() || eof)) {
            if(args.length > 0 && !args[0].isNil()) {
                final int end = ((int)pos) + RubyNumeric.fix2int(args[0]);
                if(end > internal.length()) {
                    buf = internal.substring((int)pos);
                } else {
                    buf = internal.substring((int)pos,end);
                }
            } else {
                buf = internal.substring((int)pos);
            }
            pos+= buf.length();
        }
        IRubyObject ret = null;
        if(buf == null) {
            if(args.length > 0) {
                return getRuntime().getNil();
            }
            return getRuntime().newString("");
        } else {
            if(args.length>1) {
                ((RubyString)args[1]).cat(buf);
                ret = args[1];
            } else {
                ret = getRuntime().newString(buf);
            }
        }

        return ret;
    }

    public IRubyObject readchar() {
        return getc();
    }

    public IRubyObject readline(final IRubyObject[] args) {
        return gets(args);
    }
    
    public IRubyObject readlines(final IRubyObject[] arg) {
        final java.util.List lns = new java.util.ArrayList();
        while(!(pos >= internal.length() || eof)) {
            lns.add(gets(arg));
        }
        return getRuntime().newArray(lns);
    }
    
    public IRubyObject reopen(final RubyObject str) {
        if(str instanceof RubyStringIO) {
            pos = ((RubyStringIO)str).pos;
            lineno = ((RubyStringIO)str).lineno;
            eof = ((RubyStringIO)str).eof;
            closedRead = ((RubyStringIO)str).closedRead;
            closedWrite = ((RubyStringIO)str).closedWrite;
            internal = new StringBuffer(((RubyStringIO)str).internal.toString());
        } else {
            pos = 0L;
            lineno = 0;
            eof = false;
            internal = new StringBuffer();
            internal.append(((RubyString)str).getValue());
        }
        return this;
    }

    public IRubyObject rewind() {
        this.pos = 0L;
        this.lineno = 0;
        return RubyFixnum.zero(getRuntime());
    }

    public IRubyObject seek(final IRubyObject[] args) {
        final long amount = ((RubyNumeric)args[0]).getLongValue();
        int whence = IOHandler.SEEK_SET;
        if(args.length > 1) {
            whence = (int)(((RubyNumeric)args[1]).getLongValue());
        }
        if(whence == IOHandler.SEEK_CUR) {
            pos += amount;
        } else if(whence == IOHandler.SEEK_END) {
            pos = internal.length() + amount;
        } else {
            pos = amount;
        }
        return RubyFixnum.zero(getRuntime());
    }

    public IRubyObject size() {
        return getRuntime().newFixnum(internal.length());
    }

    public IRubyObject string() {
        return getRuntime().newString(internal.toString());
    }

    public IRubyObject set_string(final RubyString arg) {
        return reopen(arg);
    }

    public IRubyObject sync() {
        return getRuntime().getTrue();
    }

    public IRubyObject set_sync(final RubyFixnum args) {
        return args;
    }

    public IRubyObject syswrite(final RubyObject args) {
        return write(args);
    }

    public IRubyObject truncate(final RubyFixnum args) {
        final int len = (int)args.getLongValue();
        internal.delete(len,internal.length());
        return RubyFixnum.zero(getRuntime());
    }

    public IRubyObject ungetc(final RubyFixnum args) {
        final char val = (char)args.getLongValue();
        internal.insert((int)pos,val);
        return getRuntime().getNil();
    }

    public IRubyObject write(final RubyObject args) {
        final String obj = args.toString();
        append(args);
        return getRuntime().newFixnum(obj.length());
    }
}// RubyStringIO
/***** BEGIN LICENSE BLOCK *****
 * Version: CPL 1.0/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Common Public
 * License Version 1.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.eclipse.org/legal/cpl-v10.html
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * Copyright (C) 2006 Ola Bini <[EMAIL PROTECTED]>
 * 
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the CPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the CPL, the GPL or the LGPL.
 ***** END LICENSE BLOCK *****/
/**
 * $Id: $
 */
package org.jruby.libraries;

import java.io.IOException;

import org.jruby.RubyStringIO;
import org.jruby.IRuby;
import org.jruby.runtime.load.Library;

/**
 * @author <a href="mailto:[EMAIL PROTECTED]">Ola Bini</a>
 * @version $Revision: $
 */
public class StringIOLibrary implements Library {
    public void load(IRuby runtime) throws IOException {
        RubyStringIO.createStringIOClass(runtime);
    }
}// StringIOLibrary

Attachment: stringio.patch
Description: Binary data

_______________________________________________
Jruby-devel mailing list
Jruby-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/jruby-devel

Reply via email to