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_8;
  import static org.jruby.RubyEnumerator.enumeratorize;
  import static org.jruby.runtime.Visibility.PRIVATE;
  
  @JRubyClass(name="StringIO")
  @SuppressWarnings("deprecation")
  public class RubyStringIO extends RubyObject implements EncodingCapable {
      static class StringIOData {
          int pos = 0;
          int lineno = 0;
          boolean eof = false;
          boolean closedRead = false;
          boolean closedWrite = false;
          ModeFlags modes;
        
ATTN: the value of internal might be reset to null (during StringIO.open with block), so watch out for that.
  
          RubyString string;
      }
      StringIOData ptr;
  
      private static ObjectAllocator STRINGIO_ALLOCATOR = new ObjectAllocator() {
          @Override
          public IRubyObject allocate(Ruby runtimeRubyClass klass) {
              return new RubyStringIO(runtimeklass);
          }
      };
  
      public static RubyClass createStringIOClass(final Ruby runtime) {
          RubyClass stringIOClass = runtime.defineClass(
                  "StringIO"runtime.getClass("Data"), );
  
          stringIOClass.defineAnnotatedMethods(RubyStringIO.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) {
         RubyStringIO strio = (RubyStringIO)((RubyClass)recv).newInstance(contextargs.);
         IRubyObject val = strio;
 
         if (block.isGiven()) {
             try {
                 val = block.yield(contextstrio);
             } finally {
                 strio.doFinalize();
             }
         }
         return val;
     }
 
     protected RubyStringIO(Ruby runtimeRubyClass klass) {
         super(runtimeklass);
          = new StringIOData();
     }
 
     private ModeFlags initializeModes(Object modeArgument) {
         Ruby runtime = getRuntime();
 
         if (modeArgument == null) {
             return RubyIO.newModeFlags(runtime"r+");
         } else if (modeArgument instanceof Long) {
             return RubyIO.newModeFlags(runtime, ((LongmodeArgument).longValue());
         } else {
             return RubyIO.newModeFlags(runtime, (StringmodeArgument);
         }
     }
 
     @JRubyMethod(optional = 2, visibility = )
     public IRubyObject initialize(IRubyObject[] argsBlock unusedBlock) {
         Ruby runtime = getRuntime();
         ModeFlags flags;
         RubyString str;
         
         switch (args.length) {
             case 0:
                 str = RubyString.newEmptyString(runtime);
                 flags = initializeModes("r+");
                 break;
             case 1:
                 str = args[0].convertToString();
                 flags = initializeModes(str.isFrozen() ? "r" : "r+");
                 
                 break;
             case 2:
                 str = args[0].convertToString();
                 Object modeArgument;
                 if (args[1] instanceof RubyFixnum) {
                     modeArgument = RubyFixnum.fix2long(args[1]);
                 } else {
                     modeArgument = args[1].convertToString().toString();
                 }
                 
                 flags = initializeModes(modeArgument);
                 if (flags.isWritable() && str.isFrozen()) {
                     throw runtime.newErrnoEACCESError("Permission denied");
                 }
                 break;
             default:
                 // not reached
                 throw runtime.newArgumentError(args.length, 2);
         }
 
         . = str;
         . = flags;
         
         setupModes();
 
         if (..isTruncate()) {
             ..modifyCheck();
             ..empty();
         }
 
         return this;
     }
 
     @JRubyMethod(visibility = )
     public IRubyObject initialize_copy(IRubyObject other) {
         RubyStringIO otherIO = (RubyStringIO) TypeConverter.convertToType(other
                 getRuntime().getClass("StringIO"), "to_strio");
 
         if (this == otherIOreturn this;
 
          = otherIO.ptr;
         if (otherIO.isTaint()) setTaint(true);
 
         return this;
     }
 
     @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 binmode() {
         return this;
     }
 
     @JRubyMethod
     public IRubyObject close() {
         checkInitialized();
         checkOpen();
 
         . = true;
         . = true;
 
         return getRuntime().getNil();
     }
 
     private void doFinalize() {
         . = true;
         . = true;
         . = null;
     }
 
     @JRubyMethod(name = "closed?")
     public IRubyObject closed_p() {
         checkInitialized();
         return getRuntime().newBoolean(. && .);
     }
 
     @JRubyMethod
     public IRubyObject close_read() {
         checkReadable();
         . = true;
 
         return getRuntime().getNil();
     }
 
     @JRubyMethod(name = "closed_read?")
     public IRubyObject closed_read_p() {
         checkInitialized();
         return getRuntime().newBoolean(.);
     }
 
     @JRubyMethod
     public IRubyObject close_write() {
         checkWritable();
         . = true;
 
         return getRuntime().getNil();
     }
 
     @JRubyMethod(name = "closed_write?")
     public IRubyObject closed_write_p() {
         checkInitialized();
         return getRuntime().newBoolean(.);
     }
 
     public IRubyObject eachInternal(ThreadContext contextIRubyObject[] argsBlock block) {
         IRubyObject line = getsOnly(contextargs);
 
         while (!line.isNil()) {
             block.yield(contextline);
             line = getsOnly(contextargs);
         }
 
         return this;
     }
 
     @JRubyMethod(name = "each", optional = 1, writes = .)
     public IRubyObject each(ThreadContext contextIRubyObject[] argsBlock block) {
         return block.isGiven() ? eachInternal(contextargsblock) : enumeratorize(context.runtimethis"each"args);
     }
 
     @JRubyMethod(optional = 1, compat = )
     public IRubyObject each_line(ThreadContext contextIRubyObject[] argsBlock block) {
         return block.isGiven() ? eachInternal(contextargsblock) : enumeratorize(context.runtimethis"each_line"args);
     }
 
     @JRubyMethod(optional = 1, compat = )
     public IRubyObject lines(ThreadContext contextIRubyObject[] argsBlock block) {
         return block.isGiven() ? each(contextargsblock) : enumeratorize(context.runtimethis"lines"args);
     }
 
     public IRubyObject each_byte(ThreadContext contextBlock block) {
         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(name = "each_byte")
     public IRubyObject each_byte19(ThreadContext contextBlock block) {
         return block.isGiven() ? each_byte(contextblock) : enumeratorize(context.runtimethis"each_byte");
     }
 
     @JRubyMethod
     public IRubyObject bytes(ThreadContext contextBlock block) {
         return block.isGiven() ? each_byte(contextblock) : enumeratorize(context.runtimethis"bytes");
     }
 
     @JRubyMethod
     public IRubyObject each_char(final ThreadContext contextfinal Block block) {
         return block.isGiven() ? each_charInternal(contextblock) : enumeratorize(context.runtimethis"each_char");
     }
 
     @JRubyMethod
     public IRubyObject chars(final ThreadContext contextfinal Block block) {
         return block.isGiven() ? each_charInternal(contextblock) : enumeratorize(context.runtimethis"chars");
     }
 
     public IRubyObject each_charInternal(final ThreadContext contextfinal Block block) {
         checkReadable();
 
         Ruby runtime = context.runtime;
         ByteList bytes = ..getByteList();
         int len = bytes.getRealSize();
         int end = bytes.getBegin() + len;
         Encoding enc = runtime.is1_9() ? bytes.getEncoding() : runtime.getKCode().getEncoding();
         while (. < len) {
             int pos = (int.;
             int n = StringSupport.length(encbytes.getUnsafeBytes(), posend);
 
             if (len < pos + nn = len - pos;
 
             . += n;
 
             block.yield(context..makeShared19(runtimeposn));
         }
 
         return this;
     }
 
     @JRubyMethod(name = {"eof""eof?"})
     public IRubyObject eof() {
         return getRuntime().newBoolean(isEOF());
     }
 
     private boolean isEOF() {
         return isEndOfString() || (getRuntime().is1_8() && .);
     }
     
     private boolean isEndOfString() {
         return . >= ..getByteList().length();
     }    
 
     @JRubyMethod(name = "fcntl")
     public IRubyObject fcntl() {
         throw getRuntime().newNotImplementedError("fcntl not implemented");
     }
 
     @JRubyMethod(name = "fileno")
     public IRubyObject fileno() {
         return getRuntime().getNil();
     }
 
     @JRubyMethod(name = "flush")
     public IRubyObject flush() {
         return this;
     }
 
     @JRubyMethod(name = "fsync")
     public IRubyObject fsync() {
         return RubyFixnum.zero(getRuntime());
     }
 
     @JRubyMethod(name = {"getc""getbyte"})
     public IRubyObject getc() {
         checkReadable();
         if (isEndOfString()) return getRuntime().getNil();
 
         return getRuntime().newFixnum(..getByteList().get((int).++) & 0xFF);
     }
 
     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 IRubyObject internalGets18(ThreadContext contextIRubyObject[] args) {
         Ruby runtime = context.runtime;
 
         if (!isEndOfString() && !.) {
             boolean isParagraph = false;
             ByteList sep = ((RubyString)runtime.getGlobalVariables().get("$/")).getByteList();
             IRubyObject sepArg;
             int limit = -1;
 
             sepArg = (args.length > 0 ? args[0] : null);
 
             if (sepArg != null) {
                 if (sepArg.isNil()) {
                     int bytesAvailable = ..getByteList().getRealSize() - (int).;
                     int bytesToUse = (limit < 0 || limit >= bytesAvailable ? bytesAvailable : limit);
                     
                     // add additional bytes to fix trailing broken character
                     bytesToUse += StringSupport.bytesToFixBrokenTrailingCharacter(..getByteList(), bytesToUse);
                     
                     ByteList buf = ..getByteList().makeShared(
                         (int).bytesToUse);
                     . += buf.getRealSize();
                     return makeString(runtimebuf);
                 }
 
                 sep = sepArg.convertToString().getByteList();
                 if (sep.getRealSize() == 0) {
                     isParagraph = true;
                     sep = .;
                 }
             }
 
             ByteList ss = ..getByteList();
 
             if (isParagraph) {
                 swallowLF(ss);
                 if (. == ss.getRealSize()) {
                     return runtime.getNil();
                 }
             }
 
             int sepIndex = ss.indexOf(sep, (int).);
 
             ByteList add;
             if (-1 == sepIndex) {
                 sepIndex = ..getByteList().getRealSize();
                 add = .;
             } else {
                 add = sep;
             }
 
             int bytes = sepIndex - (int).;
             int bytesToUse = (limit < 0 || limit >= bytes ? bytes : limit);
 
             int bytesWithSep = sepIndex - (int). + add.getRealSize();
             int bytesToUseWithSep = (limit < 0 || limit >= bytesWithSep ? bytesWithSep : limit);
 
             ByteList line = new ByteList(bytesToUseWithSep);
             line.append(..getByteList(), (int).bytesToUse);
             . += bytesToUse;
 
             int sepBytesToUse = bytesToUseWithSep - bytesToUse;
             line.append(add, 0, sepBytesToUse);
             . += sepBytesToUse;
 
             if (sepBytesToUse >= add.getRealSize()) {
                 .++;
             }
 
             return makeString(runtimeline);
         }
         return runtime.getNil();
     }
 
     private void swallowLF(ByteList list) {
         while (. < list.getRealSize()) {
             if (list.get((int).) == '\n') {
                 .++;
             } else {
                 break;
             }
         }
     }
 
     @JRubyMethod(name = "gets", optional = 1, writes = ., compat = .)
     public IRubyObject gets(ThreadContext contextIRubyObject[] args) {
         IRubyObject result = getsOnly(contextargs);
         context.setLastLine(result);
 
         return result;
     }
 
     public IRubyObject getsOnly(ThreadContext contextIRubyObject[] args) {
         checkReadable();
 
         return internalGets18(contextargs);
     }
 
     @JRubyMethod(name = {"tty?""isatty"})
     public IRubyObject isatty() {
         return getRuntime().getFalse();
     }
 
     @JRubyMethod(name = {"length""size"})
     public IRubyObject length() {
         checkFinalized();
         return getRuntime().newFixnum(..getByteList().length());
     }
 
     @JRubyMethod(name = "lineno")
     public IRubyObject lineno() {
         return getRuntime().newFixnum(.);
     }
 
     @JRubyMethod(name = "lineno=", required = 1)
     public IRubyObject set_lineno(IRubyObject arg) {
         . = RubyNumeric.fix2int(arg);
 
         return getRuntime().getNil();
     }
 
     @JRubyMethod(name = "path", compat = .)
     public IRubyObject path() {
         return getRuntime().getNil();
     }
 
     @JRubyMethod(name = "pid")
     public IRubyObject pid() {
         return getRuntime().getNil();
     }
 
     @JRubyMethod(name = {"pos""tell"})
     public IRubyObject pos() {
         return getRuntime().newFixnum(.);
     }
 
     @JRubyMethod(name = "pos=", required = 1)
     public IRubyObject set_pos(IRubyObject arg) {
         . = RubyNumeric.fix2int(arg);
         
         if (. < 0) throw getRuntime().newErrnoEINVALError("Invalid argument");
 
         if (getRuntime().is1_8() && !isEndOfString()) . = false;
 
         return getRuntime().getNil();
     }
 
     @JRubyMethod(name = "print", rest = true)
     public IRubyObject print(ThreadContext contextIRubyObject[] args) {
         Ruby runtime = context.runtime;
         if (args.length != 0) {
             for (int i=0,j=args.length;i<j;i++) {
                 append(contextargs[i]);
             }
         } else {
             IRubyObject arg = runtime.getGlobalVariables().get("$_");
             append(contextarg.isNil() ? makeString(runtimenew ByteList(new byte[] {'n''i''l'})) : arg);
         }
         IRubyObject sep = runtime.getGlobalVariables().get("$\\");
         if (!sep.isNil()) append(contextsep);
 
         return runtime.getNil();
     }
 
     @JRubyMethod(name = "printf", required = 1, rest = true)
     public IRubyObject printf(ThreadContext contextIRubyObject[] args) {
         append(context, RubyKernel.sprintf(contextthisargs));
         return getRuntime().getNil();
     }
 
     @JRubyMethod(name = "putc", required = 1)
     public IRubyObject putc(IRubyObject obj) {
         checkWritable();
         byte c = RubyNumeric.num2chr(obj);
         checkFrozen();
 
         ..modify();
         ByteList bytes = ..getByteList();
         if (..isAppendable()) {
             . = bytes.length();
             bytes.append(c);
         } else {
             if (isEndOfString()) bytes.length((int). + 1);
 
             bytes.set((int.c);
             .++;
         }
 
         return obj;
     }
 
     public static final ByteList NEWLINE = ByteList.create("\n");
 
     @JRubyMethod(name = "puts", rest = true)
     public IRubyObject puts(ThreadContext contextIRubyObject[] args) {
         checkWritable();
 
         // FIXME: the code below is a copy of RubyIO.puts,
         // and we should avoid copy-paste.
 
         if (args.length == 0) {
             callMethod(context"write", RubyString.newStringShared(getRuntime(), ));
             return getRuntime().getNil();
         }
 
         for (int i = 0; i < args.lengthi++) {
             RubyString line = getRuntime().newString();
 
             if (args[i].isNil()) {
                 line = getRuntime().newString("nil");
             } else {
                 IRubyObject tmp = args[i].checkArrayType();
                 if (!tmp.isNil()) {
                     RubyArray arr = (RubyArraytmp;
                     if (getRuntime().isInspecting(arr)) {
                         line = getRuntime().newString("[...]");
                     } else {
                         inspectPuts(contextarr);
                         continue;
                     }
                 } else {
                     if (args[iinstanceof RubyString) {
                         line = (RubyString)args[i];
                     } else {
                         line = args[i].asString();
                     }
                 }
             }
 
             callMethod(context"write"line);
 
             if (!line.getByteList().endsWith()) {
                 callMethod(context"write", RubyString.newStringShared(getRuntime(), ));
             }
         }
         return getRuntime().getNil();
     }
 
     private IRubyObject inspectPuts(ThreadContext contextRubyArray array) {
         try {
             getRuntime().registerInspecting(array);
             return puts(contextarray.toJavaArray());
         } finally {
             getRuntime().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) {
         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) {
         return read18(args);
     }
     
     private IRubyObject read18(IRubyObject[] args) {
         checkReadable();
 
         ByteList buf = null;
         int length = 0;
         int oldLength = 0;
         RubyString originalString = null;
 
         switch (args.length) {
         case 2:
             originalString = args[1].convertToString();
             // must let original string know we're modifying, so shared buffers aren't damaged
             originalString.modify();
             buf = originalString.getByteList();
         case 1:
             if (!args[0].isNil()) {
                 length = RubyNumeric.fix2int(args[0]);
                 oldLength = length;
 
                 if (length < 0) {
                     throw getRuntime().newArgumentError("negative length " + length + " given");
                 }
                 if (length > 0 && isEndOfString()) {
                     . = true;
                     if (buf != nullbuf.setRealSize(0);
                     return getRuntime().getNil();
                 } else if (.) {
                     if (buf != nullbuf.setRealSize(0);
                     return getRuntime().getNil();
                 }
                 break;
             }
         case 0:
             oldLength = -1;
             length = ..getByteList().length();
 
             if (length <= .) {
                 . = true;
                 if (buf == null) {
                     buf = new ByteList();
                 } else {
                     buf.setRealSize(0);
                 }
 
                 return makeString(getRuntime(), buf);
             } else {
                 length -= .;
             }
             break;
         default:
             getRuntime().newArgumentError(args.length, 0);
         }
 
         if (buf == null) {
             int internalLength = ..getByteList().length();
 
             if (internalLength > 0) {
                 if (internalLength >= . + length) {
                     buf = new ByteList(..getByteList(), (int.length);
                 } else {
                     int rest = (int) (..getByteList().length() - .);
 
                     if (length > restlength = rest;
                     buf = new ByteList(..getByteList(), (int.length);
                 }
             }
         } else {
             int rest = (int) (..getByteList().length() - .);
 
             if (length > restlength = rest;
 
             // Yow...this is still ugly
             byte[] target = buf.getUnsafeBytes();
             if (target.length > length) {
                 System.arraycopy(..getByteList().getUnsafeBytes(), (int.target, 0, length);
                 buf.setBegin(0);
                 buf.setRealSize(length);
             } else {
                 target = new byte[length];
                 System.arraycopy(..getByteList().getUnsafeBytes(), (int.target, 0, length);
                 buf.setBegin(0);
                 buf.setRealSize(length);
                 buf.setUnsafeBytes(target);
             }
         }
 
         if (buf == null) {
             if (!.buf = new ByteList();
             length = 0;
         } else {
             length = buf.length();
             . += length;
         }
 
         if (oldLength < 0 || oldLength > length. = true;
 
         return originalString != null ? originalString : makeString(getRuntime(), buf);
     }
 
     @JRubyMethod(name = {"readchar""readbyte"}, compat = )
     public IRubyObject readchar() {
         IRubyObject c = getc();
 
         if (c.isNil()) throw getRuntime().newEOFError();
 
         return c;
     }
 
     @JRubyMethod(name = "readline", optional = 1, writes = ., compat = )
     public IRubyObject readline18(ThreadContext contextIRubyObject[] args) {
         IRubyObject line = gets(contextargs);
 
         if (line.isNil()) throw getRuntime().newEOFError();
 
         return line;
     }
 
     @JRubyMethod(name = "readlines", optional = 1, compat = )
     public IRubyObject readlines(ThreadContext contextIRubyObject[] args) {
         checkReadable();
 
         RubyArray ary = context.runtime.newArray();
         while (!(isEOF())) {
             IRubyObject line = internalGets18(contextargs);
             if (line.isNil()) {
                 break;
             }
             ary.append(line);
         }
 
         return ary;
     }
 
     @JRubyMethod(name = "reopen", required = 0, optional = 2)
     public IRubyObject reopen(IRubyObject[] args) {
         checkFrozen();
         
         if (args.length == 1 && !(args[0] instanceof RubyString)) {
             return initialize_copy(args[0]);
         }
 
         // reset the state
         doRewind();
         . = false;
         . = false;
         return initialize(args.);
     }
 
     @JRubyMethod(name = "rewind")
     public IRubyObject rewind() {
         checkFrozen();
         
         doRewind();
         return RubyFixnum.zero(getRuntime());
     }
 
     private void doRewind() {
         this.. = 0;
         // used in 1.8 mode only
         this.. = false;
         this.. = 0;
     }
     
     @Deprecated
     public IRubyObject seek(IRubyObject[] args) {
         return seek(getRuntime().getCurrentContext(), args);
     }
 
     @JRubyMethod(required = 1, optional = 1)
     public IRubyObject seek(ThreadContext contextIRubyObject[] args) {
         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 getRuntime().newErrnoEINVALError("invalid whence");
         }
         
         if (offset < 0) throw getRuntime().newErrnoEINVALError("invalid seek value");
 
         . = offset;
         // used in 1.8 mode only
         . = false;
 
         return RubyFixnum.zero(getRuntime());
     }
 
     @JRubyMethod(name = "string=", required = 1)
     public IRubyObject set_string(IRubyObject arg) {
         checkFrozen();
         RubyString str = arg.convertToString();
         . = ModeFlags.createModeFlags(str.isFrozen() ? . : .);
         . = 0;
         . = 0;
         return . = str;
     }
 
     @JRubyMethod(name = "sync=", required = 1)
     public IRubyObject set_sync(IRubyObject args) {
         checkFrozen();
         
         return args;
     }
 
     @JRubyMethod(name = "string")
     public IRubyObject string() {
         if (. == nullreturn getRuntime().getNil();
 
         return .;
     }
 
     @JRubyMethod(name = "sync")
     public IRubyObject sync() {
         return getRuntime().getTrue();
     }
 
     @JRubyMethod(name = "sysread", optional = 2, compat = )
     public IRubyObject sysread18(IRubyObject[] args) {
         return sysreadCommon(args);
     }
     
     // only here for the fake-out class in org.jruby
     public IRubyObject sysread(IRubyObject[] args) {
         return sysread18(args);
     }
 
     private IRubyObject sysreadCommon(IRubyObject[] args) {
         IRubyObject obj = read(args);
 
         if (isEOF() && obj.isNil()) throw getRuntime().newEOFError();
 
         return obj;        
     }
 
     @JRubyMethod(name = "truncate", required = 1)
     public IRubyObject truncate(IRubyObject arg) {
         checkWritable();
 
         int len = RubyFixnum.fix2int(arg);
         if (len < 0) {
             throw getRuntime().newErrnoEINVALError("negative legnth");
         }
 
         ..modify();
         ByteList buf = ..getByteList();
         if (len < buf.length()) {
             Arrays.fill(buf.getUnsafeBytes(), lenbuf.length(), (byte) 0);
         }
         buf.length(len);
         return arg;
     }
 
     @JRubyMethod(name = "ungetc", required = 1, compat = )
     public IRubyObject ungetc(IRubyObject arg) {
         checkReadable();
 
         int c = RubyNumeric.num2int(arg);
         if (. == 0) return getRuntime().getNil();
         ungetbyteCommon(c);
         return getRuntime().getNil();
     }
 
     private void ungetbyteCommon(int c) {
         ..modify();
         .--;
         
         ByteList bytes = ..getByteList();
 
         if (isEndOfString()) bytes.length((int). + 1);
 
         if (. == -1) {
             bytes.prepend((byte)c);
             . = 0;
         } else {
             bytes.set((int.c);
         }
     }
 
     private void ungetbyteCommon(RubyString ungetBytes) {
         ByteList ungetByteList = ungetBytes.getByteList();
         int len = ungetByteList.getRealSize();
         int start = .;
         
         if (len == 0) return;
         
         ..modify();
         
         if (len > .) {
             start = 0;
         } else {
             start = . - len;
         }
         
         ByteList bytes = ..getByteList();
         
         if (isEndOfString()) bytes.length(Math.max(.len));
 
         bytes.replace(start. - startungetBytes.getByteList());
         
         . = start;
     }
 
     @JRubyMethod(name = {"write""write_nonblock""syswrite"}, required = 1)
     public IRubyObject write(ThreadContext contextIRubyObject arg) {