private void setupModes() { data.closedWrite = false; data.closedRead = false; if (data.modes.isReadOnly()) data.closedWrite = true; if (!data.modes.isReadable()) data.closedRead = true; }
@JRubyMethod(required = 1, optional = 1) @Override public IRubyObject seek(IRubyObject[] args) { checkOpen(); checkFinalized(); long amount = RubyNumeric.num2long(args[0]); int whence = Stream.SEEK_SET; long newPosition = data.pos; if (args.length > 1 && !args[0].isNil()) whence = RubyNumeric.fix2int(args[1]); if (whence == Stream.SEEK_CUR) { newPosition += amount; } else if (whence == Stream.SEEK_END) { newPosition = data.internal.getByteList().length() + amount; } else if (whence == Stream.SEEK_SET) { newPosition = amount; } else { throw getRuntime().newErrnoEINVALError("invalid whence"); } if (newPosition < 0) throw getRuntime().newErrnoEINVALError("invalid seek value"); data.pos = newPosition; data.eof = false; return RubyFixnum.zero(getRuntime()); }
@JRubyMethod(name = "string=", required = 1) public IRubyObject set_string(IRubyObject arg) { checkFrozen(); ptr.flags &= ~OpenFile.READWRITE; RubyString str = arg.convertToString(); ptr.flags = str.isFrozen() ? OpenFile.READABLE : OpenFile.READWRITE; ptr.pos = 0; ptr.lineno = 0; return ptr.string = str; }
@JRubyMethod @Override public IRubyObject close() { checkInitialized(); checkOpen(); data.closedRead = true; data.closedWrite = true; return getRuntime().getNil(); }
@JRubyMethod(name = "pos=", required = 1) @Override public IRubyObject set_pos(IRubyObject arg) { data.pos = RubyNumeric.fix2int(arg); if (data.pos < 0) throw getRuntime().newErrnoEINVALError("Invalid argument"); if (!isEndOfString()) data.eof = false; return getRuntime().getNil(); }
@JRubyMethod(name = "reopen", required = 0, optional = 2) @Override public IRubyObject reopen(IRubyObject[] args) { if (args.length == 1 && !(args[0] instanceof RubyString)) { return initialize_copy(args[0]); } // reset the state doRewind(); data.closedRead = false; data.closedWrite = false; return initialize(args, Block.NULL_BLOCK); }
private void initializeModes(Object modeArgument) { Ruby runtime = getRuntime(); if (modeArgument == null) { data.modes = RubyIO.newModeFlags(runtime, "r+"); } else if (modeArgument instanceof Long) { data.modes = RubyIO.newModeFlags(runtime, ((Long) modeArgument).longValue()); } else { data.modes = RubyIO.newModeFlags(runtime, (String) modeArgument); } setupModes(); }
@JRubyMethod(required = 1, optional = 1) public IRubyObject seek(ThreadContext context, IRubyObject[] 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 += ptr.pos; break; case 2: offset += ptr.string.size(); break; default: throw runtime.newErrnoEINVALError("invalid whence"); } if (offset < 0) throw runtime.newErrnoEINVALError("invalid seek value"); ptr.pos = offset; return RubyFixnum.zero(runtime); }
@JRubyMethod(name = "lineno=", required = 1) @Override public IRubyObject set_lineno(IRubyObject arg) { data.lineno = RubyNumeric.fix2int(arg); return getRuntime().getNil(); }
@JRubyMethod( name = {"write"}, required = 1) public IRubyObject write(ThreadContext context, IRubyObject arg) { checkWritable(); Ruby runtime = context.runtime; RubyString str = arg.asString(); int len, olen; Encoding enc, enc2; enc = ptr.string.getEncoding(); enc2 = str.getEncoding(); if (enc != enc2 && enc != EncodingUtils.ascii8bitEncoding(runtime) // this is a hack because we don't seem to handle incoming ASCII-8BIT properly in transcoder && enc2 != ASCIIEncoding.INSTANCE) { str = runtime.newString(Transcoder.strConvEnc(context, str.getByteList(), enc2, enc)); } len = str.size(); if (len == 0) return RubyFixnum.zero(runtime); checkModifiable(); olen = ptr.string.size(); if ((ptr.flags & OpenFile.APPEND) != 0) { ptr.pos = olen; } if (ptr.pos == olen // this is a hack because we don't seem to handle incoming ASCII-8BIT properly in transcoder && enc2 != ASCIIEncoding.INSTANCE) { EncodingUtils.encStrBufCat(runtime, ptr.string, str.getByteList(), enc); } else { strioExtend(ptr.pos, len); ByteList ptrByteList = ptr.string.getByteList(); System.arraycopy( str.getByteList().getUnsafeBytes(), str.getByteList().getBegin(), ptrByteList.getUnsafeBytes(), ptrByteList.begin + ptr.pos, len); ptr.string.infectBy(str); } ptr.string.infectBy(this); ptr.pos += len; return RubyFixnum.newFixnum(runtime, len); }
@JRubyMethod @Override public IRubyObject close_read() { checkReadable(); data.closedRead = true; return getRuntime().getNil(); }
@JRubyMethod @Override public IRubyObject close_write() { checkWritable(); data.closedWrite = true; return getRuntime().getNil(); }
@JRubyMethod(optional = 2, visibility = PRIVATE) @Override public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) { Object modeArgument = null; Ruby runtime = getRuntime(); switch (args.length) { case 0: data.internal = runtime.is1_9() ? RubyString.newEmptyString(runtime, runtime.getDefaultExternalEncoding()) : RubyString.newEmptyString(getRuntime()); modeArgument = "r+"; break; case 1: data.internal = args[0].convertToString(); modeArgument = data.internal.isFrozen() ? "r" : "r+"; break; case 2: data.internal = args[0].convertToString(); if (args[1] instanceof RubyFixnum) { modeArgument = RubyFixnum.fix2long(args[1]); } else { modeArgument = args[1].convertToString().toString(); } break; } initializeModes(modeArgument); if (data.modes.isWritable() && data.internal.isFrozen()) { throw getRuntime().newErrnoEACCESError("Permission denied"); } if (data.modes.isTruncate()) { data.internal.modifyCheck(); data.internal.empty(); } return this; }
@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()); ptr.pos = p; return arg; }
@JRubyMethod(name = "getc") public IRubyObject getc(ThreadContext context) { checkReadable(); if (isEndOfString()) return context.runtime.getNil(); int start = ptr.pos; int total = 1 + StringSupport.bytesToFixBrokenTrailingCharacter(ptr.string.getByteList(), start + 1); ptr.pos += total; return context.runtime.newString(ptr.string.getByteList().makeShared(start, total)); }
private void ungetbyteCommon(int c) { ptr.string.modify(); ptr.pos--; ByteList bytes = ptr.string.getByteList(); if (isEndOfString()) bytes.length((int) ptr.pos + 1); if (ptr.pos == -1) { bytes.prepend((byte) c); ptr.pos = 0; } else { bytes.set((int) ptr.pos, c); } }
private int writeInternal(ThreadContext context, IRubyObject arg) { checkWritable(); checkFrozen(); RubyString val = arg.asString(); data.internal.modify(); if (data.modes.isAppendable()) { data.internal.getByteList().append(val.getByteList()); data.pos = data.internal.getByteList().length(); } else { int left = data.internal.getByteList().length() - (int) data.pos; data.internal .getByteList() .replace((int) data.pos, Math.min(val.getByteList().length(), left), val.getByteList()); data.pos += val.getByteList().length(); } if (val.isTaint()) { data.internal.setTaint(true); } return val.getByteList().length(); }
@JRubyMethod(meta = true, rest = true) public static IRubyObject open( ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) { StringIO strio = (StringIO) ((RubyClass) recv).newInstance(context, args, Block.NULL_BLOCK); IRubyObject val = strio; if (block.isGiven()) { try { val = block.yield(context, strio); } finally { strio.ptr.string = null; strio.flags &= ~STRIO_READWRITE; } } return val; }
@JRubyMethod(name = "putc", required = 1) public IRubyObject putc(IRubyObject ch) { checkWritable(); byte c = RubyNumeric.num2chr(ch); int olen; checkModifiable(); olen = ptr.string.size(); if ((ptr.flags & OpenFile.APPEND) != 0) { ptr.pos = olen; } strioExtend(ptr.pos, 1); ptr.string.getByteList().set(ptr.pos++, c); ptr.string.infectBy(this); return ch; }
private void strioInit(ThreadContext context, IRubyObject[] 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); ptr.flags = ModeFlags.getOpenFileFlagsFor(flags); trunc = (flags & ModeFlags.TRUNC) != 0; } else { String m = args[1].convertToString().toString(); ptr.flags = OpenFile.ioModestrFmode(runtime, m); trunc = m.charAt(0) == 'w'; } string = args[0].convertToString(); if ((ptr.flags & OpenFile.WRITABLE) != 0 && string.isFrozen()) { throw runtime.newErrnoEACCESError("Permission denied"); } if (trunc) { string.resize(0); } break; case 1: string = args[0].convertToString(); ptr.flags = string.isFrozen() ? OpenFile.READABLE : OpenFile.READWRITE; break; case 0: string = RubyString.newEmptyString(runtime, runtime.getDefaultExternalEncoding()); ptr.flags = OpenFile.READWRITE; break; default: throw runtime.newArgumentError(args.length, 2); } ptr.string = string; ptr.pos = 0; ptr.lineno = 0; // funky way of shifting readwrite flags into object flags flags |= (ptr.flags & OpenFile.READWRITE) * (STRIO_READABLE / OpenFile.READABLE); }
@JRubyMethod(name = "putc", required = 1) @Override public IRubyObject putc(IRubyObject obj) { checkWritable(); byte c = RubyNumeric.num2chr(obj); checkFrozen(); data.internal.modify(); ByteList bytes = data.internal.getByteList(); if (data.modes.isAppendable()) { data.pos = bytes.length(); bytes.append(c); } else { if (isEndOfString()) bytes.length((int) data.pos + 1); bytes.set((int) data.pos, c); data.pos++; } return obj; }
@Override public IRubyObject each_charInternal(final ThreadContext context, final Block block) { checkReadable(); Ruby runtime = context.runtime; ByteList bytes = data.internal.getByteList(); int len = bytes.getRealSize(); int end = bytes.getBegin() + len; Encoding enc = runtime.is1_9() ? bytes.getEncoding() : runtime.getKCode().getEncoding(); while (data.pos < len) { int pos = (int) data.pos; int n = StringSupport.length(enc, bytes.getUnsafeBytes(), pos, end); if (len < pos + n) n = len - pos; data.pos += n; block.yield(context, data.internal.makeShared19(runtime, pos, n)); } return this; }
@JRubyMethod(name = "each_codepoint") public IRubyObject each_codepoint(ThreadContext context, Block block) { Ruby runtime = context.runtime; if (!block.isGiven()) return enumeratorize(runtime, this, "each_codepoint"); checkReadable(); Encoding enc = ptr.string.getEncoding(); byte[] unsafeBytes = ptr.string.getByteList().getUnsafeBytes(); int begin = ptr.string.getByteList().getBegin(); for (; ; ) { if (ptr.pos >= ptr.string.size()) { return this; } int c = StringSupport.codePoint(runtime, enc, unsafeBytes, begin + ptr.pos, unsafeBytes.length); int n = StringSupport.codeLength(runtime, enc, c); block.yield(context, runtime.newFixnum(c)); ptr.pos += n; } }
private void ungetbyteCommon(RubyString ungetBytes) { ByteList ungetByteList = ungetBytes.getByteList(); int len = ungetByteList.getRealSize(); int start = ptr.pos; if (len == 0) return; ptr.string.modify(); if (len > ptr.pos) { start = 0; } else { start = ptr.pos - len; } ByteList bytes = ptr.string.getByteList(); if (isEndOfString()) bytes.length(Math.max(ptr.pos, len)); bytes.replace(start, ptr.pos - start, ungetBytes.getByteList()); ptr.pos = start; }
@JRubyMethod(name = "read", optional = 2) public IRubyObject read(ThreadContext context, IRubyObject[] 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 = ptr.string.size(); if (len <= ptr.pos) { if (str.isNil()) { str = runtime.newString(); } else { ((RubyString) str).resize(0); } return str; } else { len -= ptr.pos; } break; default: throw getRuntime().newArgumentError(args.length, 0); } if (str.isNil()) { str = strioSubstr(runtime, ptr.pos, len); if (binary) ((RubyString) str).setEncoding(ASCIIEncoding.INSTANCE); } else { int rest = ptr.string.size() - ptr.pos; if (len > rest) len = rest; ((RubyString) str).resize(len); ByteList strByteList = ((RubyString) str).getByteList(); byte[] strBytes = strByteList.getUnsafeBytes(); ByteList dataByteList = ptr.string.getByteList(); byte[] dataBytes = dataByteList.getUnsafeBytes(); System.arraycopy( dataBytes, dataByteList.getBegin() + ptr.pos, strBytes, strByteList.getBegin(), len); if (binary) { ((RubyString) str).setEncoding(ASCIIEncoding.INSTANCE); } else { ((RubyString) str).setEncoding(ptr.string.getEncoding()); } } ptr.pos += ((RubyString) str).size(); return str; }
@JRubyMethod(name = "lineno=", required = 1) public IRubyObject set_lineno(ThreadContext context, IRubyObject arg) { ptr.lineno = RubyNumeric.fix2int(arg); return context.nil; }
// strio_getline private IRubyObject getline(ThreadContext context, IRubyObject[] args) { Ruby runtime = context.runtime; IRubyObject str = context.nil; ; int n, limit = -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 = ptr.string.getByteList(); byte[] dataBytes = dataByteList.getUnsafeBytes(); int begin = dataByteList.getBegin(); int s = begin + ptr.pos; int e = begin + dataByteList.getRealSize(); int p; if (limit > 0 && s + limit < e) { e = dataByteList.getEncoding().rightAdjustCharHead(dataBytes, s, s + limit, e); } if (str.isNil()) { str = strioSubstr(runtime, ptr.pos, 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(dataBytes, p, '\n', e - p); if (p != -1) { if (++p < e && dataBytes[p] == '\n') { e = p + 1; } else { e = p; } } str = strioSubstr(runtime, s - begin, e - s); } else if (n == 1) { RubyString strStr = (RubyString) str; ByteList strByteList = strStr.getByteList(); if ((p = memchr(dataBytes, s, strByteList.get(0), e - s)) != -1) { e = p + 1; } str = strioSubstr(runtime, ptr.pos, e - s); } else { if (n < e - s) { RubyString strStr = (RubyString) str; ByteList strByteList = strStr.getByteList(); byte[] strBytes = strByteList.getUnsafeBytes(); int[] skip = new int[1 << CHAR_BIT]; int pos; p = strByteList.getBegin(); bm_init_skip(skip, strBytes, p, n); if ((pos = bm_search(strBytes, p, n, dataBytes, s, e - s, skip)) >= 0) { e = s + pos + n; } } str = strioSubstr(runtime, ptr.pos, e - s); } ptr.pos = e - begin; ptr.lineno++; return str; }
@SuppressWarnings("fallthrough") @JRubyMethod(name = "read", optional = 2) @Override public IRubyObject read(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()) { data.eof = true; if (buf != null) buf.setRealSize(0); return getRuntime().getNil(); } else if (data.eof) { if (buf != null) buf.setRealSize(0); return getRuntime().getNil(); } break; } case 0: oldLength = -1; length = data.internal.getByteList().length(); if (length <= data.pos) { data.eof = true; if (buf == null) { buf = new ByteList(); } else { buf.setRealSize(0); } return makeString(getRuntime(), buf); } else { length -= data.pos; } break; default: getRuntime().newArgumentError(args.length, 0); } if (buf == null) { int internalLength = data.internal.getByteList().length(); if (internalLength > 0) { if (internalLength >= data.pos + length) { buf = new ByteList(data.internal.getByteList(), (int) data.pos, length); } else { int rest = (int) (data.internal.getByteList().length() - data.pos); if (length > rest) length = rest; buf = new ByteList(data.internal.getByteList(), (int) data.pos, length); } } } else { int rest = (int) (data.internal.getByteList().length() - data.pos); if (length > rest) length = rest; // Yow...this is still ugly byte[] target = buf.getUnsafeBytes(); if (target.length > length) { System.arraycopy( data.internal.getByteList().getUnsafeBytes(), (int) data.pos, target, 0, length); buf.setBegin(0); buf.setRealSize(length); } else { target = new byte[length]; System.arraycopy( data.internal.getByteList().getUnsafeBytes(), (int) data.pos, target, 0, length); buf.setBegin(0); buf.setRealSize(length); buf.setUnsafeBytes(target); } } if (buf == null) { if (!data.eof) buf = new ByteList(); length = 0; } else { length = buf.length(); data.pos += length; } if (oldLength < 0 || oldLength > length) data.eof = true; return originalString != null ? originalString : makeString(getRuntime(), buf); }
private void doFinalize() { data.closedRead = true; data.closedWrite = true; data.internal = null; }
private IRubyObject internalGets(ThreadContext context, IRubyObject[] args) { Ruby runtime = context.runtime; if (!isEndOfString() && !data.eof) { boolean isParagraph = false; boolean is19 = runtime.is1_9(); ByteList sep = ((RubyString) runtime.getGlobalVariables().get("$/")).getByteList(); IRubyObject sepArg; int limit = -1; if (is19) { IRubyObject limitArg = (args.length > 1 ? args[1] : (args.length > 0 && args[0] instanceof RubyFixnum ? args[0] : null)); if (limitArg != null) { limit = RubyNumeric.fix2int(limitArg); } sepArg = (args.length > 0 && !(args[0] instanceof RubyFixnum) ? args[0] : null); } else { sepArg = (args.length > 0 ? args[0] : null); } if (sepArg != null) { if (sepArg.isNil()) { int bytesAvailable = data.internal.getByteList().getRealSize() - (int) data.pos; int bytesToUse = (limit < 0 || limit >= bytesAvailable ? bytesAvailable : limit); 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; } } ByteList ss = data.internal.getByteList(); if (isParagraph) { swallowLF(ss); if (data.pos == ss.getRealSize()) { return runtime.getNil(); } } int ix = ss.indexOf(sep, (int) data.pos); ByteList add; if (-1 == ix) { ix = data.internal.getByteList().getRealSize(); add = ByteList.EMPTY_BYTELIST; } else { add = sep; } int bytes = ix - (int) data.pos; int bytesToUse = (limit < 0 || limit >= bytes ? bytes : limit); int bytesWithSep = ix - (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; int sepBytesToUse = bytesToUseWithSep - bytesToUse; line.append(add, 0, sepBytesToUse); data.pos += sepBytesToUse; if (sepBytesToUse >= add.getRealSize()) { data.lineno++; } return makeString(runtime, line); } return runtime.getNil(); }