Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
BEGIN LICENSE BLOCK ***** Version: EPL 1.0/GPL 2.0/LGPL 2.1 The contents of this file are subject to the Eclipse 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/epl-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 <ola@ologix.com> Copyright (C) 2006 Ryan Bell <ryan.l.bell@gmail.com> Copyright (C) 2007 Thomas E Enebo <enebo@acm.org> Copyright (C) 2008 Vladimir Sizikov <vsizikov@gmail.com> 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 EPL, 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 EPL, the GPL or the LGPL. END LICENSE BLOCK ***
  
  package org.jruby.ext.stringio;
  
  import org.jruby.Ruby;
  import org.jruby.RubyIO;
  
  import java.util.Arrays;
  
  import static org.jruby.CompatVersion.RUBY1_9;
  import static org.jruby.RubyEnumerator.enumeratorize;
  import static org.jruby.runtime.Visibility.PRIVATE;
  
  @JRubyClass(name="StringIO")
  @SuppressWarnings("deprecation")
  public class StringIO extends RubyObject implements EncodingCapable {
      static class StringIOData {
        
ATTN: the value of internal might be reset to null (during StringIO.open with block), so watch out for that.
  
          RubyString string;
          int pos;
          int lineno;
          int flags;
      }
      StringIOData ptr;
  
      private static final int STRIO_READABLE = ;
      private static final int STRIO_WRITABLE = ;
      private static final int STRIO_READWRITE = ( | );
  
      private static ObjectAllocator STRINGIO_ALLOCATOR = new ObjectAllocator() {
              public IRubyObject allocate(Ruby runtimeRubyClass klass) {
              return new StringIO(runtimeklass);
          }
      };
  
      public static RubyClass createStringIOClass(final Ruby runtime) {
          RubyClass stringIOClass = runtime.defineClass(
                  "StringIO"runtime.getClass("Data"), );
  
          stringIOClass.defineAnnotatedMethods(StringIO.class);
          stringIOClass.includeModule(runtime.getEnumerable());
  
         if (runtime.getObject().isConstantDefined("Java")) {
             stringIOClass.defineAnnotatedMethods(IOJavaAddons.AnyIO.class);
         }
 
         return stringIOClass;
     }
 
     public Encoding getEncoding() {
         return ..getEncoding();
     }
 
     public void setEncoding(Encoding e) {
         ..setEncoding(e);
     }
 
     @JRubyMethod(meta = true, rest = true)
     public static IRubyObject open(ThreadContext contextIRubyObject recvIRubyObject[] argsBlock block) {
         StringIO strio = (StringIO)((RubyClass)recv).newInstance(contextargs.);
         IRubyObject val = strio;
 
         if (block.isGiven()) {
             try {
                 val = block.yield(contextstrio);
             } finally {
                 strio.ptr.string = null;
                 strio.flags &= ~;
             }
         }
         return val;
     }
 
     protected StringIO(Ruby runtimeRubyClass klass) {
         super(runtimeklass);
     }
 
     @JRubyMethod(optional = 2, visibility = )
     public IRubyObject initialize(ThreadContext contextIRubyObject[] args) {
         if ( == null) {
              = new StringIOData();
         }
 
         // does not dispatch quite right and is not really necessary for us
         //Helpers.invokeSuper(context, this, metaClass, "initialize", IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
         strioInit(contextargs);
         return this;
     }
 
     private void strioInit(ThreadContext contextIRubyObject[] args) {
         Ruby runtime = context.runtime;
         RubyString string;
         IRubyObject mode;
         boolean trunc = false;
         
         switch (args.length) {
             case 2:
                 mode = args[1];
                 if (mode instanceof RubyFixnum) {
                     int flags = RubyFixnum.fix2int(mode);
                     . = ModeFlags.getOpenFileFlagsFor(flags);
                     trunc = (flags & .) != 0;
                 } else {
                     String m = args[1].convertToString().toString();
                     . = OpenFile.ioModestrFmode(runtimem);
                     trunc = m.charAt(0) == 'w';
                 }
                 string = args[0].convertToString();
                 if ((. & .) != 0 && string.isFrozen()) {
                     throw runtime.newErrnoEACCESError("Permission denied");
                 }
                 if (trunc) {
                     string.resize(0);
                 }
                 break;
             case 1:
                 string = args[0].convertToString();
                 . = string.isFrozen() ? . : .;
                 break;
             case 0:
                 string = RubyString.newEmptyString(runtimeruntime.getDefaultExternalEncoding());
                 . = .;
                 break;
             default:
                 throw runtime.newArgumentError(args.length, 2);
         }
 
         . = string;
         . = 0;
         . = 0;
         // funky way of shifting readwrite flags into object flags
          |= (. & .) * ( / .);
     }
 
     @JRubyMethod(visibility = )
     public IRubyObject initialize_copy(ThreadContext contextIRubyObject other) {
         StringIO otherIO = (StringIO) TypeConverter.convertToType(other,
                 context.runtime.getClass("StringIO"), "to_strio");
 
         if (this == otherIOreturn this;
 
          = otherIO.ptr;
         infectBy(otherIO);
          &= ~;
          |= otherIO.flags & ;
 
         return this;
     }
 
     @JRubyMethod(name = {"binmode""flush"})
     public IRubyObject strio_self() {
         return this;
     }
 
     @JRubyMethod(name = {"fcntl"}, rest = true)
     public IRubyObject strio_unimpl(ThreadContext contextIRubyObject[] args) {
         throw context.runtime.newNotImplementedError("");
     }
 
     @JRubyMethod(name = {"fsync"})
     public IRubyObject strioZero(ThreadContext context) {
         return RubyFixnum.zero(context.runtime);
     }
 
     @JRubyMethod(name = {"sync="})
     public IRubyObject strioFirst(IRubyObject arg) {
         checkInitialized();
         return arg;
     }
 
     @JRubyMethod(name = {"isatty""tty?"})
     public IRubyObject strioFalse(ThreadContext context) {
         return context.runtime.getFalse();
     }
 
     @JRubyMethod(name = {"pid""fileno"})
     public IRubyObject strioNil(ThreadContext context) {
         return context.nil;
     }
 
     @JRubyMethod(name = "<<", required = 1)
     public IRubyObject append(ThreadContext contextIRubyObject arg) {
         // Claims conversion is done via 'to_s' in docs.
         callMethod(context"write"arg);
         
         return this
     }
 
     @JRubyMethod
     public IRubyObject close(ThreadContext context) {
         checkInitialized();
         checkOpen();
 
         // NOTE: This is 2.0 behavior to allow dup'ed StringIO to remain open when original is closed
          &= ~;
 
         return context.nil;
     }
 
     @JRubyMethod(name = "closed?")
     public IRubyObject closed_p() {
         checkInitialized();
         return getRuntime().newBoolean(closed());
     }
 
     @JRubyMethod
     public IRubyObject close_read(ThreadContext context) {
         checkReadable();
          &= ~;
         return context.nil;
     }
 
     @JRubyMethod(name = "closed_read?")
     public IRubyObject closed_read_p() {
         checkInitialized();
         return getRuntime().newBoolean(!readable());
     }
 
     @JRubyMethod
     public IRubyObject close_write(ThreadContext context) {
         checkWritable();
          &= ~;
         return context.nil;
     }
 
     @JRubyMethod(name = "closed_write?")
     public IRubyObject closed_write_p() {
         checkInitialized();
         return getRuntime().newBoolean(!writable());
     }
 
     @JRubyMethod(name = "each", optional = 2, writes = ., compat = )
     public IRubyObject each(ThreadContext contextIRubyObject[] argsBlock block) {
         if (!block.isGiven()) return enumeratorize(context.runtimethis"each"args);
 
         IRubyObject line;
         
         if (args.length > 0 && !args[args.length - 1].isNil() && args[args.length - 1].checkStringType19().isNil() &&
                 RubyNumeric.num2long(args[args.length - 1]) == 0) {
             throw context.runtime.newArgumentError("invalid limit: 0 for each_line");
         }
 
         checkReadable();
         while (!(line = getline(contextargs)).isNil()) {
             block.yieldSpecific(contextline);
         }
         return this;
     }
 
     @JRubyMethod(name = "each_line", optional = 2, writes = ., compat = )
     public IRubyObject each_line(ThreadContext contextIRubyObject[] argsBlock block) {
         if (!block.isGiven()) return enumeratorize(context.runtimethis"each_line"args);
         
         return each(contextargsblock);
     }
 
     @JRubyMethod(name = "lines", optional = 2, compat = )
     public IRubyObject lines(ThreadContext contextIRubyObject[] argsBlock block) {
         context.runtime.getWarnings().warn("StringIO#lines is deprecated; use #each_line instead");
         return block.isGiven() ? each(contextargsblock) : enumeratorize(context.runtimethis"each_line"args);
     }
 
     @JRubyMethod(name = {"each_byte""bytes"})
     public IRubyObject each_byte(ThreadContext contextBlock block) {
         if (!block.isGiven()) return enumeratorize(context.runtimethis"each_byte");
 
         checkReadable();
         Ruby runtime = context.runtime;
         ByteList bytes = ..getByteList();
 
         // Check the length every iteration, since
         // the block can modify this string.
         while (. < bytes.length()) {
             block.yield(contextruntime.newFixnum(bytes.get((int.++) & 0xFF));
         }
         return this;
     }
 
     @JRubyMethod
     public IRubyObject each_char(final ThreadContext contextfinal Block block) {
         if (!block.isGiven()) return enumeratorize(context.runtimethis"each_char");
 
         IRubyObject c;
         while (!(c = getc(context)).isNil()) {
             block.yieldSpecific(contextc);
         }
         return this;
     }
 
     @JRubyMethod
     public IRubyObject chars(final ThreadContext contextfinal Block block) {
         context.runtime.getWarnings().warn("StringIO#chars is deprecated; use #each_char instead");
 
         return each_char(contextblock);
     }
 
     @JRubyMethod(name = {"eof""eof?"})
     public IRubyObject eof(ThreadContext context) {
         checkReadable();
         Ruby runtime = context.runtime;
         if (. < ..size()) return runtime.getFalse();
         return runtime.getTrue();
     }
     
     private boolean isEndOfString() {
         return . >= ..size();
     }
 
     @JRubyMethod(name = "getc", compat = .)
     public IRubyObject getc(ThreadContext context) {
         checkReadable();
 
         if (isEndOfString()) return context.runtime.getNil();
 
         int start = .;
         int total = 1 + StringSupport.bytesToFixBrokenTrailingCharacter(..getByteList(), start + 1);
         
         . += total;
         
         return context.runtime.newString(..getByteList().makeShared(starttotal));
     }
 
     @JRubyMethod(name = "getbyte")
     public IRubyObject getbyte(ThreadContext context) {
         checkReadable();
 
         if (isEndOfString()) return context.runtime.getNil();
 
         int c = ..getByteList().get(.++) & 0xFF;
 
         return context.runtime.newFixnum(c);
     }
     
     private RubyString strioSubstr(Ruby runtimeint posint len) {
         RubyString str = .;
         ByteList strByteList = str.getByteList();
         byte[] strBytes = strByteList.getUnsafeBytes();
         Encoding enc = str.getEncoding();
         int rlen = str.size() - pos;
         
         if (len > rlenlen = rlen;
         if (len < 0) len = 0;
         
         if (len == 0) return RubyString.newEmptyString(runtime);
         return RubyString.newStringShared(runtimestrBytesstrByteList.getBegin() + poslenenc);
     }
 
     private static int memchr(byte[] ptrint startint findint len) {
         for (int i = starti < start + leni++) {
             if (ptr[i] == findreturn i;
         }
         return -1;
     }
     
     private static final int CHAR_BIT = 8;
     
     private static void bm_init_skip(int[] skipbyte[] patint patPtrint m) {
         int c;
         
         for (c = 0; c < (1 << ); c++) {
             skip[c] = m;
         }
         while ((--m) > 0) {
             skip[pat[patPtr++]] = m;
         }
     }
     
     // Note that this is substantially more complex in 2.0 (Onigmo)
     private static int bm_search(byte[] littleint lstartint llenbyte[] bigint bstartint blenint[] skip) {
         int ijk;
         
         i = llen - 1;
         while (i < blen) {
             k = i;
             j = llen - 1;
             while (j >= 0 && big[k + bstart] == little[j + lstart]) {
                 k--;
                 j--;
             }
             if (j < 0) return k + 1;
             i += skip[big[i + bstart] & 0xFF];
         }
         return -1;
     }
 
 //        if (sepArg != null) {
 //            if (sepArg.isNil()) {
 //                int bytesAvailable = data.internal.getByteList().getRealSize() - (int)data.pos;
 //                int bytesToUse = (limit < 0 || limit >= bytesAvailable ? bytesAvailable : limit);
 //
 //                // add additional bytes to fix trailing broken character
 //                bytesToUse += StringSupport.bytesToFixBrokenTrailingCharacter(data.internal.getByteList(), bytesToUse);
 //
 //                ByteList buf = data.internal.getByteList().makeShared(
 //                    (int)data.pos, bytesToUse);
 //                data.pos += buf.getRealSize();
 //                return makeString(runtime, buf);
 //            }
 //
 //            sep = sepArg.convertToString().getByteList();
 //            if (sep.getRealSize() == 0) {
 //                isParagraph = true;
 //                sep = Stream.PARAGRAPH_SEPARATOR;
 //            }
 //        }
 //
 //        if (isEndOfString() || data.eof) return context.nil;
 //
 //        ByteList ss = data.internal.getByteList();
 //
 //        if (isParagraph) {
 //            swallowLF(ss);
 //            if (data.pos == ss.getRealSize()) {
 //                return runtime.getNil();
 //            }
 //        }
 //
 //        int sepIndex = ss.indexOf(sep, (int)data.pos);
 //
 //        ByteList add;
 //        if (-1 == sepIndex) {
 //            sepIndex = data.internal.getByteList().getRealSize();
 //            add = ByteList.EMPTY_BYTELIST;
 //        } else {
 //            add = sep;
 //        }
 //
 //        int bytes = sepIndex - (int)data.pos;
 //        int bytesToUse = (limit < 0 || limit >= bytes ? bytes : limit);
 //
 //        int bytesWithSep = sepIndex - (int)data.pos + add.getRealSize();
 //        int bytesToUseWithSep = (limit < 0 || limit >= bytesWithSep ? bytesWithSep : limit);
 //
 //        ByteList line = new ByteList(bytesToUseWithSep);
 //        if (is19) line.setEncoding(data.internal.getByteList().getEncoding());
 //        line.append(data.internal.getByteList(), (int)data.pos, bytesToUse);
 //        data.pos += bytesToUse;
 //
 //        if (is19) {
 //            // add additional bytes to fix trailing broken character
 //            int extraBytes = StringSupport.bytesToFixBrokenTrailingCharacter(line, line.length());
 //            if (extraBytes != 0) {
 //                line.append(data.internal.getByteList(), (int)data.pos, extraBytes);
 //                data.pos += extraBytes;
 //            }
 //        }
 //
 //        int sepBytesToUse = bytesToUseWithSep - bytesToUse;
 //        line.append(add, 0, sepBytesToUse);
 //        data.pos += sepBytesToUse;
 //
 //        if (sepBytesToUse >= add.getRealSize()) {
 //            data.lineno++;
 //        }
 //
 //        return makeString(runtime, line);
 //    }
 //
 //    private void swallowLF(ByteList list) {
 //        while (ptr.pos < list.getRealSize()) {
 //            if (list.get((int)ptr.pos) == '\n') {
 //                ptr.pos++;
 //            } else {
 //                break;
 //            }
 //        }
 //    }
 
     @JRubyMethod(name = "gets", optional = 2, writes = ., compat = .)
     public IRubyObject gets(ThreadContext contextIRubyObject[] args) {
         checkReadable();
 
         IRubyObject str = getline(contextargs);
 
         context.setLastLine(str);
         return str;
     }
 
     // strio_getline
     private IRubyObject getline(ThreadContext contextIRubyObject[] args) {
         Ruby runtime = context.runtime;
 
         IRubyObject str = context.nil;;
         int nlimit = -1;
 
         switch (args.length) {
             case 0:
                 str = runtime.getGlobalVariables().get("$/");
                 break;
 
             case 1:
             {
                 str = args[0];
                 if (!str.isNil() && !(str instanceof RubyString)) {
                     IRubyObject tmp = str.checkStringType19();
                     if (tmp.isNil()) {
                         limit = RubyNumeric.num2int(str);
                         if (limit == 0) return runtime.newString();
                         str = runtime.getGlobalVariables().get("$/");
                     } else {
                         str = tmp;
                     }
                 }
                 break;
             }
 
             case 2:
                 if (!args[0].isNil()) str = args[0].convertToString();
                 // 2.0 ignores double nil, 1.9 raises
                 if (runtime.is2_0()) {
                     if (!args[1].isNil()) {
                         limit = RubyNumeric.num2int(args[1]);
                     }
                 } else {
                     limit = RubyNumeric.num2int(args[1]);
                 }
                 break;
         }
 
         if (isEndOfString()) {
             return context.nil;
         }
 
         ByteList dataByteList = ..getByteList();
         byte[] dataBytes = dataByteList.getUnsafeBytes();
         int begin = dataByteList.getBegin();
         int s = begin + .;
         int e = begin + dataByteList.getRealSize();
         int p;
 
         if (limit > 0 && s + limit < e) {
             e = dataByteList.getEncoding().rightAdjustCharHead(dataBytesss + limite);
         }
         if (str.isNil()) {
             str = strioSubstr(runtime.e - s);
         } else if ((n = ((RubyString)str).size()) == 0) {
             // this is not an exact port; the original confused me
             p = s;
             // remove leading \n
             while (dataBytes[p] == '\n') {
                 if (++p == e) {
                     return context.nil;
                 }
             }
             s = p;
             // find next \n or end; if followed by \n, include it too
             p = memchr(dataBytesp'\n'e - p);
             if (p != -1) {
                 if (++p < e && dataBytes[p] == '\n') {
                     e = p + 1;
                 } else {
                     e = p;
                 }
             }
             str = strioSubstr(runtimes - begine - s);
         } else if (n == 1) {
             RubyString strStr = (RubyString)str;
             ByteList strByteList = strStr.getByteList();
             if ((p = memchr(dataBytessstrByteList.get(0), e - s)) != -1) {
                 e = p + 1;
             }
             str = strioSubstr(runtime.e - s);
         } else {
             if (n < e - s) {
                 RubyString strStr = (RubyString)str;
                 ByteList strByteList = strStr.getByteList();
                 byte[] strBytes = strByteList.getUnsafeBytes();
 
                 int[] skip = new int[1 << ];
                 int pos;
                 p = strByteList.getBegin();
                 bm_init_skip(skipstrBytespn);
                 if ((pos = bm_search(strBytespndataBytesse - sskip)) >= 0) {
                     e = s + pos + n;
                 }
             }
             str = strioSubstr(runtime.e - s);
         }
         . = e - begin;
         .++;
         return str;
     }
 
     @JRubyMethod(name = {"length""size"})
     public IRubyObject length() {
         checkInitialized();
         checkFinalized();
         return getRuntime().newFixnum(..size());
     }
 
     @JRubyMethod(name = "lineno")
     public IRubyObject lineno(ThreadContext context) {
         return context.runtime.newFixnum(.);
     }
 
     @JRubyMethod(name = "lineno=", required = 1)
     public IRubyObject set_lineno(ThreadContext contextIRubyObject arg) {
         . = RubyNumeric.fix2int(arg);
 
         return context.nil;
     }
 
     @JRubyMethod(name = {"pos""tell"})
     public IRubyObject pos(ThreadContext context) {
         checkInitialized();
 
         return context.runtime.newFixnum(.);
     }
 
     @JRubyMethod(name = "pos=", required = 1)
     public IRubyObject set_pos(IRubyObject arg) {
         checkInitialized();
 
         int p = RubyNumeric.fix2int(arg);
         
         if (p < 0) throw getRuntime().newErrnoEINVALError(arg.toString());
 
         . = p;
 
         return arg;
     }
 
     @JRubyMethod(name = "print", rest = true)
     public IRubyObject print(ThreadContext contextIRubyObject[] args) {
         return RubyIO.print19(contextthisargs);
     }
 
     @JRubyMethod(name = "printf", required = 1, rest = true)
     public IRubyObject printf(ThreadContext contextIRubyObject[] args) {
         callMethod(context"write", RubyKernel.sprintf(contextthisargs));
         return getRuntime().getNil();
     }
 
     private void strioExtend(int posint len) {
         int olen;
 
         checkModifiable();
         olen = ..size();
         if (pos + len > olen) {
             ..resize(pos + len);
             if (pos > olen) {
                 ByteList ptrByteList = ..getByteList();
                 // zero the gap
                 Arrays.fill(ptrByteList.getUnsafeBytes(),
                         ptrByteList.getBegin() + olen,
                         ptrByteList.getBegin() + pos,
                         (byte)0);
             }
         } else {
             ..modify19();
         }
     }
 
     @JRubyMethod(name = "putc", required = 1)
     public IRubyObject putc(IRubyObject ch) {
         checkWritable();
         byte c = RubyNumeric.num2chr(ch);
         int olen;
 
         checkModifiable();
 
         olen = ..size();
         if ((. & .) != 0) {
             . = olen;
         }
         strioExtend(., 1);
         ..getByteList().set(.++, c);
         ..infectBy(this);
         return ch;
     }
 
     public static final ByteList NEWLINE = ByteList.create("\n");
 
     @JRubyMethod(name = "puts", rest = true)
     public IRubyObject puts(ThreadContext contextIRubyObject[] args) {
         checkModifiable();
         return puts(contextthisargs);
     }
 
     private static IRubyObject puts(ThreadContext contextIRubyObject maybeIOIRubyObject[] args) {
         // TODO: This should defer to RubyIO logic, but we don't have puts right there for 1.9
         Ruby runtime = context.runtime;
         if (args.length == 0) {
             RubyIO.write(contextmaybeIO, RubyString.newStringShared(runtime));
             return runtime.getNil();
         }
 
         for (int i = 0; i < args.lengthi++) {
             RubyString line = runtime.newString();
 
             if (args[i].isNil()) {
                 if (!runtime.is1_9()) {
                     line = runtime.newString("nil");
                 }
             } else {
                 IRubyObject tmp = args[i].checkArrayType();
                 if (!tmp.isNil()) {
                     RubyArray arr = (RubyArraytmp;
                     if (runtime.isInspecting(arr)) {
                         line = runtime.newString("[...]");
                     } else {
                         inspectPuts(contextmaybeIOarr);
                         continue;
                     }
                 } else {
                     if (args[iinstanceof RubyString) {
                         line = (RubyStringargs[i];
                     } else {
                         line = args[i].asString();
                     }
                 }
             }
 
             RubyIO.write(contextmaybeIOline);
 
             if (!line.getByteList().endsWith()) {
                 RubyIO.write(contextmaybeIO, RubyString.newStringShared(runtime));
             }
         }
 
         return runtime.getNil();
     }
 
     private static IRubyObject inspectPuts(ThreadContext contextIRubyObject maybeIORubyArray array) {
         Ruby runtime = context.runtime;
         try {
             runtime.registerInspecting(array);
             return puts(contextmaybeIOarray.toJavaArray());
         } finally {
             runtime.unregisterInspecting(array);
         }
     }
     
     // Make string based on internal data encoding (which ironically is its
     // external encoding.  This seems messy and we should consider a more
     // uniform method for makeing strings (we have a slightly different variant
     // of this in RubyIO.
     private RubyString makeString(Ruby runtimeByteList bufboolean setEncoding) {
         if (runtime.is1_9() && setEncodingbuf.setEncoding(..getEncoding());
 
         RubyString str = RubyString.newString(runtimebuf);
         str.setTaint(true);
 
         return str;        
     }
     
     private RubyString makeString(Ruby runtimeByteList buf) {
         return makeString(runtimebuftrue);
     }
 
     @JRubyMethod(name = "read", optional = 2)
     public IRubyObject read(ThreadContext contextIRubyObject[] args) {
         checkReadable();
 
         Ruby runtime = context.runtime;
         IRubyObject str = runtime.getNil();
         int len;
         boolean binary = false;
 
         switch (args.length) {
         case 2:
             str = args[1];
             if (!str.isNil()) {
                 str = str.convertToString();
                 ((RubyString)str).modify();
             }
         case 1:
             if (!args[0].isNil()) {
                 len = RubyNumeric.fix2int(args[0]);
 
                 if (len < 0) {
                     throw getRuntime().newArgumentError("negative length " + len + " given");
                 }
                 if (len > 0 && isEndOfString()) {
                     if (!str.isNil()) ((RubyString)str).resize(0);
                     return getRuntime().getNil();
                 }
                 binary = true;
                 break;
             }
         case 0:
             len = ..size();
             if (len <= .) {
                 if (str.isNil()) {
                     str = runtime.newString();
                 } else {
                     ((RubyString)str).resize(0);
                 }
 
                 return str;
             } else {
                 len -= .;
             }
             break;
         default:
             throw getRuntime().newArgumentError(args.length, 0);
         }
 
         if (str.isNil()) {
             str = strioSubstr(runtime.len);
             if (binary) ((RubyString)str).setEncoding(.);
         } else {
             int rest = ..size() - .;
             if (len > restlen = rest;
             ((RubyString)str).resize(len);
             ByteList strByteList = ((RubyString)str).getByteList();
             byte[] strBytes = strByteList.getUnsafeBytes();
             ByteList dataByteList = ..getByteList();
             byte[] dataBytes = dataByteList.getUnsafeBytes();
             System.arraycopy(dataBytesdataByteList.getBegin() + .strBytesstrByteList.getBegin(), len);
             if (binary) {
                 ((RubyString)str).setEncoding(.);
             } else {
                 ((RubyString)str).setEncoding(..getEncoding());
             }
         }
         . += ((RubyString)str).size();
         return str;
     }
 
     @JRubyMethod(name="read_nonblock", compat = ., optional = 2)
     public IRubyObject read_nonblock(ThreadContext contextIRubyObject[] args) {
         // TODO: nonblock exception option
 
         IRubyObject val = read(contextargs);
         if (val.isNil()) {
             throw context.runtime.newEOFError();
         }
 
         return val;
     }
 
     @JRubyMethod(name = "readchar", compat = .)
     public IRubyObject readchar(ThreadContext context) {
         IRubyObject c = callMethod(context"getc");
 
         if (c.isNil()) throw getRuntime().newEOFError();
 
         return c;
     }
 
     @JRubyMethod(name = "readbyte", compat = .)
     public IRubyObject readbyte(ThreadContext context) {
         IRubyObject c = callMethod(context"getbyte");
 
         if (c.isNil()) throw getRuntime().newEOFError();
 
         return c;
     }
 
     @JRubyMethod(name = "readline", optional = 1, writes = ., compat = )
     public IRubyObject readline(ThreadContext contextIRubyObject[] args) {
         IRubyObject line = callMethod(context"gets"args);
 
         if (line.isNil()) throw getRuntime().newEOFError();
 
         return line;
     }
 
     @JRubyMethod(name = "readlines", optional = 2, compat = )
     public IRubyObject readlines(ThreadContext contextIRubyObject[] args) {
         Ruby runtime = context.runtime;
         
         if (args.length > 0 && !args[args.length - 1].isNil() && args[args.length - 1].checkStringType19().isNil() &&
                 RubyNumeric.num2long(args[args.length - 1]) == 0) {
             throw runtime.newArgumentError("invalid limit: 0 for each_line");
         }
 
         RubyArray ary = runtime.newArray();
         IRubyObject line;
 
         checkReadable();
 
         while (!(line = getline(contextargs)).isNil()) {
             ary.append(line);
         }
         return ary;
     }
 
     @JRubyMethod(name = "reopen", required = 0, optional = 2)
     public IRubyObject reopen(ThreadContext contextIRubyObject[] args) {
         checkFrozen();
         
         if (args.length == 1 && !(args[0] instanceof RubyString)) {
             return initialize_copy(contextargs[0]);
         }
 
         // reset the state
         strioInit(contextargs);
         return this;
     }
 
     @JRubyMethod(name = "rewind")
     public IRubyObject rewind(ThreadContext context) {
         checkInitialized();
 
         this.. = 0;
         this.. = 0;
         return RubyFixnum.zero(context.runtime);
     }
 
     @JRubyMethod(required = 1, optional = 1)
     public IRubyObject seek(ThreadContext contextIRubyObject[] args) {
         Ruby runtime = context.runtime;
 
         checkFrozen();
         checkFinalized();
         
         int offset = RubyNumeric.num2int(args[0]);
         IRubyObject whence = context.nil;
 
         if (args.length > 1 && !args[0].isNil()) whence = args[1];
         
         checkOpen();
 
         switch (whence.isNil() ? 0 : RubyNumeric.num2int(whence)) {
             case 0:
                 break;
             case 1:
                 offset += .;
                 break;
             case 2:
                 offset += ..size();
                 break;
             default:
                 throw runtime.newErrnoEINVALError("invalid whence");
         }
         
         if (offset < 0) throw runtime.newErrnoEINVALError("invalid seek value");
 
         . = offset;
 
         return RubyFixnum.zero(runtime);
     }
 
     @JRubyMethod(name = "string=", required = 1)
     public IRubyObject set_string(IRubyObject arg) {
         checkFrozen();
         . &= ~.;
         RubyString str = arg.convertToString();
         . = str.isFrozen() ? . : .;
         . = 0;
        . = 0;
        return . = str;
    }
    @JRubyMethod(name = "string")
    public IRubyObject string(ThreadContext context) {
        if (. == nullreturn context.nil;
        return .;
    }
    @JRubyMethod(name = "sync")
    public IRubyObject sync(ThreadContext context) {
        checkInitialized();
        return context.runtime.getTrue();
    }
    
    @JRubyMethod(name = {"sysread""readpartial"}, optional = 2, compat = )
    public IRubyObject sysread(ThreadContext contextIRubyObject[] args) {
        IRubyObject val = callMethod(context"read"args);
        
        if (val.isNil()) throw getRuntime().newEOFError();
        
        return val;
    }
    
    // only here for the fake-out class in org.jruby
    public IRubyObject sysread(IRubyObject[] args) {
        return sysread(getRuntime().getCurrentContext(), args);
    }
    @JRubyMethod(name = "truncate", required = 1)
    public IRubyObject truncate(IRubyObject len) {
        checkWritable();
        int l = RubyFixnum.fix2int(len);
        int plen = ..size();
        if (l < 0) {
            throw getRuntime().newErrnoEINVALError("negative legnth");
        }
        ..resize(l);
        ByteList buf = ..getByteList();