RubyString.java

  1. /*
  2.  **** BEGIN LICENSE BLOCK *****
  3.  * Version: EPL 1.0/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Eclipse Public
  6.  * License Version 1.0 (the "License"); you may not use this file
  7.  * except in compliance with the License. You may obtain a copy of
  8.  * the License at http://www.eclipse.org/legal/epl-v10.html
  9.  *
  10.  * Software distributed under the License is distributed on an "AS
  11.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  12.  * implied. See the License for the specific language governing
  13.  * rights and limitations under the License.
  14.  *
  15.  * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  16.  * Copyright (C) 2001-2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  17.  * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  18.  * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  19.  * Copyright (C) 2002-2006 Thomas E Enebo <enebo@acm.org>
  20.  * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  21.  * Copyright (C) 2004 David Corbin <dcorbin@users.sourceforge.net>
  22.  * Copyright (C) 2005 Tim Azzopardi <tim@tigerfive.com>
  23.  * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
  24.  * Copyright (C) 2006 Ola Bini <ola@ologix.com>
  25.  * Copyright (C) 2007 Nick Sieger <nicksieger@gmail.com>
  26.  *
  27.  * Alternatively, the contents of this file may be used under the terms of
  28.  * either of the GNU General Public License Version 2 or later (the "GPL"),
  29.  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30.  * in which case the provisions of the GPL or the LGPL are applicable instead
  31.  * of those above. If you wish to allow use of your version of this file only
  32.  * under the terms of either the GPL or the LGPL, and not to allow others to
  33.  * use your version of this file under the terms of the EPL, indicate your
  34.  * decision by deleting the provisions above and replace them with the notice
  35.  * and other provisions required by the GPL or the LGPL. If you do not delete
  36.  * the provisions above, a recipient may use your version of this file under
  37.  * the terms of any one of the EPL, the GPL or the LGPL.
  38.  ***** END LICENSE BLOCK *****/
  39. package org.jruby;

  40. import org.jcodings.Encoding;
  41. import org.jcodings.EncodingDB;
  42. import org.jcodings.ascii.AsciiTables;
  43. import org.jcodings.exception.EncodingException;
  44. import org.jcodings.specific.ASCIIEncoding;
  45. import org.jcodings.specific.USASCIIEncoding;
  46. import org.jcodings.specific.UTF16BEEncoding;
  47. import org.jcodings.specific.UTF16LEEncoding;
  48. import org.jcodings.specific.UTF32BEEncoding;
  49. import org.jcodings.specific.UTF32LEEncoding;
  50. import org.jcodings.specific.UTF8Encoding;
  51. import org.jcodings.util.CaseInsensitiveBytesHash;
  52. import org.jcodings.util.IntHash;
  53. import org.joni.Matcher;
  54. import org.joni.Option;
  55. import org.joni.Regex;
  56. import org.joni.Region;
  57. import org.jruby.anno.JRubyClass;
  58. import org.jruby.anno.JRubyMethod;
  59. import org.jruby.platform.Platform;
  60. import org.jruby.runtime.Block;
  61. import org.jruby.runtime.ClassIndex;
  62. import org.jruby.runtime.Helpers;
  63. import org.jruby.runtime.ObjectAllocator;
  64. import org.jruby.runtime.ThreadContext;
  65. import org.jruby.runtime.Visibility;
  66. import org.jruby.runtime.builtin.IRubyObject;
  67. import org.jruby.runtime.encoding.EncodingCapable;
  68. import org.jruby.runtime.encoding.MarshalEncoding;
  69. import org.jruby.runtime.marshal.UnmarshalStream;
  70. import org.jruby.util.*;
  71. import org.jruby.util.io.EncodingUtils;

  72. import java.nio.charset.Charset;
  73. import java.util.Locale;

  74. import static org.jruby.RubyComparable.invcmp;
  75. import static org.jruby.RubyEnumerator.enumeratorize;
  76. import static org.jruby.RubyEnumerator.enumeratorizeWithSize;
  77. import static org.jruby.anno.FrameField.BACKREF;
  78. import static org.jruby.runtime.Helpers.invokedynamic;
  79. import static org.jruby.runtime.Visibility.PRIVATE;
  80. import static org.jruby.runtime.invokedynamic.MethodNames.OP_EQUAL;
  81. import static org.jruby.util.StringSupport.CR_7BIT;
  82. import static org.jruby.util.StringSupport.CR_BROKEN;
  83. import static org.jruby.util.StringSupport.CR_MASK;
  84. import static org.jruby.util.StringSupport.CR_UNKNOWN;
  85. import static org.jruby.util.StringSupport.CR_VALID;
  86. import static org.jruby.util.StringSupport.MBCLEN_CHARFOUND_LEN;
  87. import static org.jruby.util.StringSupport.MBCLEN_CHARFOUND_P;
  88. import static org.jruby.util.StringSupport.MBCLEN_INVALID_P;
  89. import static org.jruby.util.StringSupport.MBCLEN_NEEDMORE_P;
  90. import static org.jruby.util.StringSupport.codeLength;
  91. import static org.jruby.util.StringSupport.codePoint;
  92. import static org.jruby.util.StringSupport.codeRangeScan;
  93. import static org.jruby.util.StringSupport.searchNonAscii;
  94. import static org.jruby.util.StringSupport.strLengthWithCodeRange;
  95. import static org.jruby.util.StringSupport.toLower;
  96. import static org.jruby.util.StringSupport.toUpper;
  97. import static org.jruby.util.StringSupport.unpackArg;
  98. import static org.jruby.util.StringSupport.unpackResult;
  99. import static org.jruby.RubyEnumerator.SizeFn;

  100. /**
  101.  * Implementation of Ruby String class
  102.  *
  103.  * Concurrency: no synchronization is required among readers, but
  104.  * all users must synchronize externally with writers.
  105.  *
  106.  */
  107. @JRubyClass(name="String", include={"Enumerable", "Comparable"})
  108. public class RubyString extends RubyObject implements EncodingCapable, MarshalEncoding, CodeRangeable {

  109.     private static final ASCIIEncoding ASCII = ASCIIEncoding.INSTANCE;
  110.     private static final UTF8Encoding UTF8 = UTF8Encoding.INSTANCE;
  111.     private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];

  112.     // string doesn't share any resources
  113.     private static final int SHARE_LEVEL_NONE = 0;
  114.     // string has it's own ByteList, but it's pointing to a shared buffer (byte[])
  115.     private static final int SHARE_LEVEL_BUFFER = 1;
  116.     // string doesn't have it's own ByteList (values)
  117.     private static final int SHARE_LEVEL_BYTELIST = 2;

  118.     private static final byte[] SCRUB_REPL_UTF8 = new byte[]{(byte)0xEF, (byte)0xBF, (byte)0xBD};
  119.     private static final byte[] SCRUB_REPL_ASCII = new byte[]{(byte)'?'};
  120.     private static final byte[] SCRUB_REPL_UTF16BE = new byte[]{(byte)0xFF, (byte)0xFD};
  121.     private static final byte[] SCRUB_REPL_UTF16LE = new byte[]{(byte)0xFD, (byte)0xFF};
  122.     private static final byte[] SCRUB_REPL_UTF32BE = new byte[]{(byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFD};
  123.     private static final byte[] SCRUB_REPL_UTF32LE = new byte[]{(byte)0xFD, (byte)0xFF, (byte)0x00, (byte)0x00};

  124.     private volatile int shareLevel = SHARE_LEVEL_NONE;

  125.     private ByteList value;

  126.     private static final String[][] opTable19 = {
  127.         { "+", "+(binary)" },
  128.         { "-", "-(binary)" }
  129.     };

  130.     public static RubyClass createStringClass(Ruby runtime) {
  131.         RubyClass stringClass = runtime.defineClass("String", runtime.getObject(), STRING_ALLOCATOR);
  132.         runtime.setString(stringClass);
  133.         stringClass.setClassIndex(ClassIndex.STRING);
  134.         stringClass.setReifiedClass(RubyString.class);
  135.         stringClass.kindOf = new RubyModule.JavaClassKindOf(RubyString.class);

  136.         stringClass.includeModule(runtime.getComparable());
  137.         stringClass.defineAnnotatedMethods(RubyString.class);

  138.         return stringClass;
  139.     }

  140.     private static ObjectAllocator STRING_ALLOCATOR = new ObjectAllocator() {
  141.         @Override
  142.         public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  143.             return RubyString.newAllocatedString(runtime, klass);
  144.         }
  145.     };

  146.     @Override
  147.     public Encoding getEncoding() {
  148.         return value.getEncoding();
  149.     }

  150.     @Override
  151.     public void setEncoding(Encoding encoding) {
  152.         value.setEncoding(encoding);
  153.     }

  154.     @Override
  155.     public boolean shouldMarshalEncoding() {
  156.         return getEncoding() != ASCIIEncoding.INSTANCE;
  157.     }

  158.     @Override
  159.     public Encoding getMarshalEncoding() {
  160.         return getEncoding();
  161.     }

  162.     public void associateEncoding(Encoding enc) {
  163.         if (value.getEncoding() != enc) {
  164.             if (!isCodeRangeAsciiOnly() || !enc.isAsciiCompatible()) clearCodeRange();
  165.             value.setEncoding(enc);
  166.         }
  167.     }

  168.     public final void setEncodingAndCodeRange(Encoding enc, int cr) {
  169.         value.setEncoding(enc);
  170.         setCodeRange(cr);
  171.     }

  172.     public final Encoding toEncoding(Ruby runtime) {
  173.         return runtime.getEncodingService().findEncoding(this);
  174.     }

  175.     @Override
  176.     public final int getCodeRange() {
  177.         return flags & CR_MASK;
  178.     }

  179.     public final void setCodeRange(int codeRange) {
  180.         clearCodeRange();
  181.         flags |= codeRange & CR_MASK;
  182.     }

  183.     public final void clearCodeRange() {
  184.         flags &= ~CR_MASK;
  185.     }

  186.     private void keepCodeRange() {
  187.         if (getCodeRange() == CR_BROKEN) clearCodeRange();
  188.     }

  189.     // ENC_CODERANGE_ASCIIONLY
  190.     public final boolean isCodeRangeAsciiOnly() {
  191.         return getCodeRange() == CR_7BIT;
  192.     }

  193.     // rb_enc_str_asciionly_p
  194.     public final boolean isAsciiOnly() {
  195.         return value.getEncoding().isAsciiCompatible() && scanForCodeRange() == CR_7BIT;
  196.     }

  197.     public final boolean isCodeRangeValid() {
  198.         return (flags & CR_MASK) == CR_VALID;
  199.     }

  200.     public final boolean isCodeRangeBroken() {
  201.         return (flags & CR_MASK) == CR_BROKEN;
  202.     }

  203.     static int codeRangeAnd(int cr1, int cr2) {
  204.         if (cr1 == CR_7BIT) return cr2;
  205.         if (cr1 == CR_VALID) return cr2 == CR_7BIT ? CR_VALID : cr2;
  206.         return CR_UNKNOWN;
  207.     }

  208.     private void copyCodeRangeForSubstr(RubyString from, Encoding enc) {
  209.         int fromCr = from.getCodeRange();
  210.         if (fromCr == CR_7BIT) {
  211.             setCodeRange(fromCr);
  212.         } else if (fromCr == CR_VALID) {
  213.             if (!enc.isAsciiCompatible() || searchNonAscii(value) != -1) {
  214.                 setCodeRange(CR_VALID);
  215.             } else {
  216.                 setCodeRange(CR_7BIT);
  217.             }
  218.         } else{
  219.             if (value.getRealSize() == 0) {
  220.                 setCodeRange(!enc.isAsciiCompatible() ? CR_VALID : CR_7BIT);
  221.             }
  222.         }
  223.     }

  224.     private void copyCodeRange(RubyString from) {
  225.         value.setEncoding(from.value.getEncoding());
  226.         setCodeRange(from.getCodeRange());
  227.     }

  228.     // rb_enc_str_coderange
  229.     @Override
  230.     public final int scanForCodeRange() {
  231.         int cr = getCodeRange();
  232.         if (cr == CR_UNKNOWN) {
  233.             cr = codeRangeScan(value.getEncoding(), value);
  234.             setCodeRange(cr);
  235.         }
  236.         return cr;
  237.     }

  238.     final boolean singleByteOptimizable() {
  239.         return StringSupport.isSingleByteOptimizable(this, value.getEncoding());
  240.     }

  241.     final boolean singleByteOptimizable(Encoding enc) {
  242.         return getCodeRange() == CR_7BIT || enc.maxLength() == 1;
  243.     }

  244.     // rb_enc_compatible
  245.     private Encoding isCompatibleWith(RubyString other) {
  246.         Encoding enc1 = value.getEncoding();
  247.         Encoding enc2 = other.value.getEncoding();

  248.         if (enc1 == enc2) return enc1;

  249.         if (other.value.getRealSize() == 0) return enc1;
  250.         if (value.getRealSize() == 0) {
  251.             return (enc1.isAsciiCompatible() && other.isAsciiOnly()) ? enc1 : enc2;
  252.         }

  253.         if (!enc1.isAsciiCompatible() || !enc2.isAsciiCompatible()) return null;

  254.         return RubyEncoding.areCompatible(enc1, scanForCodeRange(), enc2, other.scanForCodeRange());
  255.     }

  256.     final Encoding isCompatibleWith(EncodingCapable other) {
  257.         if (other instanceof RubyString) return checkEncoding((RubyString)other);
  258.         Encoding enc1 = value.getEncoding();
  259.         Encoding enc2 = other.getEncoding();

  260.         if (enc1 == enc2) return enc1;
  261.         if (value.getRealSize() == 0) return enc2;
  262.         if (!enc1.isAsciiCompatible() || !enc2.isAsciiCompatible()) return null;
  263.         if (enc2 instanceof USASCIIEncoding) return enc1;
  264.         if (scanForCodeRange() == CR_7BIT) return enc2;
  265.         return null;
  266.     }

  267.     // rb_enc_check
  268.     public final Encoding checkEncoding(RubyString other) {
  269.         Encoding enc = isCompatibleWith(other);
  270.         if (enc == null) throw getRuntime().newEncodingCompatibilityError("incompatible character encodings: " +
  271.                                 value.getEncoding() + " and " + other.value.getEncoding());
  272.         return enc;
  273.     }

  274.     final Encoding checkEncoding(EncodingCapable other) {
  275.         Encoding enc = isCompatibleWith(other);
  276.         if (enc == null) throw getRuntime().newEncodingCompatibilityError("incompatible character encodings: " +
  277.                                 value.getEncoding() + " and " + other.getEncoding());
  278.         return enc;
  279.     }

  280.     private Encoding checkDummyEncoding() {
  281.         Encoding enc = value.getEncoding();
  282.         if (enc.isDummy()) throw getRuntime().newEncodingCompatibilityError(
  283.                 "incompatible encoding with this operation: " + enc);
  284.         return enc;
  285.     }

  286.     private boolean isComparableWith(RubyString other) {
  287.         ByteList otherValue = other.value;
  288.         if (value.getEncoding() == otherValue.getEncoding() ||
  289.             value.getRealSize() == 0 || otherValue.getRealSize() == 0) return true;
  290.         return isComparableViaCodeRangeWith(other);
  291.     }

  292.     private boolean isComparableViaCodeRangeWith(RubyString other) {
  293.         int cr1 = scanForCodeRange();
  294.         int cr2 = other.scanForCodeRange();

  295.         if (cr1 == CR_7BIT && (cr2 == CR_7BIT || other.value.getEncoding().isAsciiCompatible())) return true;
  296.         if (cr2 == CR_7BIT && value.getEncoding().isAsciiCompatible()) return true;
  297.         return false;
  298.     }

  299.     private int strLength(Encoding enc) {
  300.         if (singleByteOptimizable(enc)) return value.getRealSize();
  301.         return strLength(value, enc);
  302.     }

  303.     public final int strLength() {
  304.         if (singleByteOptimizable()) return value.getRealSize();
  305.         return strLength(value);
  306.     }

  307.     private int strLength(ByteList bytes) {
  308.         return strLength(bytes, bytes.getEncoding());
  309.     }

  310.     private int strLength(ByteList bytes, Encoding enc) {
  311.         if (isCodeRangeValid() && enc instanceof UTF8Encoding) return StringSupport.utf8Length(value);

  312.         long lencr = strLengthWithCodeRange(bytes, enc);
  313.         int cr = unpackArg(lencr);
  314.         if (cr != 0) setCodeRange(cr);
  315.         return unpackResult(lencr);
  316.     }

  317.     final int subLength(int pos) {
  318.         if (singleByteOptimizable() || pos < 0) return pos;
  319.         return StringSupport.strLength(value.getEncoding(), value.getUnsafeBytes(), value.getBegin(), value.getBegin() + pos);
  320.     }

  321.     /** short circuit for String key comparison
  322.      *
  323.      */
  324.     @Override
  325.     public final boolean eql(IRubyObject other) {
  326.         RubyClass metaclass = getMetaClass();
  327.         Ruby runtime = metaclass.getClassRuntime();
  328.         if (metaclass != runtime.getString() || metaclass != other.getMetaClass()) return super.eql(other);
  329.         return eql19(runtime, other);
  330.     }

  331.     // rb_str_hash_cmp
  332.     private boolean eql19(Ruby runtime, IRubyObject other) {
  333.         RubyString otherString = (RubyString)other;
  334.         return isComparableWith(otherString) && value.equal(((RubyString)other).value);
  335.     }

  336.     public RubyString(Ruby runtime, RubyClass rubyClass) {
  337.         this(runtime, rubyClass, EMPTY_BYTE_ARRAY);
  338.     }

  339.     public RubyString(Ruby runtime, RubyClass rubyClass, CharSequence value) {
  340.         this(runtime, rubyClass, value, null);
  341.     }

  342.     public RubyString(Ruby runtime, RubyClass rubyClass, CharSequence value, Encoding enc) {
  343.         super(runtime, rubyClass);
  344.         assert value != null;
  345.         if (enc == null) enc = UTF8;

  346.         this.value = encodeBytelist(value, enc);
  347.     }

  348.     public RubyString(Ruby runtime, RubyClass rubyClass, byte[] value) {
  349.         super(runtime, rubyClass);
  350.         assert value != null;
  351.         this.value = new ByteList(value);
  352.     }

  353.     public RubyString(Ruby runtime, RubyClass rubyClass, ByteList value) {
  354.         super(runtime, rubyClass);
  355.         assert value != null;
  356.         this.value = value;
  357.     }

  358.     public RubyString(Ruby runtime, RubyClass rubyClass, ByteList value, boolean objectSpace) {
  359.         super(runtime, rubyClass, objectSpace);
  360.         assert value != null;
  361.         this.value = value;
  362.     }

  363.     public RubyString(Ruby runtime, RubyClass rubyClass, ByteList value, Encoding encoding, boolean objectSpace) {
  364.         this(runtime, rubyClass, value, objectSpace);
  365.         value.setEncoding(encoding);
  366.     }

  367.     protected RubyString(Ruby runtime, RubyClass rubyClass, ByteList value, Encoding enc, int cr) {
  368.         this(runtime, rubyClass, value);
  369.         value.setEncoding(enc);
  370.         flags |= cr;
  371.     }

  372.     protected RubyString(Ruby runtime, RubyClass rubyClass, ByteList value, Encoding enc) {
  373.         this(runtime, rubyClass, value);
  374.         value.setEncoding(enc);
  375.     }

  376.     protected RubyString(Ruby runtime, RubyClass rubyClass, ByteList value, int cr) {
  377.         this(runtime, rubyClass, value);
  378.         flags |= cr;
  379.     }

  380.     // Deprecated String construction routines
  381.     /** Create a new String which uses the same Ruby runtime and the same
  382.      *  class like this String.
  383.      *
  384.      *  This method should be used to satisfy RCR #38.
  385.      *  @deprecated
  386.      */
  387.     @Deprecated
  388.     public RubyString newString(CharSequence s) {
  389.         return new RubyString(getRuntime(), getType(), s);
  390.     }

  391.     /** Create a new String which uses the same Ruby runtime and the same
  392.      *  class like this String.
  393.      *
  394.      *  This method should be used to satisfy RCR #38.
  395.      *  @deprecated
  396.      */
  397.     @Deprecated
  398.     public RubyString newString(ByteList s) {
  399.         return new RubyString(getRuntime(), getMetaClass(), s);
  400.     }

  401.     @Deprecated
  402.     public static RubyString newString(Ruby runtime, RubyClass clazz, CharSequence str) {
  403.         return new RubyString(runtime, clazz, str);
  404.     }

  405.     public static RubyString newStringLight(Ruby runtime, ByteList bytes) {
  406.         return new RubyString(runtime, runtime.getString(), bytes, false);
  407.     }

  408.     public static RubyString newStringLight(Ruby runtime, int size) {
  409.         return new RubyString(runtime, runtime.getString(), new ByteList(size), false);
  410.     }

  411.     public static RubyString newStringLight(Ruby runtime, int size, Encoding encoding) {
  412.         return new RubyString(runtime, runtime.getString(), new ByteList(size), encoding, false);
  413.     }

  414.     public static RubyString newString(Ruby runtime, CharSequence str) {
  415.         return new RubyString(runtime, runtime.getString(), str);
  416.     }

  417.     public static RubyString newString(Ruby runtime, String str) {
  418.         return new RubyString(runtime, runtime.getString(), str);
  419.     }

  420.     public static RubyString newString(Ruby runtime, String str, Encoding encoding) {
  421.         return new RubyString(runtime, runtime.getString(), str, encoding);
  422.     }

  423.     public static RubyString newUSASCIIString(Ruby runtime, String str) {
  424.         return new RubyString(runtime, runtime.getString(), str, USASCIIEncoding.INSTANCE);
  425.     }

  426.     public static RubyString newString(Ruby runtime, byte[] bytes) {
  427.         return new RubyString(runtime, runtime.getString(), bytes);
  428.     }

  429.     public static RubyString newString(Ruby runtime, byte[] bytes, int start, int length) {
  430.         return newString(runtime, bytes, start, length, ASCIIEncoding.INSTANCE);
  431.     }

  432.     // rb_enc_str_new
  433.     public static RubyString newString(Ruby runtime, byte[] bytes, int start, int length, Encoding encoding) {
  434.         byte[] copy = new byte[length];
  435.         System.arraycopy(bytes, start, copy, 0, length);
  436.         return new RubyString(runtime, runtime.getString(), new ByteList(copy, encoding, false));
  437.     }

  438.     public static RubyString newString(Ruby runtime, ByteList bytes) {
  439.         return new RubyString(runtime, runtime.getString(), bytes);
  440.     }

  441.     public static RubyString newString(Ruby runtime, ByteList bytes, Encoding encoding) {
  442.         return new RubyString(runtime, runtime.getString(), bytes, encoding);
  443.     }

  444.     public static RubyString newUnicodeString(Ruby runtime, String str) {
  445.         Encoding defaultInternal = runtime.getDefaultInternalEncoding();
  446.         if (defaultInternal == UTF16BEEncoding.INSTANCE) {
  447.             return newUTF16String(runtime, str);
  448.         } else {
  449.             return newUTF8String(runtime, str);
  450.         }
  451.     }

  452.     public static RubyString newUTF8String(Ruby runtime, String str) {
  453.         ByteList byteList = new ByteList(RubyEncoding.encodeUTF8(str), UTF8Encoding.INSTANCE, false);
  454.         return new RubyString(runtime, runtime.getString(), byteList);
  455.     }

  456.     public static RubyString newUTF16String(Ruby runtime, String str) {
  457.         ByteList byteList = new ByteList(RubyEncoding.encodeUTF16(str), UTF16BEEncoding.INSTANCE, false);
  458.         return new RubyString(runtime, runtime.getString(), byteList);
  459.     }

  460.     public static RubyString newUnicodeString(Ruby runtime, CharSequence str) {
  461.         Encoding defaultInternal = runtime.getDefaultInternalEncoding();
  462.         if (defaultInternal == UTF16BEEncoding.INSTANCE) {
  463.             return newUTF16String(runtime, str);
  464.         } else {
  465.             return newUTF8String(runtime, str);
  466.         }
  467.     }

  468.     public static RubyString newUTF8String(Ruby runtime, CharSequence str) {
  469.         ByteList byteList = new ByteList(RubyEncoding.encodeUTF8(str), UTF8Encoding.INSTANCE, false);
  470.         return new RubyString(runtime, runtime.getString(), byteList);
  471.     }

  472.     public static RubyString newUTF16String(Ruby runtime, CharSequence str) {
  473.         ByteList byteList = new ByteList(RubyEncoding.encodeUTF16(str.toString()), UTF16BEEncoding.INSTANCE, false);
  474.         return new RubyString(runtime, runtime.getString(), byteList);
  475.     }

  476.     /**
  477.      * Return a new Ruby String encoded as the default internal encoding given a Java String that
  478.      * has come from an external source. If there is no default internal encoding set, the Ruby
  479.      * String will be encoded using Java's default external encoding. If an internal encoding is
  480.      * set, that encoding will be used for the Ruby String.
  481.      *
  482.      * @param runtime
  483.      * @param str
  484.      * @return
  485.      */
  486.     public static RubyString newInternalFromJavaExternal(Ruby runtime, String str) {
  487.         // Ruby internal
  488.         Encoding internal = runtime.getDefaultInternalEncoding();
  489.         Charset rubyInt = null;
  490.         if (internal != null && internal.getCharset() != null) rubyInt = internal.getCharset();

  491.         Encoding javaExtEncoding = runtime.getEncodingService().getJavaDefault();

  492.         if (rubyInt == null) {
  493.             return RubyString.newString(
  494.                     runtime,
  495.                     new ByteList(str.getBytes(), javaExtEncoding));
  496.         } else {
  497.             return RubyString.newString(
  498.                     runtime,
  499.                     new ByteList(RubyEncoding.encode(str, rubyInt), internal));
  500.         }
  501.     }

  502.     // String construction routines by NOT byte[] buffer and making the target String shared
  503.     public static RubyString newStringShared(Ruby runtime, RubyString orig) {
  504.         orig.shareLevel = SHARE_LEVEL_BYTELIST;
  505.         RubyString str = new RubyString(runtime, runtime.getString(), orig.value);
  506.         str.shareLevel = SHARE_LEVEL_BYTELIST;
  507.         return str;
  508.     }

  509.     public static RubyString newStringShared(Ruby runtime, ByteList bytes) {
  510.         return newStringShared(runtime, runtime.getString(), bytes);
  511.     }

  512.     public static RubyString newStringShared(Ruby runtime, ByteList bytes, Encoding encoding) {
  513.         return newStringShared(runtime, runtime.getString(), bytes, encoding);
  514.     }


  515.     public static RubyString newStringShared(Ruby runtime, ByteList bytes, int codeRange) {
  516.         RubyString str = new RubyString(runtime, runtime.getString(), bytes, codeRange);
  517.         str.shareLevel = SHARE_LEVEL_BYTELIST;
  518.         return str;
  519.     }

  520.     public static RubyString newStringShared(Ruby runtime, RubyClass clazz, ByteList bytes) {
  521.         RubyString str = new RubyString(runtime, clazz, bytes);
  522.         str.shareLevel = SHARE_LEVEL_BYTELIST;
  523.         return str;
  524.     }

  525.     public static RubyString newStringShared(Ruby runtime, RubyClass clazz, ByteList bytes, Encoding encoding) {
  526.         RubyString str = new RubyString(runtime, clazz, bytes, encoding);
  527.         str.shareLevel = SHARE_LEVEL_BYTELIST;
  528.         return str;
  529.     }

  530.     public static RubyString newStringShared(Ruby runtime, byte[] bytes) {
  531.         return newStringShared(runtime, new ByteList(bytes, false));
  532.     }

  533.     public static RubyString newStringShared(Ruby runtime, byte[] bytes, Encoding encoding) {
  534.         return newStringShared(runtime, new ByteList(bytes, encoding, false));
  535.     }

  536.     public static RubyString newStringShared(Ruby runtime, byte[] bytes, int start, int length) {
  537.         return newStringShared(runtime, new ByteList(bytes, start, length, false));
  538.     }

  539.     public static RubyString newStringShared(Ruby runtime, byte[] bytes, int start, int length, Encoding encoding) {
  540.         return newStringShared(runtime, new ByteList(bytes, start, length, encoding, false));
  541.     }

  542.     public static RubyString newEmptyString(Ruby runtime) {
  543.         return newEmptyString(runtime, runtime.getString());
  544.     }

  545.     private static final ByteList EMPTY_ASCII8BIT_BYTELIST = new ByteList(new byte[0], ASCIIEncoding.INSTANCE);
  546.     private static final ByteList EMPTY_USASCII_BYTELIST = new ByteList(new byte[0], USASCIIEncoding.INSTANCE);

  547.     public static RubyString newAllocatedString(Ruby runtime, RubyClass metaClass) {
  548.         RubyString empty = new RubyString(runtime, metaClass, EMPTY_ASCII8BIT_BYTELIST);
  549.         empty.shareLevel = SHARE_LEVEL_BYTELIST;
  550.         return empty;
  551.     }

  552.     public static RubyString newEmptyString(Ruby runtime, RubyClass metaClass) {
  553.         RubyString empty = new RubyString(runtime, metaClass, EMPTY_USASCII_BYTELIST);
  554.         empty.shareLevel = SHARE_LEVEL_BYTELIST;
  555.         return empty;
  556.     }

  557.     // String construction routines by NOT byte[] buffer and NOT making the target String shared
  558.     public static RubyString newStringNoCopy(Ruby runtime, ByteList bytes) {
  559.         return newStringNoCopy(runtime, runtime.getString(), bytes);
  560.     }

  561.     public static RubyString newStringNoCopy(Ruby runtime, RubyClass clazz, ByteList bytes) {
  562.         return new RubyString(runtime, clazz, bytes);
  563.     }

  564.     public static RubyString newStringNoCopy(Ruby runtime, byte[] bytes, int start, int length) {
  565.         return newStringNoCopy(runtime, new ByteList(bytes, start, length, false));
  566.     }

  567.     public static RubyString newStringNoCopy(Ruby runtime, byte[] bytes) {
  568.         return newStringNoCopy(runtime, new ByteList(bytes, false));
  569.     }

  570.     // str_independent
  571.     public boolean independent() {
  572.         return shareLevel == SHARE_LEVEL_NONE;
  573.     }

  574.     // str_make_independent, modified to create a new String rather than possibly modifying a frozen one
  575.     public RubyString makeIndependent() {
  576.         RubyClass klass = metaClass;
  577.         RubyString str = strDup(klass.getClassRuntime(), klass);
  578.         str.modify();
  579.         str.setFrozen(true);
  580.         str.infectBy(this);
  581.         return str;
  582.     }

  583.     // MRI: EXPORT_STR macro in process.c
  584.     public RubyString export(ThreadContext context) {
  585.         if (Platform.IS_WINDOWS) {
  586.             return EncodingUtils.strConvEncOpts(context, this, null, UTF8Encoding.INSTANCE, 0, context.nil);
  587.         }
  588.         return this;
  589.     }

  590.     /** Encoding aware String construction routines for 1.9
  591.      *
  592.      */
  593.     private static final class EmptyByteListHolder {
  594.         final ByteList bytes;
  595.         final int cr;
  596.         EmptyByteListHolder(Encoding enc) {
  597.             this.bytes = new ByteList(ByteList.NULL_ARRAY, enc);
  598.             this.cr = bytes.getEncoding().isAsciiCompatible() ? CR_7BIT : CR_VALID;
  599.         }
  600.     }

  601.     private static EmptyByteListHolder EMPTY_BYTELISTS[] = new EmptyByteListHolder[4];

  602.     static EmptyByteListHolder getEmptyByteList(Encoding enc) {
  603.         if (enc == null) enc = ASCIIEncoding.INSTANCE;
  604.         int index = enc.getIndex();
  605.         EmptyByteListHolder bytes;
  606.         if (index < EMPTY_BYTELISTS.length && (bytes = EMPTY_BYTELISTS[index]) != null) {
  607.             return bytes;
  608.         }
  609.         return prepareEmptyByteList(enc);
  610.     }

  611.     private static EmptyByteListHolder prepareEmptyByteList(Encoding enc) {
  612.         if (enc == null) enc = ASCIIEncoding.INSTANCE;
  613.         int index = enc.getIndex();
  614.         if (index >= EMPTY_BYTELISTS.length) {
  615.             EmptyByteListHolder tmp[] = new EmptyByteListHolder[index + 4];
  616.             System.arraycopy(EMPTY_BYTELISTS,0, tmp, 0, EMPTY_BYTELISTS.length);
  617.             EMPTY_BYTELISTS = tmp;
  618.         }
  619.         return EMPTY_BYTELISTS[index] = new EmptyByteListHolder(enc);
  620.     }

  621.     public static RubyString newEmptyString(Ruby runtime, RubyClass metaClass, Encoding enc) {
  622.         EmptyByteListHolder holder = getEmptyByteList(enc);
  623.         RubyString empty = new RubyString(runtime, metaClass, holder.bytes, holder.cr);
  624.         empty.shareLevel = SHARE_LEVEL_BYTELIST;
  625.         return empty;
  626.     }

  627.     public static RubyString newEmptyString(Ruby runtime, Encoding enc) {
  628.         return newEmptyString(runtime, runtime.getString(), enc);
  629.     }

  630.     public static RubyString newStringNoCopy(Ruby runtime, RubyClass clazz, ByteList bytes, Encoding enc, int cr) {
  631.         return new RubyString(runtime, clazz, bytes, enc, cr);
  632.     }

  633.     public static RubyString newStringNoCopy(Ruby runtime, ByteList bytes, Encoding enc, int cr) {
  634.         return newStringNoCopy(runtime, runtime.getString(), bytes, enc, cr);
  635.     }

  636.     public static RubyString newUsAsciiStringNoCopy(Ruby runtime, ByteList bytes) {
  637.         return newStringNoCopy(runtime, bytes, USASCIIEncoding.INSTANCE, CR_7BIT);
  638.     }

  639.     public static RubyString newUsAsciiStringShared(Ruby runtime, ByteList bytes) {
  640.         RubyString str = newUsAsciiStringNoCopy(runtime, bytes);
  641.         str.shareLevel = SHARE_LEVEL_BYTELIST;
  642.         return str;
  643.     }

  644.     public static RubyString newUsAsciiStringShared(Ruby runtime, byte[] bytes, int start, int length) {
  645.         byte[] copy = new byte[length];
  646.         System.arraycopy(bytes, start, copy, 0, length);
  647.         return newUsAsciiStringShared(runtime, new ByteList(copy, false));
  648.     }

  649.     @Override
  650.     public ClassIndex getNativeClassIndex() {
  651.         return ClassIndex.STRING;
  652.     }

  653.     @Override
  654.     public Class getJavaClass() {
  655.         return String.class;
  656.     }

  657.     @Override
  658.     public RubyString convertToString() {
  659.         return this;
  660.     }

  661.     @Override
  662.     public String toString() {
  663.         return decodeString();
  664.     }

  665.     /**
  666.      * Convert this Ruby string to a Java String. This version is encoding-aware.
  667.      *
  668.      * @return A decoded Java String, based on this Ruby string's encoding.
  669.      */
  670.     public String decodeString() {
  671.         return Helpers.decodeByteList(getRuntime(), value);
  672.     }

  673.     /**
  674.      * Overridden dup for fast-path logic.
  675.      *
  676.      * @return A new RubyString sharing the original backing store.
  677.      */
  678.     @Override
  679.     public IRubyObject dup() {
  680.         RubyClass mc = metaClass.getRealClass();
  681.         if (mc.getClassIndex() != ClassIndex.STRING) return super.dup();

  682.         return strDup(mc.getClassRuntime(), mc.getRealClass());
  683.     }

  684.     // rb_str_new_frozen or rb_str_dup_frozen
  685.     public IRubyObject dupFrozen() {
  686.         RubyString dup = (RubyString)dup();
  687.         dup.setFrozen(true);
  688.         return dup;
  689.     }

  690.     // MRI: rb_str_dup
  691.     public final RubyString strDup(Ruby runtime) {
  692.         return strDup(runtime, getMetaClass().getRealClass());
  693.     }

  694.     final RubyString strDup(Ruby runtime, RubyClass clazz) {
  695.         shareLevel = SHARE_LEVEL_BYTELIST;
  696.         RubyString dup = new RubyString(runtime, clazz, value);
  697.         dup.shareLevel = SHARE_LEVEL_BYTELIST;
  698.         dup.flags |= flags & (CR_MASK | TAINTED_F);

  699.         return dup;
  700.     }

  701.     /* rb_str_subseq */
  702.     public final RubyString makeSharedString(Ruby runtime, int index, int len) {
  703.         return makeShared19(runtime, runtime.getString(), index, len);
  704.     }

  705.     public RubyString makeSharedString19(Ruby runtime, int index, int len) {
  706.         return makeShared19(runtime, runtime.getString(), value, index, len);
  707.     }

  708.     public final RubyString makeShared(Ruby runtime, int index, int len) {
  709.         return makeShared19(runtime, getType(), index, len);
  710.     }

  711.     public final RubyString makeShared(Ruby runtime, RubyClass meta, int index, int len) {
  712.         final RubyString shared;
  713.         if (len == 0) {
  714.             shared = newEmptyString(runtime, meta);
  715.         } else if (len == 1) {
  716.             shared = newStringShared(runtime, meta,
  717.                     RubyInteger.SINGLE_CHAR_BYTELISTS[value.getUnsafeBytes()[value.getBegin() + index] & 0xff]);
  718.         } else {
  719.             if (shareLevel == SHARE_LEVEL_NONE) shareLevel = SHARE_LEVEL_BUFFER;
  720.             shared = new RubyString(runtime, meta, value.makeShared(index, len));
  721.             shared.shareLevel = SHARE_LEVEL_BUFFER;
  722.         }

  723.         shared.infectBy(this);
  724.         return shared;
  725.     }

  726.     public final RubyString makeShared19(Ruby runtime, int index, int len) {
  727.         return makeShared19(runtime, value, index, len);
  728.     }

  729.     public final RubyString makeShared19(Ruby runtime, RubyClass meta, int index, int len) {
  730.         return makeShared19(runtime, meta, value, index, len);
  731.     }

  732.     private RubyString makeShared19(Ruby runtime, ByteList value, int index, int len) {
  733.         return makeShared19(runtime, getType(), value, index, len);
  734.     }

  735.     private RubyString makeShared19(Ruby runtime, RubyClass meta, ByteList value, int index, int len) {
  736.         final RubyString shared;
  737.         Encoding enc = value.getEncoding();

  738.         if (len == 0) {
  739.             shared = newEmptyString(runtime, meta, enc);
  740.         } else if (len == 1) {
  741.             // as with the 1.8 makeShared, don't bother sharing for substrings that are a single byte
  742.             // to get a good speed boost in a number of common scenarios (note though that unlike 1.8,
  743.             // we can't take advantage of SINGLE_CHAR_BYTELISTS since our encoding may not be ascii, but the
  744.             // single byte copy is pretty much negligible)
  745.             shared = newStringShared(runtime,
  746.                                      meta,
  747.                                      new ByteList(new byte[] { (byte) value.get(index) }, enc),
  748.                                      enc);
  749.         } else {
  750.             if (shareLevel == SHARE_LEVEL_NONE) shareLevel = SHARE_LEVEL_BUFFER;
  751.             shared = new RubyString(runtime, meta, value.makeShared(index, len));
  752.             shared.shareLevel = SHARE_LEVEL_BUFFER;
  753.         }
  754.         shared.copyCodeRangeForSubstr(this, enc); // no need to assign encoding, same bytelist shared
  755.         shared.infectBy(this);
  756.         return shared;
  757.     }

  758.     public final void setByteListShared() {
  759.         if (shareLevel != SHARE_LEVEL_BYTELIST) shareLevel = SHARE_LEVEL_BYTELIST;
  760.     }

  761.     /**
  762.      * Check that the string can be modified, raising error otherwise.
  763.      *
  764.      * If you plan to modify a string with shared backing store, this
  765.      * method is not sufficient; you will need to call modify() instead.
  766.      */
  767.     public final void modifyCheck() {
  768.         frozenCheck();
  769.     }

  770.     private void modifyCheck(byte[] b, int len) {
  771.         if (value.getUnsafeBytes() != b || value.getRealSize() != len) throw getRuntime().newRuntimeError("string modified");
  772.     }

  773.     private void modifyCheck(byte[] b, int len, Encoding enc) {
  774.         if (value.getUnsafeBytes() != b || value.getRealSize() != len || value.getEncoding() != enc) throw getRuntime().newRuntimeError("string modified");
  775.     }

  776.     private void frozenCheck() {
  777.         frozenCheck(false);
  778.     }

  779.     private void frozenCheck(boolean runtimeError) {
  780.         if (isFrozen()) throw getRuntime().newFrozenError("string", runtimeError);
  781.     }

  782.     /** rb_str_modify
  783.      *
  784.      */
  785.     public final void modify() {
  786.         modifyCheck();

  787.         if (shareLevel != SHARE_LEVEL_NONE) {
  788.             if (shareLevel == SHARE_LEVEL_BYTELIST) {
  789.                 value = value.dup();
  790.             } else {
  791.                 value.unshare();
  792.             }
  793.             shareLevel = SHARE_LEVEL_NONE;
  794.         }

  795.         value.invalidate();
  796.     }

  797.     public final void modify19() {
  798.         modify();
  799.         clearCodeRange();
  800.     }

  801.     private void modifyAndKeepCodeRange() {
  802.         modify();
  803.         keepCodeRange();
  804.     }

  805.     /** rb_str_modify (with length bytes ensured)
  806.      *
  807.      */
  808.     public final void modify(int length) {
  809.         modifyCheck();

  810.         if (shareLevel != SHARE_LEVEL_NONE) {
  811.             if (shareLevel == SHARE_LEVEL_BYTELIST) {
  812.                 value = value.dup(length);
  813.             } else {
  814.                 value.unshare(length);
  815.             }
  816.             shareLevel = SHARE_LEVEL_NONE;
  817.         } else {
  818.             value.ensure(length);
  819.         }

  820.         value.invalidate();
  821.     }

  822.     /**
  823.      * rb_str_modify_expand
  824.      */
  825.     public final void modifyExpand(int length) {
  826.         modify(length);
  827.         clearCodeRange();
  828.     }

  829.     // io_set_read_length
  830.     public void setReadLength(int length) {
  831.         if (size() != length) {
  832.             modify();
  833.             value.setRealSize(length);
  834.         }
  835.     }

  836.     // rb_str_new_frozen, at least in spirit
  837.     public RubyString newFrozen() {
  838.         RubyClass klass;
  839.         RubyString str = this;

  840.         if (isFrozen()) return this;
  841.         klass = getMetaClass();
  842.         str = strDup(klass.getClassRuntime());
  843.         str.setCodeRange(getCodeRange());
  844.         str.modify();
  845.         str.setFrozen(true);
  846.         return str;
  847.     }

  848.     /** rb_str_resize
  849.      */
  850.     public final void resize(int length) {
  851.         modify();
  852.         if (value.getRealSize() > length) {
  853.             value.setRealSize(length);
  854.         } else if (value.length() < length) {
  855.             value.length(length);
  856.         }
  857.     }

  858.     public final void view(ByteList bytes) {
  859.         modifyCheck();

  860.         value = bytes;
  861.         shareLevel = SHARE_LEVEL_NONE;
  862.     }

  863.     private void view(byte[]bytes) {
  864.         modifyCheck();

  865.         value = new ByteList(bytes);
  866.         shareLevel = SHARE_LEVEL_NONE;

  867.         value.invalidate();
  868.     }

  869.     private void view(int index, int len) {
  870.         modifyCheck();

  871.         if (shareLevel != SHARE_LEVEL_NONE) {
  872.             if (shareLevel == SHARE_LEVEL_BYTELIST) {
  873.                 // if len == 0 then shared empty
  874.                 value = value.makeShared(index, len);
  875.                 shareLevel = SHARE_LEVEL_BUFFER;
  876.             } else {
  877.                 value.view(index, len);
  878.             }
  879.         } else {
  880.             value.view(index, len);
  881.             // FIXME this below is temporary, but its much safer for COW (it prevents not shared Strings with begin != 0)
  882.             // this allows now e.g.: ByteList#set not to be begin aware
  883.             shareLevel = SHARE_LEVEL_BUFFER;
  884.         }

  885.         value.invalidate();
  886.     }

  887.     public static String bytesToString(byte[] bytes, int beg, int len) {
  888.         return new String(ByteList.plain(bytes, beg, len));
  889.     }

  890.     public static String byteListToString(ByteList bytes) {
  891.         return bytesToString(bytes.getUnsafeBytes(), bytes.begin(), bytes.length());
  892.     }

  893.     public static String bytesToString(byte[] bytes) {
  894.         return bytesToString(bytes, 0, bytes.length);
  895.     }

  896.     public static byte[] stringToBytes(String string) {
  897.         return ByteList.plain(string);
  898.     }

  899.     @Override
  900.     public RubyString asString() {
  901.         return this;
  902.     }

  903.     @Override
  904.     public IRubyObject checkStringType() {
  905.         return this;
  906.     }

  907.     @Override
  908.     public IRubyObject checkStringType19() {
  909.         return this;
  910.     }

  911.     @JRubyMethod(meta = true)
  912.     public static IRubyObject try_convert(ThreadContext context, IRubyObject recv, IRubyObject str) {
  913.         return str.checkStringType();
  914.     }

  915.     @JRubyMethod(name = {"to_s", "to_str"})
  916.     @Override
  917.     public IRubyObject to_s() {
  918.         Ruby runtime = getRuntime();
  919.         if (getMetaClass().getRealClass() != runtime.getString()) {
  920.             return strDup(runtime, runtime.getString());
  921.         }
  922.         return this;
  923.     }

  924.     @Override
  925.     public final int compareTo(IRubyObject other) {
  926.         return (int)op_cmp(getRuntime().getCurrentContext(), other).convertToInteger().getLongValue();
  927.     }

  928.     /* rb_str_cmp_m */
  929.     @JRubyMethod(name = "<=>")
  930.     @Override
  931.     public IRubyObject op_cmp(ThreadContext context, IRubyObject other) {
  932.         Ruby runtime = context.runtime;
  933.         if (other instanceof RubyString) {
  934.             return runtime.newFixnum(op_cmp((RubyString)other));
  935.         }
  936.         if (other.respondsTo("to_str")) {
  937.             IRubyObject tmp = other.callMethod(context, "to_str");
  938.             if (tmp instanceof RubyString)
  939.               return runtime.newFixnum(op_cmp((RubyString)tmp));
  940.         } else {
  941.             return invcmp(context, this, other);
  942.         }
  943.         return runtime.getNil();
  944.     }

  945.     /** rb_str_equal
  946.      *
  947.      */
  948.     @Override
  949.     public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
  950.         return op_equal19(context, other);
  951.     }

  952.     @JRubyMethod(name = {"==", "==="})
  953.     public IRubyObject op_equal19(ThreadContext context, IRubyObject other) {
  954.         Ruby runtime = context.runtime;
  955.         if (this == other) return runtime.getTrue();
  956.         if (other instanceof RubyString) {
  957.             RubyString otherString = (RubyString)other;
  958.             return isComparableWith(otherString) && value.equal(otherString.value) ? runtime.getTrue() : runtime.getFalse();
  959.         }
  960.         return op_equalCommon(context, other);
  961.     }

  962.     private IRubyObject op_equalCommon(ThreadContext context, IRubyObject other) {
  963.         Ruby runtime = context.runtime;
  964.         if (!other.respondsTo("to_str")) return runtime.getFalse();
  965.         return invokedynamic(context, other, OP_EQUAL, this).isTrue() ? runtime.getTrue() : runtime.getFalse();
  966.     }

  967.     public IRubyObject op_plus(ThreadContext context, IRubyObject _str) {
  968.         return op_plus19(context, _str);
  969.     }

  970.     @JRubyMethod(name = "+", required = 1)
  971.     public IRubyObject op_plus19(ThreadContext context, IRubyObject _str) {
  972.         RubyString str = _str.convertToString();
  973.         Encoding enc = checkEncoding(str);
  974.         RubyString resultStr = newStringNoCopy(context.runtime, addByteLists(value, str.value),
  975.                                     enc, codeRangeAnd(getCodeRange(), str.getCodeRange()));
  976.         resultStr.infectBy(flags | str.flags);
  977.         return resultStr;
  978.     }

  979.     private ByteList addByteLists(ByteList value1, ByteList value2) {
  980.         ByteList result = new ByteList(value1.getRealSize() + value2.getRealSize());
  981.         result.setRealSize(value1.getRealSize() + value2.getRealSize());
  982.         System.arraycopy(value1.getUnsafeBytes(), value1.getBegin(), result.getUnsafeBytes(), 0, value1.getRealSize());
  983.         System.arraycopy(value2.getUnsafeBytes(), value2.getBegin(), result.getUnsafeBytes(), value1.getRealSize(), value2.getRealSize());
  984.         return result;
  985.     }

  986.     public IRubyObject op_mul(ThreadContext context, IRubyObject other) {
  987.         return op_mul19(context, other);
  988.     }

  989.     @JRubyMethod(name = "*", required = 1)
  990.     public IRubyObject op_mul19(ThreadContext context, IRubyObject other) {
  991.         RubyString result = multiplyByteList(context, other);
  992.         result.value.setEncoding(value.getEncoding());
  993.         result.copyCodeRange(this);
  994.         return result;
  995.     }

  996.     private RubyString multiplyByteList(ThreadContext context, IRubyObject arg) {
  997.         int len = RubyNumeric.num2int(arg);
  998.         if (len < 0) throw context.runtime.newArgumentError("negative argument");

  999.         // we limit to int because ByteBuffer can only allocate int sizes
  1000.         if (len > 0 && Integer.MAX_VALUE / len < value.getRealSize()) {
  1001.             throw context.runtime.newArgumentError("argument too big");
  1002.         }

  1003.         ByteList bytes = new ByteList(len *= value.getRealSize());
  1004.         if (len > 0) {
  1005.             bytes.setRealSize(len);
  1006.             int n = value.getRealSize();
  1007.             System.arraycopy(value.getUnsafeBytes(), value.getBegin(), bytes.getUnsafeBytes(), 0, n);
  1008.             while (n <= len >> 1) {
  1009.                 System.arraycopy(bytes.getUnsafeBytes(), 0, bytes.getUnsafeBytes(), n, n);
  1010.                 n <<= 1;
  1011.             }
  1012.             System.arraycopy(bytes.getUnsafeBytes(), 0, bytes.getUnsafeBytes(), n, len - n);
  1013.         }
  1014.         RubyString result = new RubyString(context.runtime, getMetaClass(), bytes);
  1015.         result.infectBy(this);
  1016.         return result;
  1017.     }

  1018.     @JRubyMethod(name = "%", required = 1)
  1019.     public IRubyObject op_format(ThreadContext context, IRubyObject arg) {
  1020.         return opFormatCommon(context, arg);
  1021.     }

  1022.     private IRubyObject opFormatCommon(ThreadContext context, IRubyObject arg) {
  1023.         IRubyObject tmp;
  1024.         if (arg instanceof RubyHash) {
  1025.             tmp = arg;
  1026.         } else {
  1027.             tmp = arg.checkArrayType();
  1028.             if (tmp.isNil()) tmp = arg;
  1029.         }

  1030.         ByteList out = new ByteList(value.getRealSize());
  1031.         out.setEncoding(value.getEncoding());

  1032.         boolean tainted;

  1033.         // FIXME: Should we make this work with platform's locale,
  1034.         // or continue hardcoding US?
  1035.         tainted = Sprintf.sprintf1_9(out, Locale.US, value, tmp);

  1036.         RubyString str = newString(context.runtime, out);

  1037.         str.setTaint(tainted || isTaint());
  1038.         return str;
  1039.     }

  1040.     @JRubyMethod
  1041.     @Override
  1042.     public RubyFixnum hash() {
  1043.         Ruby runtime = getRuntime();
  1044.         return RubyFixnum.newFixnum(runtime, strHashCode(runtime));
  1045.     }

  1046.     @Override
  1047.     public int hashCode() {
  1048.         return strHashCode(getRuntime());
  1049.     }

  1050.     /**
  1051.      * Generate a hash for the String, using its associated Ruby instance's hash seed.
  1052.      *
  1053.      * @param runtime
  1054.      * @return
  1055.      */
  1056.     public int strHashCode(Ruby runtime) {
  1057.         long hash = runtime.isSiphashEnabled() ? SipHashInline.hash24(runtime.getHashSeedK0(),
  1058.                 runtime.getHashSeedK1(), value.getUnsafeBytes(), value.getBegin(),
  1059.                 value.getRealSize()) : PerlHash.hash(runtime.getHashSeedK0(),
  1060.                 value.getUnsafeBytes(), value.getBegin(), value.getRealSize());
  1061.         hash ^= (value.getEncoding().isAsciiCompatible() && scanForCodeRange() == CR_7BIT ? 0
  1062.                 : value.getEncoding().getIndex());
  1063.         return (int) hash;
  1064.     }

  1065.     /**
  1066.      * Generate a hash for the String, without a seed.
  1067.      *
  1068.      * @param runtime
  1069.      * @return
  1070.      */
  1071.     public int unseededStrHashCode(Ruby runtime) {
  1072.         long hash = runtime.isSiphashEnabled() ? SipHashInline.hash24(0, 0, value.getUnsafeBytes(),
  1073.                 value.getBegin(), value.getRealSize()) : PerlHash.hash(0, value.getUnsafeBytes(),
  1074.                 value.getBegin(), value.getRealSize());
  1075.         hash ^= (value.getEncoding().isAsciiCompatible() && scanForCodeRange() == CR_7BIT ? 0
  1076.                 : value.getEncoding().getIndex());
  1077.         return (int) hash;
  1078.     }

  1079.     @Override
  1080.     public boolean equals(Object other) {
  1081.         if (this == other) return true;

  1082.         if (other instanceof RubyString) {
  1083.             if (((RubyString) other).value.equal(value)) return true;
  1084.         }

  1085.         return false;
  1086.     }

  1087.     /** rb_obj_as_string
  1088.      *
  1089.      */
  1090.     public static RubyString objAsString(ThreadContext context, IRubyObject obj) {
  1091.         if (obj instanceof RubyString) return (RubyString) obj;
  1092.         IRubyObject str = obj.callMethod(context, "to_s");
  1093.         if (!(str instanceof RubyString)) return (RubyString) obj.anyToString();
  1094.         if (obj.isTaint()) str.setTaint(true);
  1095.         return (RubyString) str;
  1096.     }

  1097.     /** rb_str_cmp
  1098.      *
  1099.      */
  1100.     public final int op_cmp(RubyString other) {
  1101.         int ret = value.cmp(other.value);
  1102.         if (ret == 0 && !isComparableWith(other)) {
  1103.             return value.getEncoding().getIndex() > other.value.getEncoding().getIndex() ? 1 : -1;
  1104.         }
  1105.         return ret;
  1106.     }

  1107.     /** rb_to_id
  1108.      *
  1109.      */
  1110.     @Override
  1111.     public String asJavaString() {
  1112.         return toString();
  1113.     }

  1114.     public IRubyObject doClone(){
  1115.         return newString(getRuntime(), value.dup());
  1116.     }

  1117.     public final RubyString cat(byte[] str) {
  1118.         modify(value.getRealSize() + str.length);
  1119.         System.arraycopy(str, 0, value.getUnsafeBytes(), value.getBegin() + value.getRealSize(), str.length);
  1120.         value.setRealSize(value.getRealSize() + str.length);
  1121.         return this;
  1122.     }

  1123.     public final RubyString cat(byte[] str, int beg, int len) {
  1124.         modify(value.getRealSize() + len);
  1125.         System.arraycopy(str, beg, value.getUnsafeBytes(), value.getBegin() + value.getRealSize(), len);
  1126.         value.setRealSize(value.getRealSize() + len);
  1127.         return this;
  1128.     }

  1129.     // // rb_str_buf_append against VALUE
  1130.     public final RubyString cat19(RubyString str2) {
  1131.         int str2_cr = cat19(str2.getByteList(), str2.getCodeRange());

  1132.         infectBy(str2);
  1133.         str2.setCodeRange(str2_cr);

  1134.         return this;
  1135.     }

  1136.     // rb_str_buf_append against ptr
  1137.     public final int cat19(ByteList other, int codeRange) {
  1138.         int[] ptr_cr_ret = {codeRange};
  1139.         EncodingUtils.encCrStrBufCat(getRuntime(), this, other, other.getEncoding(), codeRange, ptr_cr_ret);
  1140.         return ptr_cr_ret[0];
  1141.     }

  1142.     public final RubyString cat(RubyString str) {
  1143.         return cat(str.getByteList());
  1144.     }

  1145.     public final RubyString cat(ByteList str) {
  1146.         modify(value.getRealSize() + str.getRealSize());
  1147.         System.arraycopy(str.getUnsafeBytes(), str.getBegin(), value.getUnsafeBytes(), value.getBegin() + value.getRealSize(), str.getRealSize());
  1148.         value.setRealSize(value.getRealSize() + str.getRealSize());
  1149.         return this;
  1150.     }

  1151.     public final RubyString cat(byte ch) {
  1152.         modify(value.getRealSize() + 1);
  1153.         value.getUnsafeBytes()[value.getBegin() + value.getRealSize()] = ch;
  1154.         value.setRealSize(value.getRealSize() + 1);
  1155.         return this;
  1156.     }

  1157.     public final RubyString cat(int ch) {
  1158.         return cat((byte)ch);
  1159.     }

  1160.     public final RubyString cat(int code, Encoding enc) {
  1161.         int n = codeLength(getRuntime(), enc, code);
  1162.         modify(value.getRealSize() + n);
  1163.         enc.codeToMbc(code, value.getUnsafeBytes(), value.getBegin() + value.getRealSize());
  1164.         value.setRealSize(value.getRealSize() + n);
  1165.         return this;
  1166.     }

  1167.     // rb_enc_str_buf_cat
  1168.     public final int cat(byte[]bytes, int p, int len, Encoding enc) {
  1169.         int[] ptr_cr_ret = {CR_UNKNOWN};
  1170.         EncodingUtils.encCrStrBufCat(getRuntime(), this, new ByteList(bytes, p, len), enc, CR_UNKNOWN, ptr_cr_ret);
  1171.         return ptr_cr_ret[0];
  1172.     }

  1173.     // rb_str_buf_cat_ascii
  1174.     public final RubyString catAscii(byte[]bytes, int ptr, int ptrLen) {
  1175.         Encoding enc = value.getEncoding();
  1176.         if (enc.isAsciiCompatible()) {
  1177.             EncodingUtils.encCrStrBufCat(getRuntime(), this, new ByteList(bytes, ptr, ptrLen), enc, CR_7BIT, null);
  1178.         } else {
  1179.             byte buf[] = new byte[enc.maxLength()];
  1180.             int end = ptr + ptrLen;
  1181.             while (ptr < end) {
  1182.                 int c = bytes[ptr];
  1183.                 int len = codeLength(getRuntime(), enc, c);
  1184.                 EncodingUtils.encMbcput(c, buf, 0, enc);
  1185.                 EncodingUtils.encCrStrBufCat(getRuntime(), this, buf, 0, len, enc, CR_VALID, null);
  1186.                 ptr++;
  1187.             }
  1188.         }
  1189.         return this;
  1190.     }

  1191.     /** rb_str_replace_m
  1192.      *
  1193.      */
  1194.     public IRubyObject replace(IRubyObject other) {
  1195.         return replace19(other);
  1196.     }

  1197.     @JRubyMethod(name = "initialize_copy", required = 1, visibility = Visibility.PRIVATE)
  1198.     @Override
  1199.     public RubyString initialize_copy(IRubyObject other) {
  1200.         return replace19(other);
  1201.     }

  1202.     @JRubyMethod(name = "replace", required = 1)
  1203.     public RubyString replace19(IRubyObject other) {
  1204.         modifyCheck();
  1205.         if (this == other) return this;
  1206.         setCodeRange(replaceCommon(other).getCodeRange()); // encoding doesn't have to be copied.
  1207.         return this;
  1208.     }

  1209.     private RubyString replaceCommon(IRubyObject other) {
  1210.         modifyCheck();
  1211.         RubyString otherStr = other.convertToString();
  1212.         otherStr.shareLevel = shareLevel = SHARE_LEVEL_BYTELIST;
  1213.         value = otherStr.value;
  1214.         infectBy(otherStr);
  1215.         return otherStr;
  1216.     }

  1217.     @JRubyMethod
  1218.     public RubyString clear() {
  1219.         modifyCheck();
  1220.         Encoding enc = value.getEncoding();

  1221.         EmptyByteListHolder holder = getEmptyByteList(enc);
  1222.         value = holder.bytes;
  1223.         shareLevel = SHARE_LEVEL_BYTELIST;
  1224.         setCodeRange(holder.cr);
  1225.         return this;
  1226.     }

  1227.     public IRubyObject reverse(ThreadContext context) {
  1228.         return reverse19(context);
  1229.     }

  1230.     @JRubyMethod(name = "reverse")
  1231.     public IRubyObject reverse19(ThreadContext context) {
  1232.         Ruby runtime = context.runtime;
  1233.         if (value.getRealSize() <= 1) return strDup(context.runtime);

  1234.         byte[]bytes = value.getUnsafeBytes();
  1235.         int p = value.getBegin();
  1236.         int len = value.getRealSize();
  1237.         byte[]obytes = new byte[len];

  1238.         boolean single = true;
  1239.         Encoding enc = value.getEncoding();
  1240.         // this really needs to be inlined here
  1241.         if (singleByteOptimizable(enc)) {
  1242.             for (int i = 0; i <= len >> 1; i++) {
  1243.                 obytes[i] = bytes[p + len - i - 1];
  1244.                 obytes[len - i - 1] = bytes[p + i];
  1245.             }
  1246.         } else {
  1247.             int end = p + len;
  1248.             int op = len;
  1249.             while (p < end) {
  1250.                 int cl = StringSupport.length(enc, bytes, p, end);
  1251.                 if (cl > 1 || (bytes[p] & 0x80) != 0) {
  1252.                     single = false;
  1253.                     op -= cl;
  1254.                     System.arraycopy(bytes, p, obytes, op, cl);
  1255.                     p += cl;
  1256.                 } else {
  1257.                     obytes[--op] = bytes[p++];
  1258.                 }
  1259.             }
  1260.         }

  1261.         RubyString result = new RubyString(runtime, getMetaClass(), new ByteList(obytes, false));

  1262.         if (getCodeRange() == CR_UNKNOWN) setCodeRange(single ? CR_7BIT : CR_VALID);
  1263.         Encoding encoding = value.getEncoding();
  1264.         result.value.setEncoding(encoding);
  1265.         result.copyCodeRangeForSubstr(this, encoding);
  1266.         return result.infectBy(this);
  1267.     }

  1268.     public RubyString reverse_bang(ThreadContext context) {
  1269.         return reverse_bang19(context);
  1270.     }

  1271.     @JRubyMethod(name = "reverse!")
  1272.     public RubyString reverse_bang19(ThreadContext context) {
  1273.         modifyCheck();
  1274.         if (value.getRealSize() > 1) {
  1275.             modifyAndKeepCodeRange();
  1276.             byte[]bytes = value.getUnsafeBytes();
  1277.             int p = value.getBegin();
  1278.             int len = value.getRealSize();

  1279.             Encoding enc = value.getEncoding();
  1280.             // this really needs to be inlined here
  1281.             if (singleByteOptimizable(enc)) {
  1282.                 for (int i = 0; i < len >> 1; i++) {
  1283.                     byte b = bytes[p + i];
  1284.                     bytes[p + i] = bytes[p + len - i - 1];
  1285.                     bytes[p + len - i - 1] = b;
  1286.                 }
  1287.             } else {
  1288.                 int end = p + len;
  1289.                 int op = len;
  1290.                 byte[]obytes = new byte[len];
  1291.                 boolean single = true;
  1292.                 while (p < end) {
  1293.                     int cl = StringSupport.length(enc, bytes, p, end);
  1294.                     if (cl > 1 || (bytes[p] & 0x80) != 0) {
  1295.                         single = false;
  1296.                         op -= cl;
  1297.                         System.arraycopy(bytes, p, obytes, op, cl);
  1298.                         p += cl;
  1299.                     } else {
  1300.                         obytes[--op] = bytes[p++];
  1301.                     }
  1302.                 }
  1303.                 value.setUnsafeBytes(obytes);
  1304.                 if (getCodeRange() == CR_UNKNOWN) setCodeRange(single ? CR_7BIT : CR_VALID);
  1305.             }
  1306.         }
  1307.         return this;
  1308.     }

  1309.     /** rb_str_s_new
  1310.      *
  1311.      */
  1312.     public static RubyString newInstance(IRubyObject recv, IRubyObject[] args, Block block) {
  1313.         RubyString newString = newStringShared(recv.getRuntime(), ByteList.EMPTY_BYTELIST);
  1314.         newString.setMetaClass((RubyClass) recv);
  1315.         newString.callInit(args, block);
  1316.         return newString;
  1317.     }

  1318.     @Override
  1319.     public IRubyObject initialize(ThreadContext context) {
  1320.         return initialize19(context);
  1321.     }

  1322.     public IRubyObject initialize(ThreadContext context, IRubyObject arg0) {
  1323.         return initialize19(context, arg0);
  1324.     }

  1325.     @JRubyMethod(name = "initialize", visibility = PRIVATE)
  1326.     @Override
  1327.     public IRubyObject initialize19(ThreadContext context) {
  1328.         return this;
  1329.     }

  1330.     @JRubyMethod(name = "initialize", visibility = PRIVATE)
  1331.     public IRubyObject initialize19(ThreadContext context, IRubyObject arg0) {
  1332.         replace19(arg0);
  1333.         return this;
  1334.     }

  1335.     public IRubyObject casecmp(ThreadContext context, IRubyObject other) {
  1336.         return casecmp19(context, other);
  1337.     }

  1338.     @JRubyMethod(name = "casecmp")
  1339.     public IRubyObject casecmp19(ThreadContext context, IRubyObject other) {
  1340.         Ruby runtime = context.runtime;
  1341.         RubyString otherStr = other.convertToString();
  1342.         Encoding enc = isCompatibleWith(otherStr);
  1343.         if (enc == null) return runtime.getNil();

  1344.         if (singleByteOptimizable() && otherStr.singleByteOptimizable()) {
  1345.             return RubyFixnum.newFixnum(runtime, value.caseInsensitiveCmp(otherStr.value));
  1346.         } else {
  1347.             return multiByteCasecmp(runtime, enc, value, otherStr.value);
  1348.         }
  1349.     }

  1350.     private IRubyObject multiByteCasecmp(Ruby runtime, Encoding enc, ByteList value, ByteList otherValue) {
  1351.         byte[]bytes = value.getUnsafeBytes();
  1352.         int p = value.getBegin();
  1353.         int end = p + value.getRealSize();

  1354.         byte[]obytes = otherValue.getUnsafeBytes();
  1355.         int op = otherValue.getBegin();
  1356.         int oend = op + otherValue.getRealSize();

  1357.         while (p < end && op < oend) {
  1358.             final int c, oc;
  1359.             if (enc.isAsciiCompatible()) {
  1360.                 c = bytes[p] & 0xff;
  1361.                 oc = obytes[op] & 0xff;
  1362.             } else {
  1363.                 c = StringSupport.preciseCodePoint(enc, bytes, p, end);
  1364.                 oc = StringSupport.preciseCodePoint(enc, obytes, op, oend);
  1365.             }

  1366.             int cl, ocl;
  1367.             if (Encoding.isAscii(c) && Encoding.isAscii(oc)) {
  1368.                 byte uc = AsciiTables.ToUpperCaseTable[c];
  1369.                 byte uoc = AsciiTables.ToUpperCaseTable[oc];
  1370.                 if (uc != uoc) {
  1371.                     return uc < uoc ? RubyFixnum.minus_one(runtime) : RubyFixnum.one(runtime);
  1372.                 }
  1373.                 cl = ocl = 1;
  1374.             } else {
  1375.                 cl = StringSupport.length(enc, bytes, p, end);
  1376.                 ocl = StringSupport.length(enc, obytes, op, oend);
  1377.                 // TODO: opt for 2 and 3 ?
  1378.                 int ret = StringSupport.caseCmp(bytes, p, obytes, op, cl < ocl ? cl : ocl);
  1379.                 if (ret != 0) return ret < 0 ? RubyFixnum.minus_one(runtime) : RubyFixnum.one(runtime);
  1380.                 if (cl != ocl) return cl < ocl ? RubyFixnum.minus_one(runtime) : RubyFixnum.one(runtime);
  1381.             }

  1382.             p += cl;
  1383.             op += ocl;
  1384.         }
  1385.         if (end - p == oend - op) return RubyFixnum.zero(runtime);
  1386.         return end - p > oend - op ? RubyFixnum.one(runtime) : RubyFixnum.minus_one(runtime);
  1387.     }

  1388.     /** rb_str_match
  1389.      *
  1390.      */
  1391.     @Override
  1392.     public IRubyObject op_match(ThreadContext context, IRubyObject other) {
  1393.         return op_match19(context, other);
  1394.     }

  1395.     @JRubyMethod(name = "=~", writes = BACKREF)
  1396.     @Override
  1397.     public IRubyObject op_match19(ThreadContext context, IRubyObject other) {
  1398.         if (other instanceof RubyRegexp) return ((RubyRegexp) other).op_match19(context, this);
  1399.         if (other instanceof RubyString) throw context.runtime.newTypeError("type mismatch: String given");
  1400.         return other.callMethod(context, "=~", this);
  1401.     }
  1402.     /**
  1403.      * String#match(pattern)
  1404.      *
  1405.      * rb_str_match_m
  1406.      *
  1407.      * @param pattern Regexp or String
  1408.      */
  1409.     public IRubyObject match(ThreadContext context, IRubyObject pattern) {
  1410.         return match19(context, pattern, Block.NULL_BLOCK);
  1411.     }

  1412.     @JRubyMethod(name = "match", reads = BACKREF)
  1413.     public IRubyObject match19(ThreadContext context, IRubyObject pattern, Block block) {
  1414.         IRubyObject result = getPattern(pattern).callMethod(context, "match", this);
  1415.         return block.isGiven() && !result.isNil() ? block.yield(context, result) : result;
  1416.     }

  1417.     @JRubyMethod(name = "match", required = 1, rest = true, reads = BACKREF)
  1418.     public IRubyObject match19(ThreadContext context, IRubyObject[] args, Block block) {
  1419.         RubyRegexp pattern = getPattern(args[0]);
  1420.         args[0] = this;
  1421.         IRubyObject result = pattern.callMethod(context, "match", args);
  1422.         return block.isGiven() && !result.isNil() ? block.yield(context, result) : result;
  1423.     }

  1424.     /** rb_str_capitalize / rb_str_capitalize_bang
  1425.      *
  1426.      */
  1427.     public IRubyObject capitalize(ThreadContext context) {
  1428.         return capitalize19(context);
  1429.     }

  1430.     public IRubyObject capitalize_bang(ThreadContext context) {
  1431.         return capitalize_bang19(context);
  1432.     }

  1433.     @JRubyMethod(name = "capitalize")
  1434.     public IRubyObject capitalize19(ThreadContext context) {
  1435.         RubyString str = strDup(context.runtime);
  1436.         str.capitalize_bang19(context);
  1437.         return str;
  1438.     }

  1439.     @JRubyMethod(name = "capitalize!")
  1440.     public IRubyObject capitalize_bang19(ThreadContext context) {
  1441.         Ruby runtime = context.runtime;
  1442.         Encoding enc = checkDummyEncoding();

  1443.         if (value.getRealSize() == 0) {
  1444.             modifyCheck();
  1445.             return runtime.getNil();
  1446.         }

  1447.         modifyAndKeepCodeRange();

  1448.         int s = value.getBegin();
  1449.         int end = s + value.getRealSize();
  1450.         byte[]bytes = value.getUnsafeBytes();
  1451.         boolean modify = false;

  1452.         int c = codePoint(runtime, enc, bytes, s, end);
  1453.         if (enc.isLower(c)) {
  1454.             enc.codeToMbc(toUpper(enc, c), bytes, s);
  1455.             modify = true;
  1456.         }

  1457.         s += codeLength(runtime, enc, c);
  1458.         while (s < end) {
  1459.             c = codePoint(runtime, enc, bytes, s, end);
  1460.             if (enc.isUpper(c)) {
  1461.                 enc.codeToMbc(toLower(enc, c), bytes, s);
  1462.                 modify = true;
  1463.             }
  1464.             s += codeLength(runtime, enc, c);
  1465.         }

  1466.         return modify ? this : runtime.getNil();
  1467.     }

  1468.     public IRubyObject op_ge(ThreadContext context, IRubyObject other) {
  1469.         return op_ge19(context, other);
  1470.     }

  1471.     @JRubyMethod(name = ">=")
  1472.     public IRubyObject op_ge19(ThreadContext context, IRubyObject other) {
  1473.         if (other instanceof RubyString) return context.runtime.newBoolean(op_cmp((RubyString) other) >= 0);
  1474.         return RubyComparable.op_ge(context, this, other);
  1475.     }

  1476.     public IRubyObject op_gt(ThreadContext context, IRubyObject other) {
  1477.         return op_gt19(context, other);
  1478.     }

  1479.     @JRubyMethod(name = ">")
  1480.     public IRubyObject op_gt19(ThreadContext context, IRubyObject other) {
  1481.         if (other instanceof RubyString) return context.runtime.newBoolean(op_cmp((RubyString) other) > 0);
  1482.         return RubyComparable.op_gt(context, this, other);
  1483.     }

  1484.     public IRubyObject op_le(ThreadContext context, IRubyObject other) {
  1485.         return op_le19(context, other);
  1486.     }

  1487.     @JRubyMethod(name = "<=")
  1488.     public IRubyObject op_le19(ThreadContext context, IRubyObject other) {
  1489.         if (other instanceof RubyString) return context.runtime.newBoolean(op_cmp((RubyString) other) <= 0);
  1490.         return RubyComparable.op_le(context, this, other);
  1491.     }

  1492.     public IRubyObject op_lt(ThreadContext context, IRubyObject other) {
  1493.         return op_lt19(context, other);
  1494.     }

  1495.     @JRubyMethod(name = "<")
  1496.     public IRubyObject op_lt19(ThreadContext context, IRubyObject other) {
  1497.         if (other instanceof RubyString) return context.runtime.newBoolean(op_cmp((RubyString) other) < 0);
  1498.         return RubyComparable.op_lt(context, this, other);
  1499.     }

  1500.     public IRubyObject str_eql_p(ThreadContext context, IRubyObject other) {
  1501.         return str_eql_p19(context, other);
  1502.     }

  1503.     @JRubyMethod(name = "eql?")
  1504.     public IRubyObject str_eql_p19(ThreadContext context, IRubyObject other) {
  1505.         Ruby runtime = context.runtime;
  1506.         if (other instanceof RubyString) {
  1507.             RubyString otherString = (RubyString)other;
  1508.             if (isComparableWith(otherString) && value.equal(otherString.value)) return runtime.getTrue();
  1509.         }
  1510.         return runtime.getFalse();
  1511.     }

  1512.     /** rb_str_upcase / rb_str_upcase_bang
  1513.      *
  1514.      */
  1515.     public RubyString upcase(ThreadContext context) {
  1516.         return upcase19(context);
  1517.     }

  1518.     public IRubyObject upcase_bang(ThreadContext context) {
  1519.         return upcase_bang19(context);
  1520.     }

  1521.     @JRubyMethod(name = "upcase")
  1522.     public RubyString upcase19(ThreadContext context) {
  1523.         RubyString str = strDup(context.runtime);
  1524.         str.upcase_bang19(context);
  1525.         return str;
  1526.     }

  1527.     @JRubyMethod(name = "upcase!")
  1528.     public IRubyObject upcase_bang19(ThreadContext context) {
  1529.         Ruby runtime = context.runtime;
  1530.         Encoding enc = checkDummyEncoding();

  1531.         if (value.getRealSize() == 0) {
  1532.             modifyCheck();
  1533.             return runtime.getNil();
  1534.         }

  1535.         modifyAndKeepCodeRange();

  1536.         int s = value.getBegin();
  1537.         int end = s + value.getRealSize();
  1538.         byte[]bytes = value.getUnsafeBytes();

  1539.         if (singleByteOptimizable(enc)) {
  1540.             return singleByteUpcase(runtime, bytes, s, end);
  1541.         } else {
  1542.             return multiByteUpcase(runtime, enc, bytes, s, end);
  1543.         }
  1544.     }

  1545.     private IRubyObject singleByteUpcase(Ruby runtime, byte[]bytes, int s, int end) {
  1546.         boolean modify = false;
  1547.         while (s < end) {
  1548.             int c = bytes[s] & 0xff;
  1549.             if (ASCII.isLower(c)) {
  1550.                 bytes[s] = AsciiTables.ToUpperCaseTable[c];
  1551.                 modify = true;
  1552.             }
  1553.             s++;
  1554.         }
  1555.         return modify ? this : runtime.getNil();
  1556.     }

  1557.     private IRubyObject multiByteUpcase(Ruby runtime, Encoding enc, byte[]bytes, int s, int end) {
  1558.         boolean modify = false;
  1559.         int c;
  1560.         while (s < end) {
  1561.             if (enc.isAsciiCompatible() && Encoding.isAscii(c = bytes[s] & 0xff)) {
  1562.                 if (ASCII.isLower(c)) {
  1563.                     bytes[s] = AsciiTables.ToUpperCaseTable[c];
  1564.                     modify = true;
  1565.                 }
  1566.                 s++;
  1567.             } else {
  1568.                 c = codePoint(runtime, enc, bytes, s, end);
  1569.                 if (enc.isLower(c)) {
  1570.                     enc.codeToMbc(toUpper(enc, c), bytes, s);
  1571.                     modify = true;
  1572.                 }
  1573.                 s += codeLength(runtime, enc, c);
  1574.             }
  1575.         }
  1576.         return modify ? this : runtime.getNil();
  1577.     }

  1578.     /** rb_str_downcase / rb_str_downcase_bang
  1579.      *
  1580.      */
  1581.     public RubyString downcase(ThreadContext context) {
  1582.         return downcase19(context);
  1583.     }

  1584.     public IRubyObject downcase_bang(ThreadContext context) {
  1585.         return downcase_bang19(context);
  1586.     }

  1587.     @JRubyMethod(name = "downcase")
  1588.     public RubyString downcase19(ThreadContext context) {
  1589.         RubyString str = strDup(context.runtime);
  1590.         str.downcase_bang19(context);
  1591.         return str;
  1592.     }

  1593.     @JRubyMethod(name = "downcase!")
  1594.     public IRubyObject downcase_bang19(ThreadContext context) {
  1595.         Ruby runtime = context.runtime;
  1596.         Encoding enc = checkDummyEncoding();

  1597.         if (value.getRealSize() == 0) {
  1598.             modifyCheck();
  1599.             return runtime.getNil();
  1600.         }

  1601.         modifyAndKeepCodeRange();

  1602.         int s = value.getBegin();
  1603.         int end = s + value.getRealSize();
  1604.         byte[]bytes = value.getUnsafeBytes();

  1605.         if (singleByteOptimizable(enc)) {
  1606.             return singleByteDowncase(runtime, bytes, s, end);
  1607.         } else {
  1608.             return multiByteDowncase(runtime, enc, bytes, s, end);
  1609.         }
  1610.     }

  1611.     private IRubyObject singleByteDowncase(Ruby runtime, byte[]bytes, int s, int end) {
  1612.         boolean modify = false;
  1613.         while (s < end) {
  1614.             int c = bytes[s] & 0xff;
  1615.             if (ASCII.isUpper(c)) {
  1616.                 bytes[s] = AsciiTables.ToLowerCaseTable[c];
  1617.                 modify = true;
  1618.             }
  1619.             s++;
  1620.         }
  1621.         return modify ? this : runtime.getNil();
  1622.     }

  1623.     private IRubyObject multiByteDowncase(Ruby runtime, Encoding enc, byte[]bytes, int s, int end) {
  1624.         boolean modify = false;
  1625.         int c;
  1626.         while (s < end) {
  1627.             if (enc.isAsciiCompatible() && Encoding.isAscii(c = bytes[s] & 0xff)) {
  1628.                 if (ASCII.isUpper(c)) {
  1629.                     bytes[s] = AsciiTables.ToLowerCaseTable[c];
  1630.                     modify = true;
  1631.                 }
  1632.                 s++;
  1633.             } else {
  1634.                 c = codePoint(runtime, enc, bytes, s, end);
  1635.                 if (enc.isUpper(c)) {
  1636.                     enc.codeToMbc(toLower(enc, c), bytes, s);
  1637.                     modify = true;
  1638.                 }
  1639.                 s += codeLength(runtime, enc, c);
  1640.             }
  1641.         }
  1642.         return modify ? this : runtime.getNil();
  1643.     }


  1644.     /** rb_str_swapcase / rb_str_swapcase_bang
  1645.      *
  1646.      */
  1647.     public RubyString swapcase(ThreadContext context) {
  1648.         return swapcase19(context);
  1649.     }

  1650.     public IRubyObject swapcase_bang(ThreadContext context) {
  1651.         return swapcase_bang19(context);
  1652.     }

  1653.     @JRubyMethod(name = "swapcase")
  1654.     public RubyString swapcase19(ThreadContext context) {
  1655.         RubyString str = strDup(context.runtime);
  1656.         str.swapcase_bang19(context);
  1657.         return str;
  1658.     }

  1659.     @JRubyMethod(name = "swapcase!")
  1660.     public IRubyObject swapcase_bang19(ThreadContext context) {
  1661.         Ruby runtime = context.runtime;
  1662.         Encoding enc = checkDummyEncoding();
  1663.         if (value.getRealSize() == 0) {
  1664.             modifyCheck();
  1665.             return runtime.getNil();
  1666.         }
  1667.         modifyAndKeepCodeRange();

  1668.         int s = value.getBegin();
  1669.         int end = s + value.getRealSize();
  1670.         byte[]bytes = value.getUnsafeBytes();

  1671.         if (singleByteOptimizable(enc)) {
  1672.             return singleByteSwapcase(runtime, bytes, s, end);
  1673.         } else {
  1674.             return multiByteSwapcase(runtime, enc, bytes, s, end);
  1675.         }
  1676.     }

  1677.     private IRubyObject singleByteSwapcase(Ruby runtime, byte[]bytes, int s, int end) {
  1678.         boolean modify = false;
  1679.         while (s < end) {
  1680.             int c = bytes[s] & 0xff;
  1681.             if (ASCII.isUpper(c)) {
  1682.                 bytes[s] = AsciiTables.ToLowerCaseTable[c];
  1683.                 modify = true;
  1684.             } else if (ASCII.isLower(c)) {
  1685.                 bytes[s] = AsciiTables.ToUpperCaseTable[c];
  1686.                 modify = true;
  1687.             }
  1688.             s++;
  1689.         }

  1690.         return modify ? this : runtime.getNil();
  1691.     }

  1692.     private IRubyObject multiByteSwapcase(Ruby runtime, Encoding enc, byte[]bytes, int s, int end) {
  1693.         boolean modify = false;
  1694.         while (s < end) {
  1695.             int c = codePoint(runtime, enc, bytes, s, end);
  1696.             if (enc.isUpper(c)) {
  1697.                 enc.codeToMbc(toLower(enc, c), bytes, s);
  1698.                 modify = true;
  1699.             } else if (enc.isLower(c)) {
  1700.                 enc.codeToMbc(toUpper(enc, c), bytes, s);
  1701.                 modify = true;
  1702.             }
  1703.             s += codeLength(runtime, enc, c);
  1704.         }

  1705.         return modify ? this : runtime.getNil();
  1706.     }

  1707.     /** rb_str_dump
  1708.      *
  1709.      */
  1710.     public IRubyObject dump() {
  1711.         return dump19();
  1712.     }

  1713.     @JRubyMethod(name = "dump")
  1714.     public IRubyObject dump19() {
  1715.         ByteList outBytes = StringSupport.dumpCommon(getRuntime(), value);

  1716.         final RubyString result = new RubyString(getRuntime(), getMetaClass(), outBytes);
  1717.         Encoding enc = value.getEncoding();

  1718.         if (!enc.isAsciiCompatible()) {
  1719.             result.cat(".force_encoding(\"".getBytes());
  1720.             result.cat(enc.getName());
  1721.             result.cat((byte)'"').cat((byte)')');
  1722.             enc = ASCII;
  1723.         }
  1724.         result.associateEncoding(enc);
  1725.         result.setCodeRange(CR_7BIT);

  1726.         return result.infectBy(this);
  1727.     }

  1728.     public IRubyObject insert(ThreadContext context, IRubyObject indexArg, IRubyObject stringArg) {
  1729.         return insert19(context, indexArg, stringArg);
  1730.     }

  1731.     @JRubyMethod(name = "insert")
  1732.     public IRubyObject insert19(ThreadContext context, IRubyObject indexArg, IRubyObject stringArg) {
  1733.         RubyString str = stringArg.convertToString();
  1734.         int index = RubyNumeric.num2int(indexArg);
  1735.         if (index == -1) return append19(stringArg);
  1736.         if (index < 0) index++;
  1737.         replaceInternal19(checkIndex(index, strLength()), 0, str);
  1738.         return this;
  1739.     }

  1740.     private int checkIndex(int beg, int len) {
  1741.         if (beg > len) raiseIndexOutOfString(beg);
  1742.         if (beg < 0) {
  1743.             if (-beg > len) raiseIndexOutOfString(beg);
  1744.             beg += len;
  1745.         }
  1746.         return beg;
  1747.     }

  1748.     private int checkIndexForRef(int beg, int len) {
  1749.         if (beg >= len) raiseIndexOutOfString(beg);
  1750.         if (beg < 0) {
  1751.             if (-beg > len) raiseIndexOutOfString(beg);
  1752.             beg += len;
  1753.         }
  1754.         return beg;
  1755.     }

  1756.     private int checkLength(int len) {
  1757.         if (len < 0) throw getRuntime().newIndexError("negative length " + len);
  1758.         return len;
  1759.     }

  1760.     private void raiseIndexOutOfString(int index) {
  1761.         throw getRuntime().newIndexError("index " + index + " out of string");
  1762.     }

  1763.     /** rb_str_inspect
  1764.      *
  1765.      */
  1766.     @Override
  1767.     public IRubyObject inspect() {
  1768.         return inspect19();
  1769.     }

  1770.     @JRubyMethod(name = "inspect")
  1771.     public IRubyObject inspect19() {
  1772.         return inspect19(getRuntime(), value).infectBy(this);
  1773.     }

  1774.     public static IRubyObject inspect19(Ruby runtime, ByteList byteList) {
  1775.         byte bytes[] = byteList.getUnsafeBytes();
  1776.         int p = byteList.getBegin();
  1777.         int end = p + byteList.getRealSize();
  1778.         RubyString result = new RubyString(runtime, runtime.getString(), new ByteList(end - p));
  1779.         Encoding enc = byteList.getEncoding();

  1780.         Encoding resultEnc = runtime.getDefaultInternalEncoding();
  1781.         if (resultEnc == null) resultEnc = runtime.getDefaultExternalEncoding();
  1782.         if (!resultEnc.isAsciiCompatible()) resultEnc = USASCIIEncoding.INSTANCE;
  1783.         result.associateEncoding(resultEnc);

  1784.         boolean isUnicode = StringSupport.isUnicode(enc);

  1785.         EncodingDB.Entry e = null;
  1786.         CaseInsensitiveBytesHash<EncodingDB.Entry> encodings = runtime.getEncodingService().getEncodings();
  1787.         if (enc == encodings.get("UTF-16".getBytes()).getEncoding() && end - p > 1) {
  1788.             int c0 = bytes[p] & 0xff;
  1789.             int c1 = bytes[p + 1] & 0xff;

  1790.             if (c0 == 0xFE && c1 == 0xFF) {
  1791.                 e = encodings.get("UTF-16BE".getBytes());
  1792.             } else if (c0 == 0xFF && c1 == 0xFE) {
  1793.                 e = encodings.get("UTF-16LE".getBytes());
  1794.             } else {
  1795.                 isUnicode = false;
  1796.             }
  1797.         } else if (enc == encodings.get("UTF-32".getBytes()).getEncoding() && end - p > 3) {
  1798.             int c0 = bytes[p] & 0xff;
  1799.             int c1 = bytes[p + 1] & 0xff;
  1800.             int c2 = bytes[p + 2] & 0xff;
  1801.             int c3 = bytes[p + 3] & 0xff;

  1802.             if (c0 == 0 && c1 == 0 && c2 == 0xFE && c3 == 0xFF) {
  1803.                 e = encodings.get("UTF-32BE".getBytes());
  1804.             } else if (c3 == 0 && c2 == 0 && c1 == 0xFE && c0 == 0xFF) {
  1805.                 e = encodings.get("UTF-32LE".getBytes());
  1806.             } else {
  1807.                 isUnicode = false;
  1808.             }
  1809.         }

  1810.         if (e != null) enc = e.getEncoding();

  1811.         result.cat('"');
  1812.         int prev = p;
  1813.         while (p < end) {
  1814.             int cc = 0;

  1815.             int n = StringSupport.preciseLength(enc, bytes, p, end);
  1816.             if (n <= 0) {
  1817.                 if (p > prev) result.cat(bytes, prev, p - prev);
  1818.                 n = enc.minLength();
  1819.                 if (end < p + n) n = end - p;
  1820.                 while (n-- > 0) {
  1821.                     Sprintf.sprintf(runtime, result.getByteList() ,"\\x%02X", bytes[p] & 0377);
  1822.                     prev = ++p;
  1823.                 }
  1824.                 continue;
  1825.             }
  1826.             int c = enc.mbcToCode(bytes, p, end);
  1827.             p += n;
  1828.             if ((enc.isAsciiCompatible() || isUnicode) &&
  1829.                     (c == '"' || c == '\\' ||
  1830.                         (c == '#' && p < end && (StringSupport.preciseLength(enc, bytes, p, end) > 0) &&
  1831.                         (cc = codePoint(runtime, enc, bytes, p, end)) == '$' || cc == '@' || cc == '{'))) {
  1832.                 if (p - n > prev) result.cat(bytes, prev, p - n - prev);
  1833.                 result.cat('\\');
  1834.                 if (enc.isAsciiCompatible() || enc == resultEnc) {
  1835.                     prev = p - n;
  1836.                     continue;
  1837.                 }
  1838.             }

  1839.             switch (c) {
  1840.             case '\n': cc = 'n'; break;
  1841.             case '\r': cc = 'r'; break;
  1842.             case '\t': cc = 't'; break;
  1843.             case '\f': cc = 'f'; break;
  1844.             case '\013': cc = 'v'; break;
  1845.             case '\010': cc = 'b'; break;
  1846.             case '\007': cc = 'a'; break;
  1847.             case 033: cc = 'e'; break;
  1848.             default: cc = 0; break;
  1849.             }

  1850.             if (cc != 0) {
  1851.                 if (p - n > prev) result.cat(bytes, prev, p - n - prev);
  1852.                 result.cat('\\');
  1853.                 result.cat(cc);
  1854.                 prev = p;
  1855.                 continue;
  1856.             }

  1857.             if ((enc == resultEnc && enc.isPrint(c)) || (enc.isAsciiCompatible() && Encoding.isAscii(c) && enc.isPrint(c))) {
  1858.                 continue;
  1859.             } else {
  1860.                 if (p - n > prev) result.cat(bytes, prev, p - n - prev);
  1861.                 Sprintf.sprintf(runtime, result.getByteList() , StringSupport.escapedCharFormat(c, isUnicode), c);
  1862.                 prev = p;
  1863.                 continue;
  1864.             }
  1865.         }

  1866.         if (p > prev) result.cat(bytes, prev, p - prev);
  1867.         result.cat('"');
  1868.         return result;
  1869.     }

  1870.     public int size() {
  1871.         return value.getRealSize();
  1872.     }

  1873.     /** rb_str_length
  1874.      *
  1875.      */
  1876.     public RubyFixnum length() {
  1877.         return length19();
  1878.     }

  1879.     @JRubyMethod(name = {"length", "size"})
  1880.     public RubyFixnum length19() {
  1881.         return getRuntime().newFixnum(strLength());
  1882.     }

  1883.     @JRubyMethod(name = "bytesize")
  1884.     public RubyFixnum bytesize() {
  1885.         return getRuntime().newFixnum(value.getRealSize());
  1886.     }

  1887.     private SizeFn eachByteSizeFn() {
  1888.         final RubyString self = this;
  1889.         return new SizeFn() {
  1890.             @Override
  1891.             public IRubyObject size(IRubyObject[] args) {
  1892.                 return self.bytesize();
  1893.             }
  1894.         };
  1895.     }

  1896.     /** rb_str_empty
  1897.      *
  1898.      */
  1899.     @JRubyMethod(name = "empty?")
  1900.     public RubyBoolean empty_p(ThreadContext context) {
  1901.         return isEmpty() ? context.runtime.getTrue() : context.runtime.getFalse();
  1902.     }

  1903.     public boolean isEmpty() {
  1904.         return value.length() == 0;
  1905.     }

  1906.     /** rb_str_append
  1907.      *
  1908.      */
  1909.     public RubyString append(IRubyObject other) {
  1910.         if (other instanceof RubyFixnum) {
  1911.             cat(ConvertBytes.longToByteList(((RubyFixnum)other).getLongValue()));
  1912.             return this;
  1913.         } else if (other instanceof RubyFloat) {
  1914.             return cat((RubyString)((RubyFloat)other).to_s());
  1915.         } else if (other instanceof RubySymbol) {
  1916.             cat(((RubySymbol)other).getBytes());
  1917.             return this;
  1918.         }
  1919.         RubyString otherStr = other.convertToString();
  1920.         infectBy(otherStr);
  1921.         return cat(otherStr.value);
  1922.     }

  1923.     public RubyString append19(IRubyObject other) {
  1924.         if (other instanceof RubyFixnum) {
  1925.             cat19(ConvertBytes.longToByteList(((RubyFixnum)other).getLongValue()), StringSupport.CR_7BIT);
  1926.             return this;
  1927.         } else if (other instanceof RubyFloat) {
  1928.             return cat19((RubyString)((RubyFloat)other).to_s());
  1929.         } else if (other instanceof RubySymbol) {
  1930.             cat19(((RubySymbol)other).getBytes(), 0);
  1931.             return this;
  1932.         }
  1933.         return cat19(other.convertToString());
  1934.     }

  1935.     /** rb_str_concat
  1936.      *
  1937.      */
  1938.     public RubyString concat(IRubyObject other) {
  1939.         return concat19(getRuntime().getCurrentContext(), other);
  1940.     }

  1941.     @JRubyMethod(name = {"concat", "<<"})
  1942.     public RubyString concat19(ThreadContext context, IRubyObject other) {
  1943.         Ruby runtime = context.runtime;
  1944.         if (other instanceof RubyFixnum) {
  1945.             int c = RubyNumeric.num2int(other);
  1946.             if (c < 0) {
  1947.                 throw runtime.newRangeError("negative string size (or size too big)");
  1948.             }
  1949.             return concatNumeric(runtime, c);
  1950.         } else if (other instanceof RubyBignum) {
  1951.             if (((RubyBignum) other).getBigIntegerValue().signum() < 0) {
  1952.                 throw runtime.newRangeError("negative string size (or size too big)");
  1953.             }
  1954.             long c = ((RubyBignum) other).getLongValue();
  1955.             return concatNumeric(runtime, (int) c);
  1956.         }

  1957.         if (other instanceof RubySymbol) throw runtime.newTypeError("can't convert Symbol into String");
  1958.         return append19(other);
  1959.     }

  1960.     private RubyString concatNumeric(Ruby runtime, int c) {
  1961.         Encoding enc = value.getEncoding();
  1962.         int cl;

  1963.         try {
  1964.             cl = codeLength(runtime, enc, c);
  1965.             modify19(value.getRealSize() + cl);

  1966.             if (enc == USASCIIEncoding.INSTANCE) {
  1967.                 if (c > 0xff) runtime.newRangeError(c + " out of char range");
  1968.                 if (c > 0x79) {
  1969.                     value.setEncoding(ASCIIEncoding.INSTANCE);
  1970.                     enc = value.getEncoding();
  1971.                 }
  1972.             }
  1973.             enc.codeToMbc(c, value.getUnsafeBytes(), value.getBegin() + value.getRealSize());
  1974.         } catch (EncodingException e) {
  1975.             throw runtime.newRangeError(c + " out of char range");
  1976.         }
  1977.         value.setRealSize(value.getRealSize() + cl);
  1978.         return this;
  1979.     }

  1980.     /**
  1981.      * rb_str_prepend
  1982.      */
  1983.     @JRubyMethod
  1984.     public IRubyObject prepend(ThreadContext context, IRubyObject other) {
  1985.         return replace19(other.convertToString().op_plus19(context, this));
  1986.     }

  1987.     /** rb_str_crypt
  1988.      *
  1989.      */
  1990.     @JRubyMethod(name = "crypt")
  1991.     public RubyString crypt(ThreadContext context, IRubyObject other) {
  1992.         Encoding ascii8bit = context.runtime.getEncodingService().getAscii8bitEncoding();
  1993.         RubyString otherStr = other.convertToString();
  1994.         otherStr.associateEncoding(ascii8bit);
  1995.         String salt = otherStr.asJavaString();
  1996.         if (otherStr.getByteList().length() < 2) {
  1997.             throw context.runtime.newArgumentError("salt too short(need >=2 bytes)");
  1998.         }

  1999.         RubyString result = RubyString.newString(context.runtime,
  2000.                 context.runtime.getPosix().crypt(asJavaString(), salt).toString());
  2001.         result.associateEncoding(ascii8bit);
  2002.         result.infectBy(this);
  2003.         result.infectBy(otherStr);
  2004.         return result;
  2005.     }

  2006.     /* RubyString aka rb_string_value */
  2007.     public static RubyString stringValue(IRubyObject object) {
  2008.         return (RubyString) (object instanceof RubyString ? object :
  2009.             object.convertToString());
  2010.     }

  2011.     /** rb_str_sub / rb_str_sub_bang
  2012.      *
  2013.      */
  2014.     public IRubyObject sub(ThreadContext context, IRubyObject arg0, Block block) {
  2015.         return sub19(context, arg0, block);
  2016.     }

  2017.     public IRubyObject sub(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
  2018.         return sub19(context, arg0, arg1, block);
  2019.     }

  2020.     public IRubyObject sub_bang(ThreadContext context, IRubyObject arg0, Block block) {
  2021.         return sub_bang19(context, arg0, block);
  2022.     }

  2023.     public IRubyObject sub_bang(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
  2024.         return sub_bang19(context, arg0, arg1, block);
  2025.     }

  2026.     @JRubyMethod(name = "sub", reads = BACKREF, writes = BACKREF)
  2027.     public IRubyObject sub19(ThreadContext context, IRubyObject arg0, Block block) {
  2028.         RubyString str = strDup(context.runtime);
  2029.         str.sub_bang19(context, arg0, block);
  2030.         return str;
  2031.     }

  2032.     @JRubyMethod(name = "sub", reads = BACKREF, writes = BACKREF)
  2033.     public IRubyObject sub19(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
  2034.         RubyString str = strDup(context.runtime);
  2035.         str.sub_bang19(context, arg0, arg1, block);
  2036.         return str;
  2037.     }

  2038.     @JRubyMethod(name = "sub!", reads = BACKREF, writes = BACKREF)
  2039.     public IRubyObject sub_bang19(ThreadContext context, IRubyObject arg0, Block block) {
  2040.         Ruby runtime = context.runtime;
  2041.         frozenCheck();

  2042.         final Regex pattern, prepared;
  2043.         final RubyRegexp regexp;
  2044.         if (arg0 instanceof RubyRegexp) {
  2045.             regexp = (RubyRegexp)arg0;
  2046.             pattern = regexp.getPattern();
  2047.             prepared = regexp.preparePattern(this);
  2048.         } else {
  2049.             regexp = null;
  2050.             pattern = getStringPattern19(runtime, arg0);
  2051.             prepared = RubyRegexp.preparePattern(runtime, pattern, this);
  2052.         }

  2053.         if (block.isGiven()) return subBangIter19(runtime, context, pattern, prepared, null, block, regexp);
  2054.         throw context.runtime.newArgumentError(1, 2);
  2055.     }

  2056.     @JRubyMethod(name = "sub!", reads = BACKREF, writes = BACKREF)
  2057.     public IRubyObject sub_bang19(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
  2058.         Ruby runtime = context.runtime;
  2059.         IRubyObject hash = TypeConverter.convertToTypeWithCheck(arg1, runtime.getHash(), "to_hash");
  2060.         frozenCheck();

  2061.         final Regex pattern, prepared;
  2062.         final RubyRegexp regexp;
  2063.         if (arg0 instanceof RubyRegexp) {
  2064.             regexp = (RubyRegexp)arg0;
  2065.             pattern = regexp.getPattern();
  2066.             prepared = regexp.preparePattern(this);
  2067.         } else {
  2068.             regexp = null;
  2069.             pattern = getStringPattern19(runtime, arg0);
  2070.             prepared = RubyRegexp.preparePattern(runtime, pattern, this);
  2071.         }

  2072.         if (hash.isNil()) {
  2073.             return subBangNoIter19(runtime, context, pattern, prepared, arg1.convertToString(), regexp);
  2074.         } else {
  2075.             return subBangIter19(runtime, context, pattern, prepared, (RubyHash)hash, block, regexp);
  2076.         }
  2077.     }

  2078.     private IRubyObject subBangIter19(Ruby runtime, ThreadContext context, Regex pattern, Regex prepared, RubyHash hash, Block block, RubyRegexp regexp) {
  2079.         int begin = value.getBegin();
  2080.         int len = value.getRealSize();
  2081.         int range = begin + len;
  2082.         byte[]bytes = value.getUnsafeBytes();
  2083.         Encoding enc = value.getEncoding();
  2084.         final Matcher matcher = prepared.matcher(bytes, begin, range);

  2085.         if (RubyRegexp.matcherSearch(runtime, matcher, begin, range, Option.NONE) >= 0) {
  2086.             RubyMatchData match = RubyRegexp.createMatchData19(context, this, matcher, pattern);
  2087.             match.regexp = regexp;
  2088.             context.setBackRef(match);
  2089.             final RubyString repl;
  2090.             final int tuFlags;
  2091.             IRubyObject subStr = makeShared19(runtime, matcher.getBegin(), matcher.getEnd() - matcher.getBegin());
  2092.             if (hash == null) {
  2093.                 tuFlags = 0;
  2094.                 repl = objAsString(context, block.yield(context, subStr));
  2095.             } else {
  2096.                 tuFlags = hash.flags;
  2097.                 repl = objAsString(context, hash.op_aref(context, subStr));
  2098.             }

  2099.             modifyCheck(bytes, len, enc);
  2100.             frozenCheck();
  2101.             return subBangCommon19(context, pattern, matcher, repl, tuFlags | repl.flags);
  2102.         } else {
  2103.             return context.setBackRef(runtime.getNil());
  2104.         }
  2105.     }

  2106.     private IRubyObject subBangNoIter19(Ruby runtime, ThreadContext context, Regex pattern, Regex prepared, RubyString repl, RubyRegexp regexp) {
  2107.         int begin = value.getBegin();
  2108.         int range = begin + value.getRealSize();
  2109.         final Matcher matcher = prepared.matcher(value.getUnsafeBytes(), begin, range);

  2110.         if (RubyRegexp.matcherSearch(runtime, matcher, begin, range, Option.NONE) >= 0) {
  2111.             repl = RubyRegexp.regsub19(context, repl, this, matcher, pattern);
  2112.             RubyMatchData match = RubyRegexp.createMatchData19(context, this, matcher, pattern);
  2113.             match.regexp = regexp;
  2114.             context.setBackRef(match);
  2115.             return subBangCommon19(context, pattern, matcher, repl, repl.flags);
  2116.         } else {
  2117.             return context.setBackRef(runtime.getNil());
  2118.         }
  2119.     }

  2120.     private IRubyObject subBangCommon19(ThreadContext context, Regex pattern, Matcher matcher, RubyString repl, int tuFlags) {
  2121.         final int beg = matcher.getBegin();
  2122.         final int end = matcher.getEnd();
  2123.         int cr = getCodeRange();

  2124.         Encoding enc = isCompatibleWith(repl);
  2125.         if (enc == null) enc = subBangVerifyEncoding(context, repl, beg, end);

  2126.         final int plen = end - beg;
  2127.         ByteList replValue = repl.value;
  2128.         if (replValue.getRealSize() > plen) {
  2129.             modify19(value.getRealSize() + replValue.getRealSize() - plen);
  2130.         } else {
  2131.             modify19();
  2132.         }

  2133.         associateEncoding(enc);

  2134.         if (cr > CR_UNKNOWN && cr < CR_BROKEN) {
  2135.             int cr2 = repl.getCodeRange();
  2136.             if (cr2 == CR_BROKEN || (cr == CR_VALID && cr2 == CR_7BIT)) {
  2137.                 cr = CR_UNKNOWN;
  2138.             } else {
  2139.                 cr = cr2;
  2140.             }
  2141.         }

  2142.         if (replValue.getRealSize() != plen) {
  2143.             int src = value.getBegin() + beg + plen;
  2144.             int dst = value.getBegin() + beg + replValue.getRealSize();
  2145.             int length = value.getRealSize() - beg - plen;
  2146.             System.arraycopy(value.getUnsafeBytes(), src, value.getUnsafeBytes(), dst, length);
  2147.         }
  2148.         System.arraycopy(replValue.getUnsafeBytes(), replValue.getBegin(), value.getUnsafeBytes(), value.getBegin() + beg, replValue.getRealSize());
  2149.         value.setRealSize(value.getRealSize() + replValue.getRealSize() - plen);
  2150.         setCodeRange(cr);
  2151.         return infectBy(tuFlags);
  2152.     }

  2153.     private Encoding subBangVerifyEncoding(ThreadContext context, RubyString repl, int beg, int end) {
  2154.         byte[]bytes = value.getUnsafeBytes();
  2155.         int p = value.getBegin();
  2156.         int len = value.getRealSize();
  2157.         Encoding strEnc = value.getEncoding();
  2158.         if (codeRangeScan(strEnc, bytes, p, beg) != CR_7BIT ||
  2159.             codeRangeScan(strEnc, bytes, p + end, len - end) != CR_7BIT) {
  2160.             throw context.runtime.newArgumentError(
  2161.                     "incompatible character encodings " + strEnc + " and " + repl.value.getEncoding());
  2162.         }
  2163.         return repl.value.getEncoding();
  2164.     }

  2165.     /** rb_str_gsub / rb_str_gsub_bang
  2166.      *
  2167.      */
  2168.     public IRubyObject gsub(ThreadContext context, IRubyObject arg0, Block block) {
  2169.         return gsub19(context, arg0, block);
  2170.     }

  2171.     public IRubyObject gsub(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
  2172.         return gsub19(context, arg0, arg1, block);
  2173.     }

  2174.     public IRubyObject gsub_bang(ThreadContext context, IRubyObject arg0, Block block) {
  2175.         return gsub_bang19(context, arg0, block);
  2176.     }

  2177.     public IRubyObject gsub_bang(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
  2178.         return gsub_bang19(context, arg0, arg1, block);
  2179.     }

  2180.     @JRubyMethod(name = "gsub", reads = BACKREF, writes = BACKREF)
  2181.     public IRubyObject gsub19(ThreadContext context, IRubyObject arg0, Block block) {
  2182.         return block.isGiven() ? gsubCommon19(context, block, null, null, arg0, false, 0) : enumeratorize(context.runtime, this, "gsub", arg0);
  2183.     }

  2184.     @JRubyMethod(name = "gsub", reads = BACKREF, writes = BACKREF)
  2185.     public IRubyObject gsub19(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
  2186.         return gsub19(context, arg0, arg1, block, false);
  2187.     }

  2188.     @JRubyMethod(name = "gsub!", reads = BACKREF, writes = BACKREF)
  2189.     public IRubyObject gsub_bang19(ThreadContext context, IRubyObject arg0, Block block) {
  2190.         checkFrozen();
  2191.         return block.isGiven() ? gsubCommon19(context, block, null, null, arg0, true, 0) : enumeratorize(context.runtime, this, "gsub!", arg0);
  2192.     }

  2193.     @JRubyMethod(name = "gsub!", reads = BACKREF, writes = BACKREF)
  2194.     public IRubyObject gsub_bang19(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
  2195.         checkFrozen();
  2196.         return gsub19(context, arg0, arg1, block, true);
  2197.     }

  2198.     private IRubyObject gsub19(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block, final boolean bang) {
  2199.         Ruby runtime = context.runtime;
  2200.         IRubyObject tryHash = TypeConverter.convertToTypeWithCheck(arg1, runtime.getHash(), "to_hash");

  2201.         final RubyHash hash;
  2202.         final RubyString str;
  2203.         final int tuFlags;
  2204.         if (tryHash.isNil()) {
  2205.             hash = null;
  2206.             str = arg1.convertToString();
  2207.             tuFlags = str.flags;
  2208.         } else {
  2209.             hash = (RubyHash)tryHash;
  2210.             str = null;
  2211.             tuFlags = hash.flags & TAINTED_F;
  2212.         }

  2213.         return gsubCommon19(context, block, str, hash, arg0, bang, tuFlags);
  2214.     }

  2215.     private IRubyObject gsubCommon19(ThreadContext context, Block block, RubyString repl,
  2216.             RubyHash hash, IRubyObject arg0, final boolean bang, int tuFlags) {
  2217.         return gsubCommon19(context, block, repl, hash, arg0, bang, tuFlags, true);
  2218.     }

  2219.     // MRI: str_gsub, roughly
  2220.     private IRubyObject gsubCommon19(ThreadContext context, Block block, RubyString repl,
  2221.             RubyHash hash, IRubyObject arg0, final boolean bang, int tuFlags, boolean useBackref) {
  2222.         Ruby runtime = context.runtime;

  2223.         final Regex pattern, prepared;
  2224.         final RubyRegexp regexp;
  2225.         if (arg0 instanceof RubyRegexp) {
  2226.             regexp = (RubyRegexp)arg0;
  2227.             pattern = regexp.getPattern();
  2228.             prepared = regexp.preparePattern(this);
  2229.         } else {
  2230.             regexp = null;
  2231.             pattern = getStringPattern19(runtime, arg0);
  2232.             prepared = RubyRegexp.preparePattern(runtime, pattern, this);
  2233.         }

  2234.         int offset, cp, n, blen;

  2235.         final byte[] spBytes = value.getUnsafeBytes();
  2236.         final int sp = value.getBegin();
  2237.         final int spLen = value.getRealSize();

  2238.         final Matcher matcher = prepared.matcher(spBytes, sp, sp + spLen);

  2239.         int beg = RubyRegexp.matcherSearch(runtime, matcher, sp, sp + spLen, Option.NONE);
  2240.         if (beg < 0) {
  2241.             if (useBackref) context.setBackRef(runtime.getNil());
  2242.             return bang ? runtime.getNil() : strDup(runtime); /* bang: true, no match, no substitution */
  2243.         }

  2244.         offset = 0;
  2245.         n = 0;
  2246.         blen = value.getRealSize() + 30;
  2247.         RubyString dest = new RubyString(runtime, getMetaClass(), new ByteList(blen));
  2248.         int slen = value.getRealSize();
  2249.         cp = sp;
  2250.         Encoding str_enc = value.getEncoding();
  2251.         dest.setEncoding(str_enc);
  2252.         dest.setCodeRange(str_enc.isAsciiCompatible() ? CR_7BIT : CR_VALID);

  2253.         RubyMatchData match = null;
  2254.         do {
  2255.             n++;
  2256.             final RubyString val;
  2257.             int begz = matcher.getBegin();
  2258.             int endz = matcher.getEnd();

  2259.             if (repl != null) {     // string given
  2260.                 val = RubyRegexp.regsub19(context, repl, this, matcher, pattern);
  2261.             } else {
  2262.                 final RubyString substr = makeShared19(runtime, begz, endz - begz);
  2263.                 if (hash != null) { // hash given
  2264.                     val = objAsString(context, hash.op_aref(context, substr));
  2265.                 } else {            // block given
  2266.                     match = RubyRegexp.createMatchData19(context, this, matcher, pattern);
  2267.                     match.regexp = regexp;
  2268.                     if (useBackref) context.setBackRef(match);
  2269.                     val = objAsString(context, block.yield(context, substr));
  2270.                 }
  2271.                 modifyCheck(spBytes, slen, str_enc);
  2272.                 if (bang) frozenCheck();
  2273.             }

  2274.             tuFlags |= val.flags;

  2275.             int len = beg - offset;
  2276.             if (len != 0) dest.cat(spBytes, cp, len, str_enc);
  2277.             dest.cat19(val);
  2278.             offset = endz;
  2279.             if (begz == endz) {
  2280.                 if (slen <= endz) break;
  2281.                 len = StringSupport.encFastMBCLen(spBytes, sp + endz, sp + spLen, str_enc);
  2282.                 dest.cat(spBytes, sp + endz, len, str_enc);
  2283.                 offset = endz + len;
  2284.             }
  2285.             cp = sp + offset;
  2286.             if (offset > slen) break;
  2287.             beg = RubyRegexp.matcherSearch(runtime, matcher, cp, sp + spLen, Option.NONE);
  2288.         } while (beg >= 0);

  2289.         if (slen > offset) dest.cat(spBytes, cp, slen - offset, str_enc);

  2290.         if (match != null) { // block given
  2291.             if (useBackref) context.setBackRef(match);
  2292.         } else {
  2293.             match = RubyRegexp.createMatchData19(context, this, matcher, pattern);
  2294.             match.regexp = regexp;
  2295.             if (useBackref) context.setBackRef(match);
  2296.         }

  2297.         if (bang) {
  2298.             view(dest.value);
  2299.             setCodeRange(dest.getCodeRange());
  2300.             return infectBy(tuFlags);
  2301.         } else {
  2302.             return dest.infectBy(tuFlags | flags);
  2303.         }
  2304.     }

  2305.     /** rb_str_index_m
  2306.      *
  2307.      */
  2308.     public IRubyObject index(ThreadContext context, IRubyObject arg0) {
  2309.         return index19(context, arg0);
  2310.     }

  2311.     public IRubyObject index(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
  2312.         return index19(context, arg0, arg1);
  2313.     }

  2314.     @JRubyMethod(name = "index", reads = BACKREF, writes = BACKREF)
  2315.     public IRubyObject index19(ThreadContext context, IRubyObject arg0) {
  2316.         return indexCommon19(context.runtime, context, arg0, 0);
  2317.     }

  2318.     @JRubyMethod(name = "index", reads = BACKREF, writes = BACKREF)
  2319.     public IRubyObject index19(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
  2320.         int pos = RubyNumeric.num2int(arg1);
  2321.         Ruby runtime = context.runtime;
  2322.         if (pos < 0) {
  2323.             pos += strLength();
  2324.             if (pos < 0) {
  2325.                 if (arg0 instanceof RubyRegexp) context.setBackRef(runtime.getNil());
  2326.                 return runtime.getNil();
  2327.             }
  2328.         }
  2329.         return indexCommon19(runtime, context, arg0, pos);
  2330.     }

  2331.     private IRubyObject indexCommon19(Ruby runtime, ThreadContext context, IRubyObject sub, int pos) {
  2332.         if (sub instanceof RubyRegexp) {
  2333.             if (pos > strLength()) return context.nil;
  2334.             RubyRegexp regSub = (RubyRegexp) sub;
  2335.             pos = singleByteOptimizable() ? pos :
  2336.                     StringSupport.nth(checkEncoding(regSub), value.getUnsafeBytes(), value.getBegin(),
  2337.                             value.getBegin() + value.getRealSize(),
  2338.                                       pos) - value.getBegin();
  2339.             pos = regSub.adjustStartPos19(this, pos, false);
  2340.             IRubyObject[] holder = {context.nil};
  2341.             pos = regSub.search19(context, this, pos, false, holder);
  2342.             context.setBackRef(holder[0]);
  2343.             pos = subLength(pos);
  2344.         } else if (sub instanceof RubyString) {
  2345.             pos = StringSupport.index(this, this.value, this.strLength(this.checkEncoding((RubyString) sub)), (RubyString) sub, ((RubyString) sub).value, ((RubyString) sub).strLength(this.checkEncoding((RubyString) sub)), pos, this.checkEncoding((RubyString) sub));
  2346.             pos = subLength(pos);
  2347.         } else {
  2348.             IRubyObject tmp = sub.checkStringType();
  2349.             if (tmp.isNil()) throw runtime.newTypeError("type mismatch: " + sub.getMetaClass().getName() + " given");
  2350.             pos = StringSupport.index(this, this.value, this.strLength(this.checkEncoding((RubyString) tmp)), (RubyString) tmp, ((RubyString) tmp).value, ((RubyString) tmp).strLength(this.checkEncoding((RubyString) tmp)), pos, this.checkEncoding((RubyString) tmp));
  2351.             pos = subLength(pos);
  2352.         }

  2353.         return pos == -1 ? runtime.getNil() : RubyFixnum.newFixnum(runtime, pos);
  2354.     }

  2355.     /** rb_str_rindex_m
  2356.      *
  2357.      */
  2358.     public IRubyObject rindex(ThreadContext context, IRubyObject arg0) {
  2359.         return rindex19(context, arg0);
  2360.     }

  2361.     public IRubyObject rindex(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
  2362.         return rindex19(context, arg0, arg1);
  2363.     }

  2364.     @JRubyMethod(name = "rindex", reads = BACKREF, writes = BACKREF)
  2365.     public IRubyObject rindex19(ThreadContext context, IRubyObject arg0) {
  2366.         return rindexCommon19(context.runtime, context, arg0, strLength());
  2367.     }

  2368.     @JRubyMethod(name = "rindex", reads = BACKREF, writes = BACKREF)
  2369.     public IRubyObject rindex19(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
  2370.         int pos = RubyNumeric.num2int(arg1);
  2371.         Ruby runtime = context.runtime;
  2372.         int length = strLength();
  2373.         if (pos < 0) {
  2374.             pos += length;
  2375.             if (pos < 0) {
  2376.                 if (arg0 instanceof RubyRegexp) context.setBackRef(runtime.getNil());
  2377.                 return runtime.getNil();
  2378.             }
  2379.         }
  2380.         if (pos > length) pos = length;
  2381.         return rindexCommon19(runtime, context, arg0, pos);
  2382.     }

  2383.     private IRubyObject rindexCommon19(Ruby runtime, ThreadContext context, final IRubyObject sub, int pos) {
  2384.         if (sub instanceof RubyRegexp) {
  2385.             RubyRegexp regSub = (RubyRegexp) sub;
  2386.             pos = singleByteOptimizable() ? pos :
  2387.                     StringSupport.nth(value.getEncoding(), value.getUnsafeBytes(), value.getBegin(),
  2388.                             value.getBegin() + value.getRealSize(),
  2389.                                       pos) - value.getBegin();
  2390.             if (regSub.length() > 0) {
  2391.                 pos = regSub.adjustStartPos19(this, pos, true);
  2392.                 IRubyObject[] holder = {context.nil};
  2393.                 pos = regSub.search19(context, this, pos, true, holder);
  2394.                 context.setBackRef(holder[0]);
  2395.                 pos = subLength(pos);
  2396.             }
  2397.         } else if (sub instanceof RubyString) {
  2398.             pos = StringSupport.rindex(value, this.strLength(this.checkEncoding((RubyString) sub)), ((RubyString) sub).value, ((RubyString) sub).strLength(this.checkEncoding((RubyString) sub)), pos, (RubyString) sub, this.checkEncoding((RubyString) sub));
  2399.         } else {
  2400.             IRubyObject tmp = sub.checkStringType();
  2401.             if (tmp.isNil()) throw runtime.newTypeError("type mismatch: " + sub.getMetaClass().getName() + " given");
  2402.             pos = StringSupport.rindex(value, this.strLength(this.checkEncoding((RubyString) tmp)), ((RubyString) tmp).value, ((RubyString) tmp).strLength(this.checkEncoding((RubyString) tmp)), pos, (RubyString) tmp, this.checkEncoding((RubyString) tmp));
  2403.         }
  2404.         if (pos >= 0) return RubyFixnum.newFixnum(runtime, pos);
  2405.         return runtime.getNil();
  2406.     }

  2407.     @Deprecated
  2408.     public final IRubyObject substr(int beg, int len) {
  2409.         return substr(getRuntime(), beg, len);
  2410.     }

  2411.     /* rb_str_substr */
  2412.     public final IRubyObject substr(Ruby runtime, int beg, int len) {
  2413.         int length = value.length();
  2414.         if (len < 0 || beg > length) return runtime.getNil();

  2415.         if (beg < 0) {
  2416.             beg += length;
  2417.             if (beg < 0) return runtime.getNil();
  2418.         }

  2419.         int end = Math.min(length, beg + len);
  2420.         return makeShared19(runtime, beg, end - beg);
  2421.     }

  2422.     /* str_byte_substr */
  2423.     private IRubyObject byteSubstr(Ruby runtime, int beg, int len) {
  2424.         int length = value.length();

  2425.         if (len < 0 || beg > length) return runtime.getNil();

  2426.         if (beg < 0) {
  2427.             beg += length;
  2428.             if (beg < 0) return runtime.getNil();
  2429.         }
  2430.         if (beg + len > length) len = length - beg;

  2431.         if (len <= 0) {
  2432.             len = 0;
  2433.         }

  2434.         return makeShared19(runtime, beg, len);
  2435.     }

  2436.     /* str_byte_aref */
  2437.     private IRubyObject byteARef(Ruby runtime, IRubyObject idx) {
  2438.         final int index;

  2439.         if (idx instanceof RubyRange){
  2440.             int[] begLen = ((RubyRange) idx).begLenInt(getByteList().length(), 0);
  2441.             return begLen == null ? runtime.getNil() : byteSubstr(runtime, begLen[0], begLen[1]);
  2442.         } else if (idx instanceof RubyFixnum) {
  2443.             index = RubyNumeric.fix2int((RubyFixnum)idx);
  2444.         } else if (idx.respondsTo("begin") && idx.respondsTo("end")) {
  2445.             ThreadContext context = runtime.getCurrentContext();
  2446.             IRubyObject begin = idx.callMethod(context, "begin");
  2447.             IRubyObject end   = idx.callMethod(context, "end");
  2448.             IRubyObject excl  = idx.callMethod(context, "exclude_end?");
  2449.             RubyRange range = RubyRange.newRange(context, begin, end, excl.isTrue());

  2450.             int[] begLen = range.begLenInt(getByteList().length(), 0);
  2451.             return begLen == null ? runtime.getNil() : byteSubstr(runtime, begLen[0], begLen[1]);
  2452.         } else {
  2453.             index = RubyNumeric.num2int(idx);
  2454.         }

  2455.         IRubyObject obj = byteSubstr(runtime, index, 1);
  2456.         if (obj.isNil() || ((RubyString)obj).getByteList().length() == 0) return runtime.getNil();
  2457.         return obj;
  2458.     }

  2459.     public final IRubyObject substr19(Ruby runtime, int beg, int len) {
  2460.         if (len < 0) return runtime.getNil();
  2461.         int length = value.getRealSize();
  2462.         if (length == 0) len = 0;

  2463.         Encoding enc = value.getEncoding();
  2464.         if (singleByteOptimizable(enc)) {
  2465.             if (beg > length) return runtime.getNil();
  2466.             if (beg < 0) {
  2467.                 beg += length;
  2468.                 if (beg < 0) return runtime.getNil();
  2469.             }
  2470.             if (beg + len > length) len = length - beg;
  2471.             if (len <= 0) len = beg = 0;
  2472.             return makeShared19(runtime, beg, len);
  2473.         } else {
  2474.             if (beg + len > length) len = length - beg;
  2475.             return multibyteSubstr19(runtime, enc, len, beg, length);
  2476.         }
  2477.     }

  2478.     private IRubyObject multibyteSubstr19(Ruby runtime, Encoding enc, int len, int beg, int length) {
  2479.         int p;
  2480.         int s = value.getBegin();
  2481.         int end = s + length;
  2482.         byte[]bytes = value.getUnsafeBytes();

  2483.         if (beg < 0) {
  2484.             if (len > -beg) len = -beg;
  2485.             if (-beg * enc.maxLength() < length >>> 3) {
  2486.                 beg = -beg;
  2487.                 int e = end;
  2488.                 while (beg-- > len && (e = enc.prevCharHead(bytes, s, e, e)) != -1) {} // nothing
  2489.                 p = e;
  2490.                 if (p == -1) return runtime.getNil();
  2491.                 while (len-- > 0 && (p = enc.prevCharHead(bytes, s, p, e)) != -1) {} // nothing
  2492.                 if (p == -1) return runtime.getNil();
  2493.                 return makeShared19(runtime, p - s, e - p);
  2494.             } else {
  2495.                 beg += strLength(enc);
  2496.                 if (beg < 0) return runtime.getNil();
  2497.             }
  2498.         } else if (beg > 0 && beg > strLength(enc)) {
  2499.             return runtime.getNil();
  2500.         }
  2501.         if (len == 0) {
  2502.             p = 0;
  2503.         } else if (isCodeRangeValid() && enc instanceof UTF8Encoding) {
  2504.             p = StringSupport.utf8Nth(bytes, s, end, beg);
  2505.             len = StringSupport.utf8Offset(bytes, p, end, len);
  2506.         } else if (enc.isFixedWidth()) {
  2507.             int w = enc.maxLength();
  2508.             p = s + beg * w;
  2509.             if (p > end) {
  2510.                 p = end;
  2511.                 len = 0;
  2512.             } else if (len * w > end - p) {
  2513.                 len = end - p;
  2514.             } else {
  2515.                 len *= w;
  2516.             }
  2517.         } else if ((p = StringSupport.nth(enc, bytes, s, end, beg)) == end) {
  2518.             len = 0;
  2519.         } else {
  2520.             len = StringSupport.offset(enc, bytes, p, end, len);
  2521.         }
  2522.         return makeShared19(runtime, p - s, len);
  2523.     }

  2524.     /* rb_str_splice */
  2525.     private IRubyObject replaceInternal(int beg, int len, RubyString repl) {
  2526.         int oldLength = value.getRealSize();
  2527.         if (beg + len >= oldLength) len = oldLength - beg;
  2528.         ByteList replBytes = repl.value;
  2529.         int replLength = replBytes.getRealSize();
  2530.         int newLength = oldLength + replLength - len;

  2531.         byte[]oldBytes = value.getUnsafeBytes();
  2532.         int oldBegin = value.getBegin();

  2533.         modify(newLength);
  2534.         if (replLength != len) {
  2535.             System.arraycopy(oldBytes, oldBegin + beg + len, value.getUnsafeBytes(), beg + replLength, oldLength - (beg + len));
  2536.         }

  2537.         if (replLength > 0) System.arraycopy(replBytes.getUnsafeBytes(), replBytes.getBegin(), value.getUnsafeBytes(), beg, replLength);
  2538.         value.setRealSize(newLength);
  2539.         return infectBy(repl);
  2540.     }

  2541.     private void replaceInternal19(int beg, int len, RubyString repl) {
  2542.         Encoding enc = checkEncoding(repl);
  2543.         int p = value.getBegin();
  2544.         int e;
  2545.         if (singleByteOptimizable()) {
  2546.             p += beg;
  2547.             e = p + len;
  2548.         } else {
  2549.             int end = p + value.getRealSize();
  2550.             byte[]bytes = value.getUnsafeBytes();
  2551.             p = StringSupport.nth(enc, bytes, p, end, beg);
  2552.             if (p == -1) p = end;
  2553.             e = StringSupport.nth(enc, bytes, p, end, len);
  2554.             if (e == -1) e = end;
  2555.         }

  2556.         int cr = getCodeRange();
  2557.         if (cr == CR_BROKEN) clearCodeRange();
  2558.         replaceInternal(p - value.getBegin(), e - p, repl);
  2559.         associateEncoding(enc);
  2560.         cr = codeRangeAnd(cr, repl.getCodeRange());
  2561.         if (cr != CR_BROKEN) setCodeRange(cr);
  2562.     }

  2563.     /** rb_str_aref, rb_str_aref_m
  2564.      *
  2565.      */
  2566.     public IRubyObject op_aref(ThreadContext context, IRubyObject arg1, IRubyObject arg2) {
  2567.         return op_aref19(context, arg1, arg2);
  2568.     }

  2569.     public IRubyObject op_aref(ThreadContext context, IRubyObject arg) {
  2570.         return op_aref19(context, arg);
  2571.     }

  2572.     @JRubyMethod(name = {"[]", "slice"}, reads = BACKREF, writes = BACKREF)
  2573.     public IRubyObject op_aref19(ThreadContext context, IRubyObject arg1, IRubyObject arg2) {
  2574.         Ruby runtime = context.runtime;
  2575.         if (arg1 instanceof RubyRegexp) return subpat19(runtime, context, (RubyRegexp)arg1, arg2);
  2576.         return substr19(runtime, RubyNumeric.num2int(arg1), RubyNumeric.num2int(arg2));
  2577.     }

  2578.     @JRubyMethod(name = {"[]", "slice"}, reads = BACKREF, writes = BACKREF)
  2579.     public IRubyObject op_aref19(ThreadContext context, IRubyObject arg) {
  2580.         Ruby runtime = context.runtime;
  2581.         if (arg instanceof RubyFixnum) {
  2582.             return op_aref19(runtime, RubyNumeric.fix2int((RubyFixnum)arg));
  2583.         } else if (arg instanceof RubyRegexp) {
  2584.             return subpat19(runtime, context, (RubyRegexp)arg);
  2585.         } else if (arg instanceof RubyString) {
  2586.             RubyString str = (RubyString)arg;
  2587.             return StringSupport.index(this, this.value, this.strLength(this.checkEncoding(str)), str, str.value, str.strLength(this.checkEncoding(str)), 0, this.checkEncoding(str)) != -1 ? str.strDup(runtime) : runtime.getNil();
  2588.         } else if (arg instanceof RubyRange) {
  2589.             int len = strLength();
  2590.             int[] begLen = ((RubyRange) arg).begLenInt(len, 0);
  2591.             return begLen == null ? runtime.getNil() : substr19(runtime, begLen[0], begLen[1]);
  2592.         } else if (arg.respondsTo("begin") && arg.respondsTo("end")) {
  2593.             int len = strLength();
  2594.             IRubyObject begin = arg.callMethod(context, "begin");
  2595.             IRubyObject end   = arg.callMethod(context, "end");
  2596.             IRubyObject excl  = arg.callMethod(context, "exclude_end?");
  2597.             RubyRange range = RubyRange.newRange(context, begin, end, excl.isTrue());

  2598.             int[] begLen = range.begLenInt(len, 0);
  2599.             return begLen == null ? runtime.getNil() : substr19(runtime, begLen[0], begLen[1]);
  2600.         }
  2601.         return op_aref19(runtime, RubyNumeric.num2int(arg));
  2602.     }

  2603.     @JRubyMethod
  2604.     public IRubyObject byteslice(ThreadContext context, IRubyObject arg1, IRubyObject arg2) {
  2605.         return byteSubstr(context.runtime, RubyNumeric.num2int(arg1), RubyNumeric.num2int(arg2));
  2606.     }

  2607.     @JRubyMethod
  2608.     public IRubyObject byteslice(ThreadContext context, IRubyObject arg) {
  2609.         return byteARef(context.runtime, arg);
  2610.     }

  2611.     private IRubyObject op_aref19(Ruby runtime, int idx) {
  2612.         IRubyObject str = substr19(runtime, idx, 1);
  2613.         return !str.isNil() && ((RubyString) str).value.getRealSize() == 0 ? runtime.getNil() : str;
  2614.     }

  2615.     private int subpatSetCheck(Ruby runtime, int nth, Region regs) {
  2616.         int numRegs = regs == null ? 1 : regs.numRegs;
  2617.         if (nth < numRegs) {
  2618.             if (nth < 0) {
  2619.                 if (-nth < numRegs) return nth + numRegs;
  2620.             } else {
  2621.                 return nth;
  2622.             }
  2623.         }
  2624.         throw runtime.newIndexError("index " + nth + " out of regexp");
  2625.     }

  2626.     private void subpatSet19(ThreadContext context, RubyRegexp regexp, IRubyObject backref, IRubyObject repl) {
  2627.         Ruby runtime = context.runtime;
  2628.         IRubyObject[] holder = {context.nil};

  2629.         int result = regexp.search19(context, this, 0, false, holder);
  2630.         context.setBackRef(holder[0]);

  2631.         if (result < 0) throw runtime.newIndexError("regexp not matched");

  2632.         // this cast should be ok, since nil matchdata will be < 0 above
  2633.         RubyMatchData match = (RubyMatchData)holder[0];

  2634.         int nth = backref == null ? 0 : subpatSetCheck(runtime, match.backrefNumber(backref), match.regs);

  2635.         final int start, end;
  2636.         if (match.regs == null) {
  2637.             start = match.begin;
  2638.             end = match.end;
  2639.         } else {
  2640.             start = match.regs.beg[nth];
  2641.             end = match.regs.end[nth];
  2642.         }
  2643.         if (start == -1) throw runtime.newIndexError("regexp group " + nth + " not matched");
  2644.         RubyString replStr =  repl.convertToString();
  2645.         Encoding enc = checkEncoding(replStr);
  2646.         // TODO: keep cr
  2647.         replaceInternal(start, end - start, replStr); // TODO: rb_str_splice_0
  2648.         associateEncoding(enc);
  2649.     }

  2650.     private IRubyObject subpat19(Ruby runtime, ThreadContext context, RubyRegexp regex, IRubyObject backref) {
  2651.         IRubyObject[] holder = {context.nil};

  2652.         int result = regex.search19(context, this, 0, false, holder);
  2653.         context.setBackRef(holder[0]);

  2654.         if (result >= 0) {
  2655.             RubyMatchData match = (RubyMatchData)holder[0];
  2656.             return RubyRegexp.nth_match(match.backrefNumber(backref), match);
  2657.         }

  2658.         return runtime.getNil();
  2659.     }

  2660.     private IRubyObject subpat19(Ruby runtime, ThreadContext context, RubyRegexp regex) {
  2661.         IRubyObject[] holder = {context.nil};

  2662.         int result = regex.search19(context, this, 0, false, holder);
  2663.         context.setBackRef(holder[0]);

  2664.         if (result >= 0) {
  2665.             return RubyRegexp.nth_match(0, holder[0]);
  2666.         }

  2667.         return runtime.getNil();
  2668.     }

  2669.     /** rb_str_aset, rb_str_aset_m
  2670.      *
  2671.      */
  2672.     public IRubyObject op_aset(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
  2673.         return op_aset19(context, arg0, arg1);
  2674.     }

  2675.     public IRubyObject op_aset(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
  2676.         return op_aset19(context, arg0, arg1, arg2);
  2677.     }

  2678.     @JRubyMethod(name = "[]=", reads = BACKREF)
  2679.     public IRubyObject op_aset19(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
  2680.         if (arg0 instanceof RubyFixnum) {
  2681.             return op_aset19(context, RubyNumeric.fix2int((RubyFixnum)arg0), arg1);
  2682.         } else if (arg0 instanceof RubyRegexp) {
  2683.             subpatSet19(context, (RubyRegexp)arg0, null, arg1);
  2684.             return arg1;
  2685.         } else if (arg0 instanceof RubyString) {
  2686.             RubyString orig = (RubyString)arg0;
  2687.             int beg = StringSupport.index(this, this.value, this.strLength(this.checkEncoding(orig)), orig, orig.value, orig.strLength(this.checkEncoding(orig)), 0, this.checkEncoding(orig));
  2688.             if (beg < 0) throw context.runtime.newIndexError("string not matched");
  2689.             beg = subLength(beg);
  2690.             replaceInternal19(beg, orig.strLength(), arg1.convertToString());
  2691.             return arg1;
  2692.         } else if (arg0 instanceof RubyRange) {
  2693.             int[] begLen = ((RubyRange) arg0).begLenInt(strLength(), 2);
  2694.             replaceInternal19(begLen[0], begLen[1], arg1.convertToString());
  2695.             return arg1;
  2696.         } else if (arg0.respondsTo("begin") && arg0.respondsTo("end")) {
  2697.             IRubyObject begin = arg0.callMethod(context, "begin");
  2698.             IRubyObject end   = arg0.callMethod(context, "end");
  2699.             IRubyObject excl  = arg0.callMethod(context, "exclude_end?");
  2700.             RubyRange rng = RubyRange.newRange(context, begin, end, excl.isTrue());

  2701.             int[] begLen = rng.begLenInt(strLength(), 2);
  2702.             replaceInternal19(begLen[0], begLen[1], arg1.convertToString());

  2703.             return arg1;
  2704.         }
  2705.         return op_aset19(context, RubyNumeric.num2int(arg0), arg1);
  2706.     }

  2707.     private IRubyObject op_aset19(ThreadContext context, int idx, IRubyObject arg1) {
  2708.         replaceInternal19(checkIndex(idx, strLength()), 1, arg1.convertToString());
  2709.         return arg1;
  2710.     }

  2711.     @JRubyMethod(name = "[]=", reads = BACKREF)
  2712.     public IRubyObject op_aset19(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
  2713.         if (arg0 instanceof RubyRegexp) {
  2714.             subpatSet19(context, (RubyRegexp)arg0, arg1, arg2);
  2715.         } else {
  2716.             int beg = RubyNumeric.num2int(arg0);
  2717.             int len = RubyNumeric.num2int(arg1);
  2718.             checkLength(len);
  2719.             RubyString repl = arg2.convertToString();
  2720.             replaceInternal19(checkIndex(beg, strLength()), len, repl);
  2721.         }
  2722.         return arg2;
  2723.     }

  2724.     /** rb_str_slice_bang
  2725.      *
  2726.      */
  2727.     public IRubyObject slice_bang(ThreadContext context, IRubyObject arg0) {
  2728.         return slice_bang19(context, arg0);
  2729.     }

  2730.     public IRubyObject slice_bang(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
  2731.         return slice_bang19(context, arg0, arg1);
  2732.     }

  2733.     @JRubyMethod(name = "slice!", reads = BACKREF, writes = BACKREF)
  2734.     public IRubyObject slice_bang19(ThreadContext context, IRubyObject arg0) {
  2735.         IRubyObject result = op_aref19(context, arg0);
  2736.         if (result.isNil()) {
  2737.             modifyCheck(); // keep cr ?
  2738.         } else {
  2739.             op_aset19(context, arg0, RubyString.newEmptyString(context.runtime));
  2740.         }
  2741.         return result;
  2742.     }

  2743.     @JRubyMethod(name = "slice!", reads = BACKREF, writes = BACKREF)
  2744.     public IRubyObject slice_bang19(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
  2745.         IRubyObject result = op_aref19(context, arg0, arg1);
  2746.         if (result.isNil()) {
  2747.             modifyCheck(); // keep cr ?
  2748.         } else {
  2749.             op_aset19(context, arg0, arg1, RubyString.newEmptyString(context.runtime));
  2750.         }
  2751.         return result;
  2752.     }

  2753.     public IRubyObject succ(ThreadContext context) {
  2754.         return succ19(context);
  2755.     }

  2756.     public IRubyObject succ_bang() {
  2757.         return succ_bang19();
  2758.     }

  2759.     @JRubyMethod(name = {"succ", "next"})
  2760.     public IRubyObject succ19(ThreadContext context) {
  2761.         Ruby runtime = context.runtime;
  2762.         final RubyString str;
  2763.         if (value.getRealSize() > 0) {
  2764.             str = new RubyString(runtime, getMetaClass(), StringSupport.succCommon(value));
  2765.             // TODO: rescan code range ?
  2766.         } else {
  2767.             str = newEmptyString(runtime, getType(), value.getEncoding());
  2768.         }
  2769.         return str.infectBy(this);
  2770.     }

  2771.     @JRubyMethod(name = {"succ!", "next!"})
  2772.     public IRubyObject succ_bang19() {
  2773.         modifyCheck();
  2774.         if (value.getRealSize() > 0) {
  2775.             value = StringSupport.succCommon(value);
  2776.             shareLevel = SHARE_LEVEL_NONE;
  2777.             // TODO: rescan code range ?
  2778.         }
  2779.         return this;
  2780.     }

  2781.     /** rb_str_upto_m
  2782.      *
  2783.      */
  2784.     @JRubyMethod(name = "upto")
  2785.     public IRubyObject upto19(ThreadContext context, IRubyObject end, Block block) {
  2786.         Ruby runtime = context.runtime;
  2787.         return block.isGiven() ? uptoCommon19(context, end, false, block) : enumeratorize(runtime, this, "upto", end);
  2788.     }

  2789.     @JRubyMethod(name = "upto")
  2790.     public IRubyObject upto19(ThreadContext context, IRubyObject end, IRubyObject excl, Block block) {
  2791.         return block.isGiven() ? uptoCommon19(context, end, excl.isTrue(), block) :
  2792.             enumeratorize(context.runtime, this, "upto", new IRubyObject[]{end, excl});
  2793.     }

  2794.     final IRubyObject uptoCommon19(ThreadContext context, IRubyObject arg, boolean excl, Block block) {
  2795.         return uptoCommon19(context, arg, excl, block, false);
  2796.     }

  2797.     final IRubyObject uptoCommon19(ThreadContext context, IRubyObject arg, boolean excl, Block block, boolean asSymbol) {
  2798.         Ruby runtime = context.runtime;
  2799.         if (arg instanceof RubySymbol) throw runtime.newTypeError("can't convert Symbol into String");

  2800.         RubyString end = arg.convertToString();
  2801.         Encoding enc = checkEncoding(end);
  2802.         boolean isAscii = scanForCodeRange() == CR_7BIT && end.scanForCodeRange() == CR_7BIT;
  2803.         if (value.getRealSize() == 1 && end.value.getRealSize() == 1 && isAscii) {
  2804.             byte c = value.getUnsafeBytes()[value.getBegin()];
  2805.             byte e = end.value.getUnsafeBytes()[end.value.getBegin()];
  2806.             if (c > e || (excl && c == e)) return this;
  2807.             while (true) {
  2808.                 RubyString s = new RubyString(runtime, runtime.getString(), RubyInteger.SINGLE_CHAR_BYTELISTS[c & 0xff],
  2809.                                                                             enc, CR_7BIT);
  2810.                 s.shareLevel = SHARE_LEVEL_BYTELIST;
  2811.                 block.yield(context, asSymbol ? runtime.newSymbol(s.toString()) : s);

  2812.                 if (!excl && c == e) break;
  2813.                 c++;
  2814.                 if (excl && c == e) break;
  2815.             }
  2816.             return this;
  2817.         } else if (isAscii && ASCII.isDigit(value.getUnsafeBytes()[value.getBegin()]) && ASCII.isDigit(end.value.getUnsafeBytes()[end.value.getBegin()])) {
  2818.             int s = value.getBegin();
  2819.             int send = s + value.getRealSize();
  2820.             byte[]bytes = value.getUnsafeBytes();

  2821.             while (s < send) {
  2822.                 if (!ASCII.isDigit(bytes[s] & 0xff)) return uptoCommon19NoDigits(context, end, excl, block, asSymbol);
  2823.                 s++;
  2824.             }
  2825.             s = end.value.getBegin();
  2826.             send = s + end.value.getRealSize();
  2827.             bytes = end.value.getUnsafeBytes();

  2828.             while (s < send) {
  2829.                 if (!ASCII.isDigit(bytes[s] & 0xff)) return uptoCommon19NoDigits(context, end, excl, block, asSymbol);
  2830.                 s++;
  2831.             }

  2832.             IRubyObject b = stringToInum19(10, false);
  2833.             IRubyObject e = end.stringToInum19(10, false);

  2834.             IRubyObject[]args = new IRubyObject[2];
  2835.             args[0] = RubyFixnum.newFixnum(runtime, value.length());
  2836.             RubyArray argsArr = runtime.newArrayNoCopy(args);

  2837.             if (b instanceof RubyFixnum && e instanceof RubyFixnum) {
  2838.                 int bi = RubyNumeric.fix2int(b);
  2839.                 int ei = RubyNumeric.fix2int(e);

  2840.                 while (bi <= ei) {
  2841.                     if (excl && bi == ei) break;
  2842.                     args[1] = RubyFixnum.newFixnum(runtime, bi);
  2843.                     ByteList to = new ByteList(value.length() + 5);
  2844.                     Sprintf.sprintf(to, "%.*d", argsArr);
  2845.                     RubyString str = RubyString.newStringNoCopy(runtime, to, USASCIIEncoding.INSTANCE, CR_7BIT);
  2846.                     block.yield(context, asSymbol ? runtime.newSymbol(str.toString()) : str);
  2847.                     bi++;
  2848.                 }
  2849.             } else {
  2850.                 String op = excl ? "<" : "<=";

  2851.                 while (b.callMethod(context, op, e).isTrue()) {
  2852.                     args[1] = b;
  2853.                     ByteList to = new ByteList(value.length() + 5);
  2854.                     Sprintf.sprintf(to, "%.*d", argsArr);
  2855.                     RubyString str = RubyString.newStringNoCopy(runtime, to, USASCIIEncoding.INSTANCE, CR_7BIT);
  2856.                     block.yield(context, asSymbol ? runtime.newSymbol(str.toString()) : str);
  2857.                     b = b.callMethod(context, "succ");
  2858.                 }
  2859.             }
  2860.             return this;
  2861.         }

  2862.         return uptoCommon19NoDigits(context, end, excl, block, asSymbol);
  2863.     }

  2864.     private IRubyObject uptoCommon19NoDigits(ThreadContext context, RubyString end, boolean excl, Block block, boolean asSymbol) {
  2865.         Ruby runtime = context.runtime;
  2866.         int n = op_cmp(end);
  2867.         if (n > 0 || (excl && n == 0)) return this;
  2868.         IRubyObject afterEnd = end.callMethod(context, "succ");
  2869.         RubyString current = strDup(context.runtime);

  2870.         while (!current.op_equal19(context, afterEnd).isTrue()) {
  2871.             IRubyObject next = null;
  2872.             if (excl || !current.op_equal19(context, end).isTrue()) next = current.callMethod(context, "succ");
  2873.             block.yield(context, asSymbol ? runtime.newSymbol(current.toString()) : current);
  2874.             if (next == null) break;
  2875.             current = next.convertToString();
  2876.             if (excl && current.op_equal19(context, end).isTrue()) break;
  2877.             if (current.getByteList().length() > end.getByteList().length() || current.getByteList().length() == 0) break;
  2878.         }
  2879.         return this;
  2880.     }

  2881.     /** rb_str_include
  2882.      *
  2883.      */
  2884.     public RubyBoolean include_p(ThreadContext context, IRubyObject obj) {
  2885.         return include_p19(context, obj);
  2886.     }

  2887.     @JRubyMethod(name = "include?")
  2888.     public RubyBoolean include_p19(ThreadContext context, IRubyObject obj) {
  2889.         Ruby runtime = context.runtime;
  2890.         RubyString coerced = obj.convertToString();
  2891.         return StringSupport.index(this, this.value, this.strLength(this.checkEncoding(coerced)), coerced, coerced.value, coerced.strLength(this.checkEncoding(coerced)), 0, this.checkEncoding(coerced)) == -1 ? runtime.getFalse() : runtime.getTrue();
  2892.     }

  2893.     @JRubyMethod
  2894.     public IRubyObject chr(ThreadContext context) {
  2895.         return substr19(context.runtime, 0, 1);
  2896.     }

  2897.     @JRubyMethod
  2898.     public IRubyObject getbyte(ThreadContext context, IRubyObject index) {
  2899.         Ruby runtime = context.runtime;
  2900.         int i = RubyNumeric.num2int(index);
  2901.         if (i < 0) i += value.getRealSize();
  2902.         if (i < 0 || i >= value.getRealSize()) return runtime.getNil();
  2903.         return RubyFixnum.newFixnum(runtime, value.getUnsafeBytes()[value.getBegin() + i] & 0xff);
  2904.     }

  2905.     @JRubyMethod
  2906.     public IRubyObject setbyte(ThreadContext context, IRubyObject index, IRubyObject val) {
  2907.         modifyAndKeepCodeRange();
  2908.         int i = RubyNumeric.num2int(index);
  2909.         int b = RubyNumeric.num2int(val);
  2910.         value.getUnsafeBytes()[checkIndexForRef(i, value.getRealSize())] = (byte)b;
  2911.         return val;
  2912.     }

  2913.     /** rb_str_to_i
  2914.      *
  2915.      */
  2916.     public IRubyObject to_i() {
  2917.         return to_i19();
  2918.     }

  2919.     /** rb_str_to_i
  2920.      *
  2921.      */
  2922.     public IRubyObject to_i(IRubyObject arg0) {
  2923.         return to_i19(arg0);
  2924.     }

  2925.     @JRubyMethod(name = "to_i")
  2926.     public IRubyObject to_i19() {
  2927.         return stringToInum19(10, false);
  2928.     }

  2929.     @JRubyMethod(name = "to_i")
  2930.     public IRubyObject to_i19(IRubyObject arg0) {
  2931.         long base = checkBase(arg0);
  2932.         return stringToInum19((int)base, false);
  2933.     }

  2934.     private long checkBase(IRubyObject arg0) {
  2935.         long base = arg0.convertToInteger().getLongValue();
  2936.         if(base < 0) {
  2937.             throw getRuntime().newArgumentError("illegal radix " + base);
  2938.         }
  2939.         return base;
  2940.     }

  2941.     /** rb_str_to_inum
  2942.      *
  2943.      */
  2944.     public IRubyObject stringToInum(int base, boolean badcheck) {
  2945.         ByteList s = this.value;
  2946.         return ConvertBytes.byteListToInum(getRuntime(), s, base, badcheck);
  2947.     }

  2948.     public IRubyObject stringToInum19(int base, boolean badcheck) {
  2949.         ByteList s = this.value;
  2950.         return ConvertBytes.byteListToInum19(getRuntime(), s, base, badcheck);
  2951.     }

  2952.     /** rb_str_oct
  2953.      *
  2954.      */
  2955.     public IRubyObject oct(ThreadContext context) {
  2956.         return oct19(context);
  2957.     }

  2958.     @JRubyMethod(name = "oct")
  2959.     public IRubyObject oct19(ThreadContext context) {
  2960.         if (!value.getEncoding().isAsciiCompatible()) {
  2961.             throw context.runtime.newEncodingCompatibilityError("ASCII incompatible encoding: " + value.getEncoding());
  2962.         }
  2963.         return stringToInum(-8, false);
  2964.     }

  2965.     /** rb_str_hex
  2966.      *
  2967.      */
  2968.     public IRubyObject hex(ThreadContext context) {
  2969.         return hex19(context);
  2970.     }

  2971.     @JRubyMethod(name = "hex")
  2972.     public IRubyObject hex19(ThreadContext context) {
  2973.         if (!value.getEncoding().isAsciiCompatible()) {
  2974.             throw context.runtime.newEncodingCompatibilityError("ASCII incompatible encoding: " + value.getEncoding());
  2975.         }
  2976.         return stringToInum19(16, false);
  2977.     }

  2978.     /** rb_str_to_f
  2979.      *
  2980.      */
  2981.     public IRubyObject to_f() {
  2982.         return to_f19();
  2983.     }

  2984.     @JRubyMethod(name = "to_f")
  2985.     public IRubyObject to_f19() {
  2986.         return RubyNumeric.str2fnum19(getRuntime(), this, false);
  2987.     }

  2988.     /** rb_str_split_m
  2989.      *
  2990.      */
  2991.     public RubyArray split(ThreadContext context) {
  2992.         return split19(context);
  2993.     }

  2994.     public RubyArray split(ThreadContext context, IRubyObject arg0) {
  2995.         return split19(context, arg0);
  2996.     }

  2997.     public RubyArray split(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
  2998.         return split19(context, arg0, arg1);
  2999.     }

  3000.     private void populateCapturesForSplit(Ruby runtime, RubyArray result, Matcher matcher, boolean is19) {
  3001.         Region region = matcher.getRegion();
  3002.         for (int i = 1; i < region.numRegs; i++) {
  3003.             int beg = region.beg[i];
  3004.             if (beg == -1) continue;
  3005.             result.append(is19 ? makeShared19(runtime, beg, region.end[i] - beg) : makeShared(runtime, beg, region.end[i] - beg));
  3006.         }
  3007.     }

  3008.     @JRubyMethod(name = "split", writes = BACKREF)
  3009.     public RubyArray split19(ThreadContext context) {
  3010.         return split19(context, context.runtime.getNil());
  3011.     }

  3012.     @JRubyMethod(name = "split", writes = BACKREF)
  3013.     public RubyArray split19(ThreadContext context, IRubyObject arg0) {
  3014.         return splitCommon19(arg0, false, 0, 0, context, true);
  3015.     }

  3016.     @JRubyMethod(name = "split", writes = BACKREF)
  3017.     public RubyArray split19(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
  3018.         final int lim = RubyNumeric.num2int(arg1);
  3019.         if (lim <= 0) {
  3020.             return splitCommon19(arg0, false, lim, 1, context, true);
  3021.         } else {
  3022.             if (lim == 1) return value.getRealSize() == 0 ? context.runtime.newArray() : context.runtime.newArray(this);
  3023.             return splitCommon19(arg0, true, lim, 1, context, true);
  3024.         }
  3025.     }

  3026.     public RubyArray split19(ThreadContext context, IRubyObject arg0, boolean useBackref) {
  3027.         return splitCommon19(arg0, useBackref, flags, flags, context, useBackref);
  3028.     }

  3029.     private RubyArray splitCommon19(IRubyObject spat, final boolean limit, final int lim, final int i, ThreadContext context, boolean useBackref) {
  3030.         final RubyArray result;
  3031.         if (spat.isNil() && (spat = context.runtime.getGlobalVariables().get("$;")).isNil()) {
  3032.             result = awkSplit19(limit, lim, i);
  3033.         } else {
  3034.             if (spat instanceof RubyString) {
  3035.                 ByteList spatValue = ((RubyString)spat).value;
  3036.                 int len = spatValue.getRealSize();
  3037.                 Encoding spatEnc = spatValue.getEncoding();
  3038.                 if (len == 0) {
  3039.                     Regex pattern = RubyRegexp.getRegexpFromCache(context.runtime, spatValue, spatEnc, new RegexpOptions());
  3040.                     result = regexSplit19(context, pattern, pattern, limit, lim, i, useBackref);
  3041.                 } else {
  3042.                     final int c;
  3043.                     byte[]bytes = spatValue.getUnsafeBytes();
  3044.                     int p = spatValue.getBegin();
  3045.                     if (spatEnc.isAsciiCompatible()) {
  3046.                         c = len == 1 ? bytes[p] & 0xff : -1;
  3047.                     } else {
  3048.                         c = len == StringSupport.preciseLength(spatEnc, bytes, p, p + len) ? spatEnc.mbcToCode(bytes, p, p + len) : -1;
  3049.                     }
  3050.                     result = c == ' ' ? awkSplit19(limit, lim, i) : stringSplit19(context, (RubyString)spat, limit, lim, i);
  3051.                 }
  3052.             } else {
  3053.                 final Regex pattern, prepared;

  3054.                 Ruby runtime = context.runtime;
  3055.                 if (spat instanceof RubyRegexp) {
  3056.                     RubyRegexp regexp = (RubyRegexp)spat;
  3057.                     pattern = regexp.getPattern();
  3058.                     prepared = regexp.preparePattern(this);
  3059.                 } else {
  3060.                     pattern = getStringPattern19(runtime, spat);
  3061.                     prepared = RubyRegexp.preparePattern(runtime, pattern, this);
  3062.                 }
  3063.                 result = regexSplit19(context, pattern, prepared, limit, lim, i, useBackref);
  3064.             }
  3065.         }

  3066.         if (!limit && lim == 0) {
  3067.             while (result.size() > 0 && ((RubyString) result.eltInternal(result.size() - 1)).value.getRealSize() == 0) {
  3068.                 result.pop(context);
  3069.             }
  3070.         }

  3071.         return result;
  3072.     }

  3073.     private RubyArray regexSplit19(ThreadContext context, Regex pattern, Regex prepared, boolean limit, int lim, int i, boolean useBackref) {
  3074.         Ruby runtime = context.runtime;

  3075.         int begin = value.getBegin();
  3076.         int len = value.getRealSize();
  3077.         int range = begin + len;
  3078.         byte[]bytes = value.getUnsafeBytes();

  3079.         final Matcher matcher = prepared.matcher(bytes, begin, range);

  3080.         RubyArray result = runtime.newArray();
  3081.         Encoding enc = value.getEncoding();
  3082.         boolean captures = pattern.numberOfCaptures() != 0;

  3083.         int end, beg = 0;
  3084.         boolean lastNull = false;
  3085.         int start = begin;
  3086.         while ((end = RubyRegexp.matcherSearch(runtime, matcher, start, range, Option.NONE)) >= 0) {
  3087.             if (start == end + begin && matcher.getBegin() == matcher.getEnd()) {
  3088.                 if (len == 0) {
  3089.                     result.append(newEmptyString(runtime, getMetaClass()).infectBy(this));
  3090.                     break;
  3091.                 } else if (lastNull) {
  3092.                     result.append(makeShared19(runtime, beg, StringSupport.length(enc, bytes, begin + beg, range)));
  3093.                     beg = start - begin;
  3094.                 } else {
  3095.                     start += start == range ? 1 : StringSupport.length(enc, bytes, start, range);
  3096.                     lastNull = true;
  3097.                     continue;
  3098.                 }
  3099.             } else {
  3100.                 result.append(makeShared19(runtime, beg, end - beg));
  3101.                 beg = matcher.getEnd();
  3102.                 start = begin + beg;
  3103.             }
  3104.             lastNull = false;

  3105.             if (captures) populateCapturesForSplit(runtime, result, matcher, true);
  3106.             if (limit && lim <= ++i) break;
  3107.         }

  3108.         // only this case affects backrefs
  3109.         if (useBackref) context.setBackRef(runtime.getNil());

  3110.         if (len > 0 && (limit || len > beg || lim < 0)) result.append(makeShared19(runtime, beg, len - beg));
  3111.         return result;
  3112.     }

  3113.     private RubyArray awkSplit19(boolean limit, int lim, int i) {
  3114.         Ruby runtime = getRuntime();
  3115.         RubyArray result = runtime.newArray();

  3116.         byte[]bytes = value.getUnsafeBytes();
  3117.         int p = value.getBegin();
  3118.         int ptr = p;
  3119.         int len = value.getRealSize();
  3120.         int end = p + len;
  3121.         Encoding enc = value.getEncoding();
  3122.         boolean skip = true;

  3123.         int e = 0, b = 0;
  3124.         boolean singlebyte = singleByteOptimizable(enc);
  3125.         while (p < end) {
  3126.             final int c;
  3127.             if (singlebyte) {
  3128.                 c = bytes[p++] & 0xff;
  3129.             } else {
  3130.                 c = StringSupport.codePoint(runtime, enc, bytes, p, end);
  3131.                 p += StringSupport.length(enc, bytes, p, end);
  3132.             }

  3133.             if (skip) {
  3134.                 if (enc.isSpace(c)) {
  3135.                     b = p - ptr;
  3136.                 } else {
  3137.                     e = p - ptr;
  3138.                     skip = false;
  3139.                     if (limit && lim <= i) break;
  3140.                 }
  3141.             } else {
  3142.                 if (enc.isSpace(c)) {
  3143.                     result.append(makeShared19(runtime, b, e - b));
  3144.                     skip = true;
  3145.                     b = p - ptr;
  3146.                     if (limit) i++;
  3147.                 } else {
  3148.                     e = p - ptr;
  3149.                 }
  3150.             }
  3151.         }

  3152.         if (len > 0 && (limit || len > b || lim < 0)) result.append(makeShared19(runtime, b, len - b));
  3153.         return result;
  3154.     }

  3155.     private RubyArray stringSplit19(ThreadContext context, RubyString spat, boolean limit, int lim, int i) {
  3156.         Ruby runtime = context.runtime;
  3157.         if (scanForCodeRange() == CR_BROKEN) throw runtime.newArgumentError("invalid byte sequence in " + value.getEncoding());
  3158.         if (spat.scanForCodeRange() == CR_BROKEN) throw runtime.newArgumentError("invalid byte sequence in " + spat.value.getEncoding());

  3159.         RubyArray result = runtime.newArray();
  3160.         Encoding enc = checkEncoding(spat);
  3161.         ByteList pattern = spat.value;

  3162.         byte[] patternBytes = pattern.getUnsafeBytes();
  3163.         int patternBegin = pattern.getBegin();
  3164.         int patternRealSize = pattern.getRealSize();

  3165.         byte[] bytes = value.getUnsafeBytes();
  3166.         int begin = value.getBegin();
  3167.         int realSize = value.getRealSize();

  3168.         int e, p = 0;

  3169.         while (p < realSize && (e = indexOf(bytes, begin, realSize, patternBytes, patternBegin, patternRealSize, p)) >= 0) {
  3170.             int t = enc.rightAdjustCharHead(bytes, p + begin, e + begin, begin + realSize) - begin;
  3171.             if (t != e) {
  3172.                 p = t;
  3173.                 continue;
  3174.             }
  3175.             result.append(makeShared19(runtime, p, e - p));
  3176.             p = e + pattern.getRealSize();
  3177.             if (limit && lim <= ++i) break;
  3178.         }

  3179.         if (value.getRealSize() > 0 && (limit || value.getRealSize() > p || lim < 0)) {
  3180.             result.append(makeShared19(runtime, p, value.getRealSize() - p));
  3181.         }

  3182.         return result;
  3183.     }

  3184.     // TODO: make the ByteList version public and use it, rather than copying here
  3185.     static int indexOf(byte[] source, int sourceOffset, int sourceCount, byte[] target, int targetOffset, int targetCount, int fromIndex) {
  3186.         if (fromIndex >= sourceCount) return (targetCount == 0 ? sourceCount : -1);
  3187.         if (fromIndex < 0) fromIndex = 0;
  3188.         if (targetCount == 0) return fromIndex;

  3189.         byte first  = target[targetOffset];
  3190.         int max = sourceOffset + (sourceCount - targetCount);

  3191.         for (int i = sourceOffset + fromIndex; i <= max; i++) {
  3192.             if (source[i] != first) while (++i <= max && source[i] != first);

  3193.             if (i <= max) {
  3194.                 int j = i + 1;
  3195.                 int end = j + targetCount - 1;
  3196.                 for (int k = targetOffset + 1; j < end && source[j] == target[k]; j++, k++);

  3197.                 if (j == end) return i - sourceOffset;
  3198.             }
  3199.         }
  3200.         return -1;
  3201.     }

  3202.     private RubyString getStringForPattern(IRubyObject obj) {
  3203.         if (obj instanceof RubyString) return (RubyString)obj;
  3204.         IRubyObject val = obj.checkStringType();
  3205.         if (val.isNil()) throw getRuntime().newTypeError("wrong argument type " + obj.getMetaClass() + " (expected Regexp)");
  3206.         return (RubyString)val;
  3207.     }

  3208.     /** get_pat (used by match/match19)
  3209.      *
  3210.      */
  3211.     private RubyRegexp getPattern(IRubyObject obj) {
  3212.         if (obj instanceof RubyRegexp) return (RubyRegexp)obj;
  3213.         return RubyRegexp.newRegexp(getRuntime(), getStringForPattern(obj).value);
  3214.     }

  3215.     private Regex getStringPattern19(Ruby runtime, IRubyObject obj) {
  3216.         RubyString str = getStringForPattern(obj);
  3217.         if (str.scanForCodeRange() == CR_BROKEN) {
  3218.             throw runtime.newRegexpError("invalid multybyte character: " +
  3219.                     RubyRegexp.regexpDescription19(runtime, str.value, new RegexpOptions(), str.value.getEncoding()).toString());
  3220.         }
  3221.         if (str.value.getEncoding().isDummy()) {
  3222.             throw runtime.newArgumentError("can't make regexp with dummy encoding");
  3223.         }

  3224.         return RubyRegexp.getQuotedRegexpFromCache19(runtime, str.value, new RegexpOptions(), str.isAsciiOnly());
  3225.     }

  3226.     /** rb_str_scan
  3227.      *
  3228.      */
  3229.     public IRubyObject scan(ThreadContext context, IRubyObject arg, Block block) {
  3230.         return scan19(context, arg, block);
  3231.     }

  3232.     private IRubyObject populateCapturesForScan(Ruby runtime, Matcher matcher, int range, int tuFlags, boolean is19) {
  3233.         Region region = matcher.getRegion();
  3234.         RubyArray result = getRuntime().newArray(region.numRegs);
  3235.         for (int i=1; i<region.numRegs; i++) {
  3236.             int beg = region.beg[i];
  3237.             if (beg == -1) {
  3238.                 result.append(runtime.getNil());
  3239.             } else {
  3240.                 RubyString substr = is19 ? makeShared19(runtime, beg, region.end[i] - beg) : makeShared(runtime, beg, region.end[i] - beg);
  3241.                 substr.infectBy(tuFlags);
  3242.                 result.append(substr);
  3243.             }
  3244.         }
  3245.         return result;
  3246.     }

  3247.     @JRubyMethod(name = "scan", reads = BACKREF, writes = BACKREF)
  3248.     public IRubyObject scan19(ThreadContext context, IRubyObject arg, Block block) {
  3249.         Ruby runtime = context.runtime;
  3250.         Encoding enc = value.getEncoding();
  3251.         final Regex pattern, prepared;
  3252.         final RubyRegexp regexp;
  3253.         final int tuFlags;
  3254.         if (arg instanceof RubyRegexp) {
  3255.             regexp = (RubyRegexp)arg;
  3256.             tuFlags = regexp.flags;
  3257.             pattern = regexp.getPattern();
  3258.             prepared = regexp.preparePattern(this);
  3259.         } else {
  3260.             regexp = null;
  3261.             tuFlags = arg.isTaint() ? RubyBasicObject.TAINTED_F : 0;
  3262.             pattern = getStringPattern19(runtime, arg);
  3263.             prepared = RubyRegexp.preparePattern(runtime, pattern, this);
  3264.         }

  3265.         if (block.isGiven()) {
  3266.             return scanIter19(context, pattern, prepared, enc, block, regexp, tuFlags);
  3267.         } else {
  3268.             return scanNoIter19(context, pattern, prepared, enc, regexp, tuFlags);
  3269.         }
  3270.     }

  3271.     private IRubyObject scanIter19(ThreadContext context, Regex pattern, Regex prepared, Encoding enc, Block block, RubyRegexp regexp, int tuFlags) {
  3272.         Ruby runtime = context.runtime;
  3273.         byte[]bytes = value.getUnsafeBytes();
  3274.         int begin = value.getBegin();
  3275.         int len = value.getRealSize();
  3276.         int range = begin + len;
  3277.         final Matcher matcher = prepared.matcher(bytes, begin, range);

  3278.         int end = 0;
  3279.         RubyMatchData match = null;
  3280.         if (pattern.numberOfCaptures() == 0) {
  3281.             while (RubyRegexp.matcherSearch(runtime, matcher, begin + end, range, Option.NONE) >= 0) {
  3282.                 end = StringSupport.positionEndForScan(value, matcher, enc, begin, range);
  3283.                 match = RubyRegexp.createMatchData19(context, this, matcher, pattern);
  3284.                 match.regexp = regexp;
  3285.                 RubyString substr = makeShared19(runtime, matcher.getBegin(), matcher.getEnd() - matcher.getBegin());
  3286.                 substr.infectBy(tuFlags);
  3287.                 match.infectBy(tuFlags);
  3288.                 context.setBackRef(match);
  3289.                 block.yield(context, substr);
  3290.                 modifyCheck(bytes, len, enc);
  3291.             }
  3292.         } else {
  3293.             while (RubyRegexp.matcherSearch(runtime, matcher, begin + end, range, Option.NONE) >= 0) {
  3294.                 end = StringSupport.positionEndForScan(value, matcher, enc, begin, range);
  3295.                 match = RubyRegexp.createMatchData19(context, this, matcher, pattern);
  3296.                 match.regexp = regexp;
  3297.                 match.infectBy(tuFlags);
  3298.                 context.setBackRef(match);
  3299.                 block.yield(context, populateCapturesForScan(runtime, matcher, range, tuFlags, true));
  3300.                 modifyCheck(bytes, len, enc);
  3301.             }
  3302.         }
  3303.         context.setBackRef(match == null ? runtime.getNil() : match);
  3304.         return this;
  3305.     }

  3306.     private IRubyObject scanNoIter19(ThreadContext context, Regex pattern, Regex prepared, Encoding enc, RubyRegexp regexp, int tuFlags) {
  3307.         Ruby runtime = context.runtime;
  3308.         byte[]bytes = value.getUnsafeBytes();
  3309.         int begin = value.getBegin();
  3310.         int range = begin + value.getRealSize();
  3311.         final Matcher matcher = prepared.matcher(bytes, begin, range);

  3312.         RubyArray ary = runtime.newArray();

  3313.         int end = 0;
  3314.         if (pattern.numberOfCaptures() == 0) {
  3315.             while (RubyRegexp.matcherSearch(runtime, matcher, begin + end, range, Option.NONE) >= 0) {
  3316.                 end = StringSupport.positionEndForScan(value, matcher, enc, begin, range);
  3317.                 RubyString substr = makeShared19(runtime, matcher.getBegin(), matcher.getEnd() - matcher.getBegin());
  3318.                 substr.infectBy(tuFlags);
  3319.                 ary.append(substr);
  3320.             }
  3321.         } else {
  3322.             while (RubyRegexp.matcherSearch(runtime, matcher, begin + end, range, Option.NONE) >= 0) {
  3323.                 end = StringSupport.positionEndForScan(value, matcher, enc, begin, range);
  3324.                 ary.append(populateCapturesForScan(runtime, matcher, range, tuFlags, true));
  3325.             }
  3326.         }

  3327.         if (ary.size() > 0) {
  3328.             RubyMatchData match = RubyRegexp.createMatchData19(context, this, matcher, pattern);
  3329.             match.regexp = regexp;
  3330.             match.infectBy(tuFlags);
  3331.             context.setBackRef(match);
  3332.         } else {
  3333.             context.setBackRef(runtime.getNil());
  3334.         }
  3335.         return ary;
  3336.     }

  3337.     @JRubyMethod(name = "start_with?")
  3338.     public IRubyObject start_with_p(ThreadContext context) {
  3339.         return context.runtime.getFalse();
  3340.     }

  3341.     @JRubyMethod(name = "start_with?")
  3342.     public IRubyObject start_with_p(ThreadContext context, IRubyObject arg) {
  3343.         return start_with_pCommon(arg) ? context.runtime.getTrue() : context.runtime.getFalse();
  3344.     }

  3345.     @JRubyMethod(name = "start_with?", rest = true)
  3346.     public IRubyObject start_with_p(ThreadContext context, IRubyObject[]args) {
  3347.         for (int i = 0; i < args.length; i++) {
  3348.             if (start_with_pCommon(args[i])) return context.runtime.getTrue();
  3349.         }
  3350.         return context.runtime.getFalse();
  3351.     }

  3352.     private boolean start_with_pCommon(IRubyObject arg) {
  3353.         IRubyObject tmp = arg.checkStringType();
  3354.         if (tmp.isNil()) return false;
  3355.         RubyString otherString = (RubyString)tmp;
  3356.         checkEncoding(otherString);
  3357.         if (value.getRealSize() < otherString.value.getRealSize()) return false;
  3358.         return value.startsWith(otherString.value);
  3359.     }

  3360.     @JRubyMethod(name = "end_with?")
  3361.     public IRubyObject end_with_p(ThreadContext context) {
  3362.         return context.runtime.getFalse();
  3363.     }

  3364.     @JRubyMethod(name = "end_with?")
  3365.     public IRubyObject end_with_p(ThreadContext context, IRubyObject arg) {
  3366.         return end_with_pCommon(arg) ? context.runtime.getTrue() : context.runtime.getFalse();
  3367.     }

  3368.     @JRubyMethod(name = "end_with?", rest = true)
  3369.     public IRubyObject end_with_p(ThreadContext context, IRubyObject[]args) {
  3370.         for (int i = 0; i < args.length; i++) {
  3371.             if (end_with_pCommon(args[i])) return context.runtime.getTrue();
  3372.         }
  3373.         return context.runtime.getFalse();
  3374.     }

  3375.     private boolean end_with_pCommon(IRubyObject arg) {
  3376.         IRubyObject tmp = arg.checkStringType();
  3377.         if (tmp.isNil()) return false;
  3378.         RubyString otherString = (RubyString)tmp;
  3379.         int otherLength = otherString.value.getRealSize();
  3380.         Encoding enc = checkEncoding(otherString);
  3381.         if (value.getRealSize() < otherLength) return false;
  3382.         int p = value.getBegin();
  3383.         int end = p + value.getRealSize();
  3384.         if (otherLength == 0) {
  3385.             // other is '', so return true
  3386.             return true;
  3387.         }
  3388.         int s = end - otherLength;
  3389.         if (enc.leftAdjustCharHead(value.getUnsafeBytes(), p, s, end) != s) return false;
  3390.         return value.endsWith(otherString.value);
  3391.     }

  3392.     private static final ByteList SPACE_BYTELIST = new ByteList(ByteList.plain(" "));

  3393.     private IRubyObject justify19(IRubyObject arg0, int jflag) {
  3394.         Ruby runtime = getRuntime();
  3395.         RubyString result = justifyCommon(runtime, SPACE_BYTELIST,
  3396.                                                    1,
  3397.                                                    true, value.getEncoding(), RubyFixnum.num2int(arg0), jflag);
  3398.         if (getCodeRange() != CR_BROKEN) result.setCodeRange(getCodeRange());
  3399.         return result;
  3400.     }

  3401.     private IRubyObject justify19(IRubyObject arg0, IRubyObject arg1, int jflag) {
  3402.         Ruby runtime = getRuntime();
  3403.         RubyString padStr = arg1.convertToString();
  3404.         ByteList pad = padStr.value;
  3405.         Encoding enc = checkEncoding(padStr);
  3406.         int padCharLen = padStr.strLength(enc);
  3407.         if (pad.getRealSize() == 0 || padCharLen == 0) throw runtime.newArgumentError("zero width padding");
  3408.         int width = RubyFixnum.num2int(arg0);
  3409.         RubyString result = justifyCommon(runtime, pad,
  3410.                                                    padCharLen,
  3411.                                                    padStr.singleByteOptimizable(),
  3412.                                                    enc, width, jflag);
  3413.         if (RubyFixnum.num2int(result.length19()) > RubyFixnum.num2int(length19())) result.infectBy(padStr);
  3414.         int cr = codeRangeAnd(getCodeRange(), padStr.getCodeRange());
  3415.         if (cr != CR_BROKEN) result.setCodeRange(cr);
  3416.         return result;
  3417.     }

  3418.     private RubyString justifyCommon(Ruby runtime, ByteList pad, int padCharLen, boolean padSinglebyte, Encoding enc, int width, int jflag) {
  3419.         int len = strLength(enc);
  3420.         if (width < 0 || len >= width) return strDup(runtime);
  3421.         int n = width - len;

  3422.         int llen = (jflag == 'l') ? 0 : ((jflag == 'r') ? n : n / 2);
  3423.         int rlen = n - llen;

  3424.         int padP = pad.getBegin();
  3425.         int padLen = pad.getRealSize();
  3426.         byte padBytes[] = pad.getUnsafeBytes();

  3427.         ByteList res = new ByteList(value.getRealSize() + n * padLen / padCharLen + 2);

  3428.         int p = res.getBegin();
  3429.         byte bytes[] = res.getUnsafeBytes();

  3430.         while (llen > 0) {
  3431.             if (padLen <= 1) {
  3432.                 bytes[p++] = padBytes[padP];
  3433.                 llen--;
  3434.             } else if (llen > padCharLen) {
  3435.                 System.arraycopy(padBytes, padP, bytes, p, padLen);
  3436.                 p += padLen;
  3437.                 llen -= padCharLen;
  3438.             } else {
  3439.                 int padPP = padSinglebyte ? padP + llen : StringSupport.nth(enc, padBytes, padP, padP + padLen, llen);
  3440.                 n = padPP - padP;
  3441.                 System.arraycopy(padBytes, padP, bytes, p, n);
  3442.                 p += n;
  3443.                 break;
  3444.             }
  3445.         }

  3446.         System.arraycopy(value.getUnsafeBytes(), value.getBegin(), bytes, p, value.getRealSize());
  3447.         p += value.getRealSize();

  3448.         while (rlen > 0) {
  3449.             if (padLen <= 1) {
  3450.                 bytes[p++] = padBytes[padP];
  3451.                 rlen--;
  3452.             } else if (rlen > padCharLen) {
  3453.                 System.arraycopy(padBytes, padP, bytes, p, padLen);
  3454.                 p += padLen;
  3455.                 rlen -= padCharLen;
  3456.             } else {
  3457.                 int padPP = padSinglebyte ? padP + rlen : StringSupport.nth(enc, padBytes, padP, padP + padLen, rlen);
  3458.                 n = padPP - padP;
  3459.                 System.arraycopy(padBytes, padP, bytes, p, n);
  3460.                 p += n;
  3461.                 break;
  3462.             }
  3463.         }

  3464.         res.setRealSize(p);

  3465.         RubyString result = new RubyString(runtime, getMetaClass(), res);
  3466.         if (RubyFixnum.num2int(result.length19()) > RubyFixnum.num2int(length19())) {
  3467.                  result.infectBy(this);
  3468.              }
  3469.         result.associateEncoding(enc);
  3470.         return result;
  3471.     }

  3472.     /** rb_str_ljust
  3473.      *
  3474.      */
  3475.     public IRubyObject ljust(IRubyObject arg0) {
  3476.         return ljust19(arg0);
  3477.     }

  3478.     public IRubyObject ljust(IRubyObject arg0, IRubyObject arg1) {
  3479.         return ljust19(arg0, arg1);
  3480.     }

  3481.     @JRubyMethod(name = "ljust")
  3482.     public IRubyObject ljust19(IRubyObject arg0) {
  3483.         return justify19(arg0, 'l');
  3484.     }

  3485.     @JRubyMethod(name = "ljust")
  3486.     public IRubyObject ljust19(IRubyObject arg0, IRubyObject arg1) {
  3487.         return justify19(arg0, arg1, 'l');
  3488.     }

  3489.     /** rb_str_rjust
  3490.      *
  3491.      */
  3492.     public IRubyObject rjust(IRubyObject arg0) {
  3493.         return rjust19(arg0);
  3494.     }

  3495.     public IRubyObject rjust(IRubyObject arg0, IRubyObject arg1) {
  3496.         return rjust19(arg0, arg1);
  3497.     }

  3498.     @JRubyMethod(name = "rjust")
  3499.     public IRubyObject rjust19(IRubyObject arg0) {
  3500.         return justify19(arg0, 'r');
  3501.     }

  3502.     @JRubyMethod(name = "rjust")
  3503.     public IRubyObject rjust19(IRubyObject arg0, IRubyObject arg1) {
  3504.         return justify19(arg0, arg1, 'r');
  3505.     }

  3506.     /** rb_str_center
  3507.      *
  3508.      */
  3509.     public IRubyObject center(IRubyObject arg0) {
  3510.         return center19(arg0);
  3511.     }

  3512.     public IRubyObject center(IRubyObject arg0, IRubyObject arg1) {
  3513.         return center19(arg0, arg1);
  3514.     }

  3515.     @JRubyMethod(name = "center")
  3516.     public IRubyObject center19(IRubyObject arg0) {
  3517.         return justify19(arg0, 'c');
  3518.     }

  3519.     @JRubyMethod(name = "center")
  3520.     public IRubyObject center19(IRubyObject arg0, IRubyObject arg1) {
  3521.         return justify19(arg0, arg1, 'c');
  3522.     }

  3523.     @JRubyMethod(reads = BACKREF, writes = BACKREF)
  3524.     public IRubyObject partition(ThreadContext context, Block block) {
  3525.         return RubyEnumerable.partition(context, this, block);
  3526.     }

  3527.     @JRubyMethod(reads = BACKREF, writes = BACKREF)
  3528.     public IRubyObject partition(ThreadContext context, IRubyObject arg, Block block) {
  3529.         Ruby runtime = context.runtime;
  3530.         final int pos;
  3531.         final RubyString sep;
  3532.         if (arg instanceof RubyRegexp) {
  3533.             RubyRegexp regex = (RubyRegexp)arg;
  3534.             IRubyObject[] holder = {context.nil};

  3535.             pos = regex.search19(context, this, 0, false, holder);
  3536.             context.setBackRef(holder[0]);
  3537.             if (pos < 0) return partitionMismatch(runtime);
  3538.             sep = (RubyString)subpat19(runtime, context, regex);
  3539.             if (pos == 0 && sep.value.getRealSize() == 0) return partitionMismatch(runtime);
  3540.         } else {
  3541.             IRubyObject tmp = arg.checkStringType();
  3542.             if (tmp.isNil()) throw runtime.newTypeError("type mismatch: " + arg.getMetaClass().getName() + " given");
  3543.             sep = (RubyString)tmp;
  3544.             pos = StringSupport.index(this, this.value, this.strLength(this.checkEncoding(sep)), sep, sep.value, sep.strLength(this.checkEncoding(sep)), 0, this.checkEncoding(sep));
  3545.             if (pos < 0) return partitionMismatch(runtime);
  3546.         }

  3547.         return RubyArray.newArray(runtime, new IRubyObject[]{
  3548.                 makeShared19(runtime, 0, pos),
  3549.                 sep,
  3550.                 makeShared19(runtime, pos + sep.value.getRealSize(), value.getRealSize() - pos - sep.value.getRealSize())});
  3551.     }

  3552.     private IRubyObject partitionMismatch(Ruby runtime) {
  3553.         return RubyArray.newArray(runtime, new IRubyObject[]{this, newEmptyString(runtime), newEmptyString(runtime)});
  3554.     }

  3555.     @JRubyMethod(name = "rpartition", reads = BACKREF, writes = BACKREF)
  3556.     public IRubyObject rpartition(ThreadContext context, IRubyObject arg) {
  3557.         Ruby runtime = context.runtime;
  3558.         final int pos;
  3559.         final RubyString sep;
  3560.         if (arg instanceof RubyRegexp) {
  3561.             RubyRegexp regex = (RubyRegexp)arg;
  3562.             IRubyObject[] holder = {context.nil};

  3563.             pos = regex.search19(context, this, value.getRealSize(), true, holder);
  3564.             context.setBackRef(holder[0]);

  3565.             if (pos < 0) return rpartitionMismatch(runtime);
  3566.             sep = (RubyString)RubyRegexp.nth_match(0, holder[0]);
  3567.         } else {
  3568.             IRubyObject tmp = arg.checkStringType();
  3569.             if (tmp.isNil()) throw runtime.newTypeError("type mismatch: " + arg.getMetaClass().getName() + " given");
  3570.             sep = (RubyString)tmp;
  3571.             pos = StringSupport.rindex(value, this.strLength(this.checkEncoding(sep)), sep.value, sep.strLength(this.checkEncoding(sep)), subLength(value.getRealSize()), sep, this.checkEncoding(sep));
  3572.             if (pos < 0) return rpartitionMismatch(runtime);
  3573.         }

  3574.         return RubyArray.newArray(runtime, new IRubyObject[]{
  3575.                 substr19(runtime, 0, pos),
  3576.                 sep,
  3577.                 substr19(runtime, pos + sep.strLength(), value.getRealSize())});
  3578.     }

  3579.     private IRubyObject rpartitionMismatch(Ruby runtime) {
  3580.         return RubyArray.newArray(runtime, new IRubyObject[]{newEmptyString(runtime), newEmptyString(runtime), this});
  3581.     }

  3582.     /** rb_str_chop / rb_str_chop_bang
  3583.      *
  3584.      */
  3585.     public IRubyObject chop(ThreadContext context) {
  3586.         return chop19(context);
  3587.     }

  3588.     public IRubyObject chop_bang(ThreadContext context) {
  3589.         return chop_bang19(context);
  3590.     }

  3591.     @JRubyMethod(name = "chop")
  3592.     public IRubyObject chop19(ThreadContext context) {
  3593.         Ruby runtime = context.runtime;
  3594.         if (value.getRealSize() == 0) return newEmptyString(runtime, getMetaClass(), value.getEncoding()).infectBy(this);
  3595.         return makeShared19(runtime, 0, choppedLength19(runtime));
  3596.     }

  3597.     @JRubyMethod(name = "chop!")
  3598.     public IRubyObject chop_bang19(ThreadContext context) {
  3599.         modifyCheck();
  3600.         Ruby runtime = context.runtime;
  3601.         if (value.getRealSize() == 0) return runtime.getNil();
  3602.         view(0, choppedLength19(runtime));
  3603.         if (getCodeRange() != CR_7BIT) clearCodeRange();
  3604.         return this;
  3605.     }

  3606.     private int choppedLength19(Ruby runtime) {
  3607.         int p = value.getBegin();
  3608.         int end = p + value.getRealSize();

  3609.         if (p > end) return 0;
  3610.         byte bytes[] = value.getUnsafeBytes();
  3611.         Encoding enc = value.getEncoding();

  3612.         int s = enc.prevCharHead(bytes, p, end, end);
  3613.         if (s == -1) return 0;
  3614.         if (s > p && codePoint(runtime, enc, bytes, s, end) == '\n') {
  3615.             int s2 = enc.prevCharHead(bytes, p, s, end);
  3616.             if (s2 != -1 && codePoint(runtime, enc, bytes, s2, end) == '\r') s = s2;
  3617.         }
  3618.         return s - p;
  3619.     }

  3620.     /** rb_str_chop
  3621.      *
  3622.      */
  3623.     public RubyString chomp(ThreadContext context) {
  3624.         return chomp19(context);
  3625.     }

  3626.     public RubyString chomp(ThreadContext context, IRubyObject arg0) {
  3627.         return chomp19(context, arg0);
  3628.     }

  3629.     /**
  3630.      * rb_str_chomp_bang
  3631.      *
  3632.      * In the common case, removes CR and LF characters in various ways depending on the value of
  3633.      *   the optional args[0].
  3634.      * If args.length==0 removes one instance of CR, CRLF or LF from the end of the string.
  3635.      * If args.length>0 and args[0] is "\n" then same behaviour as args.length==0 .
  3636.      * If args.length>0 and args[0] is "" then removes trailing multiple LF or CRLF (but no CRs at
  3637.      *   all(!)).
  3638.      */
  3639.     public IRubyObject chomp_bang(ThreadContext context) {
  3640.         return chomp_bang19(context);
  3641.     }

  3642.     public IRubyObject chomp_bang(ThreadContext context, IRubyObject arg0) {
  3643.         return chomp_bang19(context, arg0);
  3644.     }

  3645.     @JRubyMethod(name = "chomp")
  3646.     public RubyString chomp19(ThreadContext context) {
  3647.         RubyString str = strDup(context.runtime);
  3648.         str.chomp_bang19(context);
  3649.         return str;
  3650.     }

  3651.     @JRubyMethod(name = "chomp")
  3652.     public RubyString chomp19(ThreadContext context, IRubyObject arg0) {
  3653.         RubyString str = strDup(context.runtime);
  3654.         str.chomp_bang19(context, arg0);
  3655.         return str;
  3656.     }

  3657.     @JRubyMethod(name = "chomp!")
  3658.     public IRubyObject chomp_bang19(ThreadContext context) {
  3659.         Ruby runtime = context.runtime;
  3660.         if (value.getRealSize() == 0) return runtime.getNil();

  3661.         IRubyObject rsObj = runtime.getGlobalVariables().get("$/");

  3662.         if (rsObj == runtime.getGlobalVariables().getDefaultSeparator()) return smartChopBangCommon19(runtime);
  3663.         return chompBangCommon19(runtime, rsObj);
  3664.     }

  3665.     @JRubyMethod(name = "chomp!")
  3666.     public IRubyObject chomp_bang19(ThreadContext context, IRubyObject arg0) {
  3667.         modifyCheck();
  3668.         Ruby runtime = context.runtime;
  3669.         if (value.getRealSize() == 0) return runtime.getNil();
  3670.         return chompBangCommon19(runtime, arg0);
  3671.     }

  3672.     private IRubyObject chompBangCommon19(Ruby runtime, IRubyObject rsObj) {
  3673.         if (rsObj.isNil()) return rsObj;

  3674.         RubyString rs = rsObj.convertToString();
  3675.         int p = value.getBegin();
  3676.         int len = value.getRealSize();
  3677.         int end = p + len;
  3678.         byte[] bytes = value.getUnsafeBytes();

  3679.         int rslen = rs.value.getRealSize();
  3680.         if (rslen == 0) {
  3681.             while (len > 0 && bytes[p + len - 1] == (byte)'\n') {
  3682.                 len--;
  3683.                 if (len > 0 && bytes[p + len - 1] == (byte)'\r') len--;
  3684.             }
  3685.             if (len < value.getRealSize()) {
  3686.                 keepCodeRange();
  3687.                 view(0, len);
  3688.                 return this;
  3689.             }
  3690.             return runtime.getNil();
  3691.         }

  3692.         if (rslen > len) return runtime.getNil();
  3693.         byte newline = rs.value.getUnsafeBytes()[rslen - 1];
  3694.         if (rslen == 1 && newline == (byte)'\n') return smartChopBangCommon19(runtime);

  3695.         Encoding enc = checkEncoding(rs);
  3696.         if (rs.scanForCodeRange() == CR_BROKEN) return runtime.getNil();

  3697.         int pp = end - rslen;
  3698.         if (bytes[p + len - 1] == newline && rslen <= 1 || value.endsWith(rs.value)) {
  3699.             if (enc.leftAdjustCharHead(bytes, p, pp, end) != pp) return runtime.getNil();
  3700.             if (getCodeRange() != CR_7BIT) clearCodeRange();
  3701.             view(0, value.getRealSize() - rslen);
  3702.             return this;
  3703.         }
  3704.         return runtime.getNil();
  3705.     }

  3706.     private IRubyObject smartChopBangCommon19(Ruby runtime) {
  3707.         final int p = value.getBegin();
  3708.         int len = value.getRealSize();
  3709.         int end = p + len;
  3710.         byte bytes[] = value.getUnsafeBytes();
  3711.         Encoding enc = value.getEncoding();

  3712.         keepCodeRange();
  3713.         if (enc.minLength() > 1) {
  3714.             int pp = enc.leftAdjustCharHead(bytes, p, end - enc.minLength(), end);
  3715.             if (enc.isNewLine(bytes, pp, end)) end = pp;
  3716.             pp = end - enc.minLength();
  3717.             if (pp >= p) {
  3718.                 pp = enc.leftAdjustCharHead(bytes, p, pp, end);
  3719.                 if (StringSupport.preciseLength(enc, bytes, pp, end) > 0 &&
  3720.                         enc.mbcToCode(bytes, pp, end) == '\r') end = pp;
  3721.             }
  3722.             if (end == p + value.getRealSize()) {
  3723.                 modifyCheck();
  3724.                 return runtime.getNil();
  3725.             }
  3726.             len = end - p;
  3727.             view(0, len);
  3728.         } else {
  3729.             if (bytes[p + len - 1] == (byte)'\n') {
  3730.                 len--;
  3731.                 if (len > 0 && bytes[p + len - 1] == (byte)'\r') len--;
  3732.                 view(0, len);
  3733.             } else if (bytes[p + len - 1] == (byte)'\r') {
  3734.                 len--;
  3735.                 view(0, len);
  3736.             } else {
  3737.                 modifyCheck();
  3738.                 return runtime.getNil();
  3739.             }
  3740.         }
  3741.         return this;
  3742.     }

  3743.     /** rb_str_lstrip / rb_str_lstrip_bang
  3744.      *
  3745.      */
  3746.     public IRubyObject lstrip(ThreadContext context) {
  3747.         return lstrip19(context);
  3748.     }

  3749.     public IRubyObject lstrip_bang(ThreadContext context) {
  3750.         return lstrip_bang19(context);
  3751.     }

  3752.     @JRubyMethod(name = "lstrip")
  3753.     public IRubyObject lstrip19(ThreadContext context) {
  3754.         RubyString str = strDup(context.runtime);
  3755.         str.lstrip_bang19(context);
  3756.         return str;
  3757.     }

  3758.     @JRubyMethod(name = "lstrip!")
  3759.     public IRubyObject lstrip_bang19(ThreadContext context) {
  3760.         modifyCheck();
  3761.         Ruby runtime = context.runtime;
  3762.         if (value.getRealSize() == 0) {
  3763.             return runtime.getNil();
  3764.         }

  3765.         Encoding enc = value.getEncoding();
  3766.         int s = value.getBegin();
  3767.         int end = s + value.getRealSize();
  3768.         byte[]bytes = value.getUnsafeBytes();

  3769.         final IRubyObject result;
  3770.         if (singleByteOptimizable(enc)) {
  3771.             result = singleByteLStrip(runtime, bytes, s, end);
  3772.         } else {
  3773.             result = multiByteLStrip(runtime, enc, bytes, s, end);
  3774.         }
  3775.         keepCodeRange();
  3776.         return result;
  3777.     }

  3778.     private IRubyObject singleByteLStrip(Ruby runtime, byte[]bytes, int s, int end) {
  3779.         int p = s;
  3780.         while (p < end && ASCII.isSpace(bytes[p] & 0xff)) p++;
  3781.         if (p > s) {
  3782.             view(p - s, end - p);
  3783.             return this;
  3784.         }
  3785.         return runtime.getNil();
  3786.     }

  3787.     private IRubyObject multiByteLStrip(Ruby runtime, Encoding enc, byte[]bytes, int s, int end) {
  3788.         int p = s;

  3789.         while (p < end) {
  3790.             int c = codePoint(runtime, enc, bytes, p, end);
  3791.             if (!ASCII.isSpace(c)) break;
  3792.             p += codeLength(runtime, enc, c);
  3793.         }

  3794.         if (p > s) {
  3795.             view(p - s, end - p);
  3796.             return this;
  3797.         }

  3798.         return runtime.getNil();
  3799.     }

  3800.     /** rb_str_rstrip / rb_str_rstrip_bang
  3801.      *
  3802.      */
  3803.     public IRubyObject rstrip(ThreadContext context) {
  3804.         return rstrip19(context);
  3805.     }

  3806.     public IRubyObject rstrip_bang(ThreadContext context) {
  3807.         return rstrip_bang19(context);
  3808.     }

  3809.     @JRubyMethod(name = "rstrip")
  3810.     public IRubyObject rstrip19(ThreadContext context) {
  3811.         RubyString str = strDup(context.runtime);
  3812.         str.rstrip_bang19(context);
  3813.         return str;
  3814.     }

  3815.     @JRubyMethod(name = "rstrip!")
  3816.     public IRubyObject rstrip_bang19(ThreadContext context) {
  3817.         modifyCheck();
  3818.         Ruby runtime = context.runtime;
  3819.         if (value.getRealSize() == 0) {
  3820.             return runtime.getNil();
  3821.         }

  3822.         IRubyObject result = singleByteOptimizable(value.getEncoding()) ?
  3823.             singleByteRStrip19(runtime) : multiByteRStrip19(runtime);

  3824.         keepCodeRange();
  3825.         return result;
  3826.     }

  3827.     // In 1.9 we strip any combination of \0 and \s
  3828.     private IRubyObject singleByteRStrip19(Ruby runtime) {
  3829.         byte[] bytes = value.getUnsafeBytes();
  3830.         int start = value.getBegin();
  3831.         int end = start + value.getRealSize();
  3832.         int endp = end - 1;
  3833.         while (endp >= start && (bytes[endp] == 0 ||
  3834.                 ASCII.isSpace(bytes[endp] & 0xff))) endp--;

  3835.         if (endp < end - 1) {
  3836.             view(0, endp - start + 1);
  3837.             return this;
  3838.         }

  3839.         return runtime.getNil();
  3840.     }

  3841.     // In 1.9 we strip any combination of \0 and \s
  3842.     private IRubyObject multiByteRStrip19(Ruby runtime) {
  3843.         byte[] bytes = value.getUnsafeBytes();
  3844.         int start = value.getBegin();
  3845.         int end = start + value.getRealSize();
  3846.         Encoding enc = value.getEncoding();
  3847.         int endp = end;
  3848.         int prev;
  3849.         while ((prev = enc.prevCharHead(bytes, start, endp, end)) != -1) {
  3850.             int point = codePoint(runtime, enc, bytes, prev, end);
  3851.             if (point != 0 && !ASCII.isSpace(point)) break;
  3852.             endp = prev;
  3853.         }

  3854.         if (endp < end) {
  3855.             view(0, endp - start);
  3856.             return this;
  3857.         }
  3858.         return runtime.getNil();
  3859.     }

  3860.     /** rb_str_strip / rb_str_strip_bang
  3861.      *
  3862.      */
  3863.     public IRubyObject strip(ThreadContext context) {
  3864.         return strip19(context);
  3865.     }

  3866.     public IRubyObject strip_bang(ThreadContext context) {
  3867.         return strip_bang19(context);
  3868.     }

  3869.     @JRubyMethod(name = "strip")
  3870.     public IRubyObject strip19(ThreadContext context) {
  3871.         RubyString str = strDup(context.runtime);
  3872.         str.strip_bang19(context);
  3873.         return str;
  3874.     }

  3875.     @JRubyMethod(name = "strip!")
  3876.     public IRubyObject strip_bang19(ThreadContext context) {
  3877.         modifyCheck();

  3878.         IRubyObject left = lstrip_bang19(context);
  3879.         IRubyObject right = rstrip_bang19(context);

  3880.         return left.isNil() && right.isNil() ? context.runtime.getNil() : this;
  3881.     }

  3882.     /** rb_str_count
  3883.      *
  3884.      */
  3885.     public IRubyObject count(ThreadContext context) {
  3886.         return count19(context);
  3887.     }

  3888.     public IRubyObject count(ThreadContext context, IRubyObject arg) {
  3889.         return count19(context, arg);
  3890.     }

  3891.     public IRubyObject count(ThreadContext context, IRubyObject[] args) {
  3892.         return count19(context, args);
  3893.     }

  3894.     @JRubyMethod(name = "count")
  3895.     public IRubyObject count19(ThreadContext context) {
  3896.         throw context.runtime.newArgumentError("wrong number of arguments");
  3897.     }

  3898.     @JRubyMethod(name = "count")
  3899.     public IRubyObject count19(ThreadContext context, IRubyObject arg) {
  3900.         Ruby runtime = context.runtime;
  3901.         if (value.getRealSize() == 0) return RubyFixnum.zero(runtime);

  3902.         RubyString otherStr = arg.convertToString();
  3903.         Encoding enc = checkEncoding(otherStr);

  3904.         int c;
  3905.         if (otherStr.value.length() == 1 && enc.isAsciiCompatible() &&
  3906.                 ((c = otherStr.value.unsafeBytes()[otherStr.value.getBegin()] & 0xff)) < 0x80 && scanForCodeRange() != CR_BROKEN) {

  3907.             if (value.length() ==0) return RubyFixnum.zero(runtime);
  3908.             byte[]bytes = value.unsafeBytes();
  3909.             int p = value.getBegin();
  3910.             int end = p + value.length();
  3911.             int n = 0;
  3912.             while (p < end) {
  3913.                 if ((bytes[p++] & 0xff) == c) n++;
  3914.             }
  3915.             return RubyFixnum.newFixnum(runtime, n);
  3916.         }

  3917.         final boolean[]table = new boolean[StringSupport.TRANS_SIZE + 1];
  3918.         StringSupport.TrTables tables = StringSupport.trSetupTable(otherStr.value, context.runtime, table, null, true, enc);
  3919.         return runtime.newFixnum(StringSupport.countCommon19(value, runtime, table, tables, enc));
  3920.     }

  3921.     @JRubyMethod(name = "count", required = 1, rest = true)
  3922.     public IRubyObject count19(ThreadContext context, IRubyObject[] args) {
  3923.         Ruby runtime = context.runtime;
  3924.         if (value.getRealSize() == 0) return RubyFixnum.zero(runtime);

  3925.         RubyString otherStr = args[0].convertToString();
  3926.         Encoding enc = checkEncoding(otherStr);
  3927.         final boolean[]table = new boolean[StringSupport.TRANS_SIZE + 1];
  3928.         StringSupport.TrTables tables = StringSupport.trSetupTable(otherStr.value, runtime, table, null, true, enc);
  3929.         for (int i = 1; i<args.length; i++) {
  3930.             otherStr = args[i].convertToString();
  3931.             enc = checkEncoding(otherStr);
  3932.             tables = StringSupport.trSetupTable(otherStr.value, runtime, table, tables, false, enc);
  3933.         }

  3934.         return runtime.newFixnum(StringSupport.countCommon19(value, runtime, table, tables, enc));
  3935.     }

  3936.     /** rb_str_delete / rb_str_delete_bang
  3937.      *
  3938.      */
  3939.     public IRubyObject delete(ThreadContext context) {
  3940.         return delete19(context);
  3941.     }

  3942.     public IRubyObject delete(ThreadContext context, IRubyObject arg) {
  3943.         return delete19(context, arg);
  3944.     }

  3945.     public IRubyObject delete(ThreadContext context, IRubyObject[] args) {
  3946.         return delete19(context, args);
  3947.     }

  3948.     public IRubyObject delete_bang(ThreadContext context) {
  3949.         return delete_bang19(context);
  3950.     }

  3951.     public IRubyObject delete_bang(ThreadContext context, IRubyObject arg) {
  3952.         return delete_bang19(context, arg);
  3953.     }

  3954.     public IRubyObject delete_bang(ThreadContext context, IRubyObject[] args) {
  3955.         return delete_bang19(context, args);
  3956.     }

  3957.     @JRubyMethod(name = "delete")
  3958.     public IRubyObject delete19(ThreadContext context) {
  3959.         throw context.runtime.newArgumentError("wrong number of arguments");
  3960.     }

  3961.     @JRubyMethod(name = "delete")
  3962.     public IRubyObject delete19(ThreadContext context, IRubyObject arg) {
  3963.         RubyString str = strDup(context.runtime);
  3964.         str.delete_bang19(context, arg);
  3965.         return str;
  3966.     }

  3967.     @JRubyMethod(name = "delete", required = 1, rest = true)
  3968.     public IRubyObject delete19(ThreadContext context, IRubyObject[] args) {
  3969.         RubyString str = strDup(context.runtime);
  3970.         str.delete_bang19(context, args);
  3971.         return str;
  3972.     }

  3973.     @JRubyMethod(name = "delete!")
  3974.     public IRubyObject delete_bang19(ThreadContext context) {
  3975.         throw context.runtime.newArgumentError("wrong number of arguments");
  3976.     }

  3977.     @JRubyMethod(name = "delete!")
  3978.     public IRubyObject delete_bang19(ThreadContext context, IRubyObject arg) {
  3979.         Ruby runtime = context.runtime;
  3980.         if (value.getRealSize() == 0) return runtime.getNil();

  3981.         RubyString otherStr = arg.convertToString();
  3982.         Encoding enc = checkEncoding(otherStr);
  3983.         final boolean[]squeeze = new boolean[StringSupport.TRANS_SIZE + 1];
  3984.         StringSupport.TrTables tables = StringSupport.trSetupTable(otherStr.value, runtime, squeeze, null, true, enc);
  3985.         return delete_bangCommon19(runtime, squeeze, tables, enc);
  3986.     }

  3987.     @JRubyMethod(name = "delete!", required = 1, rest = true)
  3988.     public IRubyObject delete_bang19(ThreadContext context, IRubyObject[] args) {
  3989.         Ruby runtime = context.runtime;
  3990.         if (value.getRealSize() == 0) return runtime.getNil();

  3991.         RubyString otherStr = args[0].convertToString();
  3992.         Encoding enc = checkEncoding(otherStr);
  3993.         boolean[]squeeze = new boolean[StringSupport.TRANS_SIZE + 1];
  3994.         StringSupport.TrTables tables = StringSupport.trSetupTable(otherStr.value, runtime, squeeze, null, true, enc);
  3995.         for (int i=1; i<args.length; i++) {
  3996.             otherStr = args[i].convertToString();
  3997.             enc = checkEncoding(otherStr);
  3998.             tables = StringSupport.trSetupTable(otherStr.value, runtime, squeeze, tables, false, enc);
  3999.         }

  4000.         return delete_bangCommon19(runtime, squeeze, tables, enc);
  4001.     }

  4002.     private IRubyObject delete_bangCommon19(Ruby runtime, boolean[]squeeze, StringSupport.TrTables tables, Encoding enc) {
  4003.         modifyAndKeepCodeRange();

  4004.         int s = value.getBegin();
  4005.         int t = s;
  4006.         int send = s + value.getRealSize();
  4007.         byte[]bytes = value.getUnsafeBytes();
  4008.         boolean modify = false;
  4009.         boolean asciiCompatible = enc.isAsciiCompatible();
  4010.         int cr = asciiCompatible ? CR_7BIT : CR_VALID;
  4011.         while (s < send) {
  4012.             int c;
  4013.             if (asciiCompatible && Encoding.isAscii(c = bytes[s] & 0xff)) {
  4014.                 if (squeeze[c]) {
  4015.                     modify = true;
  4016.                 } else {
  4017.                     if (t != s) bytes[t] = (byte)c;
  4018.                     t++;
  4019.                 }
  4020.                 s++;
  4021.             } else {
  4022.                 c = codePoint(runtime, enc, bytes, s, send);
  4023.                 int cl = codeLength(runtime, enc, c);
  4024.                 if (StringSupport.trFind(c, squeeze, tables)) {
  4025.                     modify = true;
  4026.                 } else {
  4027.                     if (t != s) enc.codeToMbc(c, bytes, t);
  4028.                     t += cl;
  4029.                     if (cr == CR_7BIT) cr = CR_VALID;
  4030.                 }
  4031.                 s += cl;
  4032.             }
  4033.         }
  4034.         value.setRealSize(t - value.getBegin());
  4035.         setCodeRange(cr);

  4036.         return modify ? this : runtime.getNil();
  4037.     }

  4038.     /** rb_str_squeeze / rb_str_squeeze_bang
  4039.      *
  4040.      */
  4041.     public IRubyObject squeeze(ThreadContext context) {
  4042.         return squeeze19(context);
  4043.     }

  4044.     public IRubyObject squeeze(ThreadContext context, IRubyObject arg) {
  4045.         return squeeze19(context, arg);
  4046.     }

  4047.     public IRubyObject squeeze(ThreadContext context, IRubyObject[] args) {
  4048.         return squeeze19(context, args);
  4049.     }

  4050.     public IRubyObject squeeze_bang(ThreadContext context) {
  4051.         return squeeze_bang19(context);
  4052.     }

  4053.     public IRubyObject squeeze_bang(ThreadContext context, IRubyObject arg) {
  4054.         return squeeze_bang19(context, arg);
  4055.     }

  4056.     public IRubyObject squeeze_bang(ThreadContext context, IRubyObject[] args) {
  4057.         return squeeze_bang19(context, args);
  4058.     }

  4059.     private IRubyObject squeezeCommon(Ruby runtime, boolean squeeze[]) {
  4060.         int s = value.getBegin();
  4061.         int t = s;
  4062.         int send = s + value.getRealSize();
  4063.         byte[]bytes = value.getUnsafeBytes();
  4064.         int save = -1;

  4065.         while (s < send) {
  4066.             int c = bytes[s++] & 0xff;
  4067.             if (c != save || !squeeze[c]) bytes[t++] = (byte)(save = c);
  4068.         }

  4069.         if (t - value.getBegin() != value.getRealSize()) { // modified
  4070.             value.setRealSize(t - value.getBegin());
  4071.             return this;
  4072.         }

  4073.         return runtime.getNil();
  4074.     }

  4075.     @JRubyMethod(name = "squeeze")
  4076.     public IRubyObject squeeze19(ThreadContext context) {
  4077.         RubyString str = strDup(context.runtime);
  4078.         str.squeeze_bang19(context);
  4079.         return str;
  4080.     }

  4081.     @JRubyMethod(name = "squeeze")
  4082.     public IRubyObject squeeze19(ThreadContext context, IRubyObject arg) {
  4083.         RubyString str = strDup(context.runtime);
  4084.         str.squeeze_bang19(context, arg);
  4085.         return str;
  4086.     }

  4087.     @JRubyMethod(name = "squeeze", rest = true)
  4088.     public IRubyObject squeeze19(ThreadContext context, IRubyObject[] args) {
  4089.         RubyString str = strDup(context.runtime);
  4090.         str.squeeze_bang19(context, args);
  4091.         return str;
  4092.     }

  4093.     @JRubyMethod(name = "squeeze!")
  4094.     public IRubyObject squeeze_bang19(ThreadContext context) {
  4095.         Ruby runtime = context.runtime;
  4096.         if (value.getRealSize() == 0) {
  4097.             modifyCheck();
  4098.             return runtime.getNil();
  4099.         }
  4100.         final boolean squeeze[] = new boolean[StringSupport.TRANS_SIZE];
  4101.         for (int i=0; i< StringSupport.TRANS_SIZE; i++) squeeze[i] = true;

  4102.         modifyAndKeepCodeRange();
  4103.         if (singleByteOptimizable()) {
  4104.             return squeezeCommon(runtime, squeeze); // 1.8
  4105.         } else {
  4106.             return squeezeCommon19(runtime, squeeze, null, value.getEncoding(), false);
  4107.         }
  4108.     }

  4109.     @JRubyMethod(name = "squeeze!")
  4110.     public IRubyObject squeeze_bang19(ThreadContext context, IRubyObject arg) {
  4111.         Ruby runtime = context.runtime;
  4112.         if (value.getRealSize() == 0) {
  4113.             modifyCheck();
  4114.             return runtime.getNil();
  4115.         }

  4116.         RubyString otherStr = arg.convertToString();
  4117.         final boolean squeeze[] = new boolean[StringSupport.TRANS_SIZE + 1];
  4118.         StringSupport.TrTables tables = StringSupport.trSetupTable(otherStr.value, runtime, squeeze, null, true, checkEncoding(otherStr));

  4119.         modifyAndKeepCodeRange();
  4120.         if (singleByteOptimizable() && otherStr.singleByteOptimizable()) {
  4121.             return squeezeCommon(runtime, squeeze); // 1.8
  4122.         } else {
  4123.             return squeezeCommon19(runtime, squeeze, tables, value.getEncoding(), true);
  4124.         }

  4125.     }

  4126.     @JRubyMethod(name = "squeeze!", rest = true)
  4127.     public IRubyObject squeeze_bang19(ThreadContext context, IRubyObject[] args) {
  4128.         Ruby runtime = context.runtime;
  4129.         if (value.getRealSize() == 0) {
  4130.             modifyCheck();
  4131.             return runtime.getNil();
  4132.         }

  4133.         RubyString otherStr = args[0].convertToString();
  4134.         Encoding enc = checkEncoding(otherStr);
  4135.         final boolean squeeze[] = new boolean[StringSupport.TRANS_SIZE + 1];
  4136.         StringSupport.TrTables tables = StringSupport.trSetupTable(otherStr.value, runtime, squeeze, null, true, enc);

  4137.         boolean singlebyte = singleByteOptimizable() && otherStr.singleByteOptimizable();
  4138.         for (int i=1; i<args.length; i++) {
  4139.             otherStr = args[i].convertToString();
  4140.             enc = checkEncoding(otherStr);
  4141.             singlebyte = singlebyte && otherStr.singleByteOptimizable();
  4142.             tables = StringSupport.trSetupTable(otherStr.value, runtime, squeeze, tables, false, enc);
  4143.         }

  4144.         modifyAndKeepCodeRange();
  4145.         if (singlebyte) {
  4146.             return squeezeCommon(runtime, squeeze); // 1.8
  4147.         } else {
  4148.             return squeezeCommon19(runtime, squeeze, tables, enc, true);
  4149.         }
  4150.     }

  4151.     private IRubyObject squeezeCommon19(Ruby runtime, boolean squeeze[], StringSupport.TrTables tables, Encoding enc, boolean isArg) {
  4152.         int s = value.getBegin();
  4153.         int t = s;
  4154.         int send = s + value.getRealSize();
  4155.         byte[]bytes = value.getUnsafeBytes();
  4156.         int save = -1;
  4157.         int c;

  4158.         while (s < send) {
  4159.             if (enc.isAsciiCompatible() && (c = bytes[s] & 0xff) < 0x80) {
  4160.                 if (c != save || (isArg && !squeeze[c])) bytes[t++] = (byte)(save = c);
  4161.                 s++;
  4162.             } else {
  4163.                 c = codePoint(runtime, enc, bytes, s, send);
  4164.                 int cl = codeLength(runtime, enc, c);
  4165.                 if (c != save || (isArg && !StringSupport.trFind(c, squeeze, tables))) {
  4166.                     if (t != s) enc.codeToMbc(c, bytes, t);
  4167.                     save = c;
  4168.                     t += cl;
  4169.                 }
  4170.                 s += cl;
  4171.             }
  4172.         }

  4173.         if (t - value.getBegin() != value.getRealSize()) { // modified
  4174.             value.setRealSize(t - value.getBegin());
  4175.             return this;
  4176.         }

  4177.         return runtime.getNil();
  4178.     }

  4179.     /** rb_str_tr / rb_str_tr_bang
  4180.      *
  4181.      */
  4182.     public IRubyObject tr(ThreadContext context, IRubyObject src, IRubyObject repl) {
  4183.         return tr19(context, src, repl);
  4184.     }

  4185.     public IRubyObject tr_bang(ThreadContext context, IRubyObject src, IRubyObject repl) {
  4186.         return tr_bang19(context, src, repl);
  4187.     }

  4188.     @JRubyMethod(name = "tr")
  4189.     public IRubyObject tr19(ThreadContext context, IRubyObject src, IRubyObject repl) {
  4190.         RubyString str = strDup(context.runtime);
  4191.         str.trTrans19(context, src, repl, false);
  4192.         return str;
  4193.     }

  4194.     @JRubyMethod(name = "tr!")
  4195.     public IRubyObject tr_bang19(ThreadContext context, IRubyObject src, IRubyObject repl) {
  4196.         return trTrans19(context, src, repl, false);
  4197.     }

  4198.     private IRubyObject trTrans19(ThreadContext context, IRubyObject src, IRubyObject repl, boolean sflag) {
  4199.         Ruby runtime = context.runtime;
  4200.         if (value.getRealSize() == 0) return runtime.getNil();

  4201.         RubyString replStr = repl.convertToString();
  4202.         ByteList replList = replStr.value;
  4203.         if (replList.getRealSize() == 0) return delete_bang19(context, src);

  4204.         RubyString srcStr = src.convertToString();
  4205.         ByteList srcList = srcStr.value;
  4206.         Encoding e1 = checkEncoding(srcStr);
  4207.         Encoding e2 = checkEncoding(replStr);
  4208.         Encoding enc = e1 == e2 ? e1 : srcStr.checkEncoding(replStr);

  4209.         int cr = getCodeRange();

  4210.         final StringSupport.TR trSrc = new StringSupport.TR(srcList);
  4211.         boolean cflag = false;
  4212.         if (value.getRealSize() > 0) {
  4213.             if (enc.isAsciiCompatible()) {
  4214.                 if (trSrc.buf.length > 0 && (trSrc.buf[trSrc.p] & 0xff) == '^' && trSrc.p + 1 < trSrc.pend) {
  4215.                     cflag = true;
  4216.                     trSrc.p++;
  4217.                 }
  4218.             } else {
  4219.                 int cl = StringSupport.preciseLength(enc, trSrc.buf, trSrc.p, trSrc.pend);
  4220.                 if (enc.mbcToCode(trSrc.buf, trSrc.p, trSrc.pend) == '^' && trSrc.p + cl < trSrc.pend) {
  4221.                     cflag = true;
  4222.                     trSrc.p += cl;
  4223.                 }
  4224.             }
  4225.         }

  4226.         boolean singlebyte = singleByteOptimizable();

  4227.         int c;
  4228.         final int[]trans = new int[StringSupport.TRANS_SIZE];
  4229.         IntHash<Integer> hash = null;
  4230.         final StringSupport.TR trRepl = new StringSupport.TR(replList);

  4231.         int last = 0;
  4232.         if (cflag) {
  4233.             for (int i=0; i< StringSupport.TRANS_SIZE; i++) trans[i] = 1;

  4234.             while ((c = StringSupport.trNext(trSrc, runtime, enc)) >= 0) {
  4235.                 if (c < StringSupport.TRANS_SIZE) {
  4236.                     trans[c & 0xff] = -1;
  4237.                 } else {
  4238.                     if (hash == null) hash = new IntHash<Integer>();
  4239.                     hash.put(c, 1); // QTRUE
  4240.                 }
  4241.             }
  4242.             while ((c = StringSupport.trNext(trRepl, runtime, enc)) >= 0) {}  /* retrieve last replacer */
  4243.             last = trRepl.now;
  4244.             for (int i=0; i< StringSupport.TRANS_SIZE; i++) {
  4245.                 if (trans[i] >= 0) trans[i] = last;
  4246.             }
  4247.         } else {
  4248.             for (int i=0; i< StringSupport.TRANS_SIZE; i++) trans[i] = -1;

  4249.             while ((c = StringSupport.trNext(trSrc, runtime, enc)) >= 0) {
  4250.                 int r = StringSupport.trNext(trRepl, runtime, enc);
  4251.                 if (r == -1) r = trRepl.now;
  4252.                 if (c < StringSupport.TRANS_SIZE) {
  4253.                     trans[c] = r;
  4254.                     if (codeLength(runtime, enc, r) != 1) singlebyte = false;
  4255.                 } else {
  4256.                     if (hash == null) hash = new IntHash<Integer>();
  4257.                     hash.put(c, r);
  4258.                 }
  4259.             }
  4260.         }

  4261.         if (cr == CR_VALID) cr = CR_7BIT;
  4262.         modifyAndKeepCodeRange();
  4263.         int s = value.getBegin();
  4264.         int send = s + value.getRealSize();
  4265.         byte sbytes[] = value.getUnsafeBytes();
  4266.         int max = value.getRealSize();
  4267.         boolean modify = false;

  4268.         int clen, tlen, c0;

  4269.         if (sflag) {
  4270.             int save = -1;
  4271.             byte[]buf = new byte[max];
  4272.             int t = 0;
  4273.             while (s < send) {
  4274.                 boolean mayModify = false;
  4275.                 c0 = c = codePoint(runtime, e1, sbytes, s, send);
  4276.                 clen = codeLength(runtime, e1, c);
  4277.                 tlen = enc == e1 ? clen : codeLength(runtime, enc, c);
  4278.                 s += clen;

  4279.                 c = trCode(c, trans, hash, cflag, last, false);
  4280.                 if (c != -1) {
  4281.                     if (save == c) {
  4282.                         if (cr == CR_7BIT && !Encoding.isAscii(c)) cr = CR_VALID;
  4283.                         continue;
  4284.                     }
  4285.                     save = c;
  4286.                     tlen = codeLength(runtime, enc, c);
  4287.                     modify = true;
  4288.                 } else {
  4289.                     save = -1;
  4290.                     c = c0;
  4291.                     if (enc != e1) mayModify = true;
  4292.                 }

  4293.                 while (t + tlen >= max) {
  4294.                     max <<= 1;
  4295.                     byte[]tbuf = new byte[max];
  4296.                     System.arraycopy(buf, 0, tbuf, 0, buf.length);
  4297.                     buf = tbuf;
  4298.                 }
  4299.                 enc.codeToMbc(c, buf, t);
  4300.                 if (mayModify && (tlen == 1 ? sbytes[s] != buf[t] : ByteList.memcmp(sbytes, s, buf, t, tlen) != 0)) modify = true;
  4301.                 if (cr == CR_7BIT && !Encoding.isAscii(c)) cr = CR_VALID;
  4302.                 t += tlen;
  4303.             }
  4304.             value.setUnsafeBytes(buf);
  4305.             value.setRealSize(t);
  4306.         } else if (enc.isSingleByte() || (singlebyte && hash == null)) {
  4307.             while (s < send) {
  4308.                 c = sbytes[s] & 0xff;
  4309.                 if (trans[c] != -1) {
  4310.                     if (!cflag) {
  4311.                         c = trans[c];
  4312.                         sbytes[s] = (byte)c;
  4313.                     } else {
  4314.                         sbytes[s] = (byte)last;
  4315.                     }
  4316.                     modify = true;
  4317.                 }
  4318.                 if (cr == CR_7BIT && !Encoding.isAscii(c)) cr = CR_VALID;
  4319.                 s++;
  4320.             }
  4321.         } else {
  4322.             max += max >> 1;
  4323.             byte[]buf = new byte[max];
  4324.             int t = 0;

  4325.             while (s < send) {
  4326.                 boolean mayModify = false;
  4327.                 c0 = c = codePoint(runtime, e1, sbytes, s, send);
  4328.                 clen = codeLength(runtime, e1, c);
  4329.                 tlen = enc == e1 ? clen : codeLength(runtime, enc, c);

  4330.                 c = trCode(c, trans, hash, cflag, last, true);

  4331.                 if (c != -1) {
  4332.                     tlen = codeLength(runtime, enc, c);
  4333.                     modify = true;
  4334.                 } else {
  4335.                     c = c0;
  4336.                     if (enc != e1) mayModify = true;
  4337.                 }
  4338.                 while (t + tlen >= max) {
  4339.                     max <<= 1;
  4340.                     byte[]tbuf = new byte[max];
  4341.                     System.arraycopy(buf, 0, tbuf, 0, buf.length);
  4342.                     buf = tbuf;
  4343.                 }
  4344.                 enc.codeToMbc(c, buf, t);
  4345.                 if (mayModify && (tlen == 1 ? sbytes[s] != buf[t] : ByteList.memcmp(sbytes, s, buf, t, tlen) != 0)) modify = true;

  4346.                 if (cr == CR_7BIT && !Encoding.isAscii(c)) cr = CR_VALID;
  4347.                 s += clen;
  4348.                 t += tlen;
  4349.             }
  4350.             value.setUnsafeBytes(buf);
  4351.             value.setRealSize(t);
  4352.         }

  4353.         if (modify) {
  4354.             if (cr != CR_BROKEN) setCodeRange(cr);
  4355.             associateEncoding(enc);
  4356.             return this;
  4357.         }
  4358.         return runtime.getNil();
  4359.     }

  4360.     private int trCode(int c, int[]trans, IntHash<Integer> hash, boolean cflag, int last, boolean set) {
  4361.         if (c < StringSupport.TRANS_SIZE) {
  4362.             return trans[c];
  4363.         } else if (hash != null) {
  4364.             Integer tmp = hash.get(c);
  4365.             if (tmp == null) {
  4366.                 return cflag ? last : -1;
  4367.             } else {
  4368.                 return cflag ? -1 : tmp;
  4369.             }
  4370.         } else {
  4371.             return cflag && set ? last : -1;
  4372.         }
  4373.     }

  4374.     /** rb_str_tr_s / rb_str_tr_s_bang
  4375.      *
  4376.      */
  4377.     public IRubyObject tr_s(ThreadContext context, IRubyObject src, IRubyObject repl) {
  4378.         return tr_s19(context, src, repl);
  4379.     }

  4380.     public IRubyObject tr_s_bang(ThreadContext context, IRubyObject src, IRubyObject repl) {
  4381.         return tr_s_bang19(context, src, repl);
  4382.     }

  4383.     @JRubyMethod(name = "tr_s")
  4384.     public IRubyObject tr_s19(ThreadContext context, IRubyObject src, IRubyObject repl) {
  4385.         RubyString str = strDup(context.runtime);
  4386.         str.trTrans19(context, src, repl, true);
  4387.         return str;
  4388.     }

  4389.     @JRubyMethod(name = "tr_s!")
  4390.     public IRubyObject tr_s_bang19(ThreadContext context, IRubyObject src, IRubyObject repl) {
  4391.         return trTrans19(context, src, repl, true);
  4392.     }

  4393.     /** rb_str_each_line
  4394.      *
  4395.      */
  4396.     public IRubyObject each_line(ThreadContext context, Block block) {
  4397.         return each_lineCommon(context, context.runtime.getGlobalVariables().get("$/"), block);
  4398.     }

  4399.     public IRubyObject each_line(ThreadContext context, IRubyObject arg, Block block) {
  4400.         return each_lineCommon(context, arg, block);
  4401.     }

  4402.     public IRubyObject each_lineCommon(ThreadContext context, IRubyObject sep, Block block) {
  4403.         Ruby runtime = context.runtime;
  4404.         if (sep.isNil()) {
  4405.             block.yield(context, this);
  4406.             return this;
  4407.         }

  4408.         RubyString sepStr = sep.convertToString();
  4409.         ByteList sepValue = sepStr.value;
  4410.         int rslen = sepValue.getRealSize();

  4411.         final byte newline;
  4412.         if (rslen == 0) {
  4413.             newline = '\n';
  4414.         } else {
  4415.             newline = sepValue.getUnsafeBytes()[sepValue.getBegin() + rslen - 1];
  4416.         }

  4417.         int p = value.getBegin();
  4418.         int end = p + value.getRealSize();
  4419.         int ptr = p, s = p;
  4420.         int len = value.getRealSize();
  4421.         byte[] bytes = value.getUnsafeBytes();

  4422.         p += rslen;

  4423.         for (; p < end; p++) {
  4424.             if (rslen == 0 && bytes[p] == '\n') {
  4425.                 if (++p == end || bytes[p] != '\n') continue;
  4426.                 while(p < end && bytes[p] == '\n') p++;
  4427.             }
  4428.             if (ptr < p && bytes[p - 1] == newline &&
  4429.                (rslen <= 1 ||
  4430.                 ByteList.memcmp(sepValue.getUnsafeBytes(), sepValue.getBegin(), rslen, bytes, p - rslen, rslen) == 0)) {
  4431.                 block.yield(context, makeShared(runtime, s - ptr, p - s).infectBy(this));
  4432.                 modifyCheck(bytes, len);
  4433.                 s = p;
  4434.             }
  4435.         }

  4436.         if (s != end) {
  4437.             if (p > end) p = end;
  4438.             block.yield(context, makeShared(runtime, s - ptr, p - s).infectBy(this));
  4439.         }

  4440.         return this;
  4441.     }

  4442.     @JRubyMethod(name = "each_line")
  4443.     public IRubyObject each_line19(ThreadContext context, Block block) {
  4444.         return block.isGiven() ? each_lineCommon19(context, block) :
  4445.             enumeratorize(context.runtime, this, "each_line");
  4446.     }

  4447.     @JRubyMethod(name = "each_line")
  4448.     public IRubyObject each_line19(ThreadContext context, IRubyObject arg, Block block) {
  4449.         return block.isGiven() ? each_lineCommon19(context, arg, block) :
  4450.             enumeratorize(context.runtime, this, "each_line", arg);
  4451.     }

  4452.     public IRubyObject lines(ThreadContext context, Block block) {
  4453.         return lines20(context, block);
  4454.     }

  4455.     public IRubyObject lines(ThreadContext context, IRubyObject arg, Block block) {
  4456.         return lines20(context, arg, block);
  4457.     }

  4458.     @JRubyMethod(name = "lines")
  4459.     public IRubyObject lines20(ThreadContext context, Block block) {
  4460.         // FIXME: Inefficient; build array manually rather than via Enumerator
  4461.         return block.isGiven() ? each_lineCommon19(context, block) :
  4462.             enumeratorize(context.runtime, this, "lines").callMethod(context, "to_a");
  4463.     }

  4464.     @JRubyMethod(name = "lines")
  4465.     public IRubyObject lines20(ThreadContext context, IRubyObject arg, Block block) {
  4466.         // FIXME: Inefficient; build array manually rather than via Enumerator
  4467.         return block.isGiven() ? each_lineCommon19(context, arg, block) :
  4468.             enumeratorize(context.runtime, this, "lines", arg).callMethod(context, "to_a");
  4469.     }

  4470.     private IRubyObject each_lineCommon19(ThreadContext context, Block block) {
  4471.         return each_lineCommon19(context, context.runtime.getGlobalVariables().get("$/"), block);
  4472.     }

  4473.     private IRubyObject each_lineCommon19(ThreadContext context, IRubyObject sep, Block block) {
  4474.         Ruby runtime = context.runtime;
  4475.         if (sep.isNil()) {
  4476.             block.yield(context, this);
  4477.             return this;
  4478.         }
  4479.         if (! sep.respondsTo("to_str")) {
  4480.             throw runtime.newTypeError("can't convert " + sep.getMetaClass() + " into String");
  4481.         }

  4482.         ByteList val = value.shallowDup();
  4483.         int p = val.getBegin();
  4484.         int s = p;
  4485.         int offset = p;
  4486.         int len = val.getRealSize();
  4487.         int end = p + len;
  4488.         byte[]bytes = val.getUnsafeBytes();

  4489.         final Encoding enc;
  4490.         RubyString sepStr = sep.convertToString();
  4491.         if (sepStr == runtime.getGlobalVariables().getDefaultSeparator()) {
  4492.             enc = val.getEncoding();
  4493.             while (p < end) {
  4494.                 if (bytes[p] == (byte)'\n') {
  4495.                     int p0 = enc.leftAdjustCharHead(bytes, s, p, end);
  4496.                     if (enc.isNewLine(bytes, p0, end)) {
  4497.                         p = p0 + StringSupport.length(enc, bytes, p0, end);
  4498.                         block.yield(context, makeShared19(runtime, val, s - offset, p - s).infectBy(this));
  4499.                         s = p;
  4500.                         continue;
  4501.                     }
  4502.                 }
  4503.                 p++;
  4504.             }
  4505.         } else {
  4506.             enc = checkEncoding(sepStr);
  4507.             ByteList sepValue = sepStr.value;
  4508.             final int newLine;
  4509.             int rslen = sepValue.getRealSize();
  4510.             if (rslen == 0) {
  4511.                 newLine = '\n';
  4512.             } else {
  4513.                 newLine = codePoint(runtime, enc, sepValue.getUnsafeBytes(), sepValue.getBegin(), sepValue.getBegin() + sepValue.getRealSize());
  4514.             }

  4515.             while (p < end) {
  4516.                 int c = codePoint(runtime, enc, bytes, p, end);
  4517.                 again: do {
  4518.                     int n = codeLength(runtime, enc, c);
  4519.                     if (rslen == 0 && c == newLine) {
  4520.                         p += n;
  4521.                         if (p < end && (c = codePoint(runtime, enc, bytes, p, end)) != newLine) continue again;
  4522.                         while (p < end && codePoint(runtime, enc, bytes, p, end) == newLine) p += n;
  4523.                         p -= n;
  4524.                     }
  4525.                     if (c == newLine &&
  4526.                             rslen <= end - p &&
  4527.                             (rslen <= 1 ||
  4528.                             ByteList.memcmp(sepValue.getUnsafeBytes(), sepValue.getBegin(), rslen, bytes, p, rslen) == 0)) {
  4529.                         block.yield(context, makeShared19(runtime, val, s - offset, p - s + (rslen != 0 ? rslen : n)).infectBy(this));
  4530.                         s = p + (rslen != 0 ? rslen : n);
  4531.                     }
  4532.                     p += n;
  4533.                 } while (false);
  4534.             }
  4535.         }

  4536.         if (s != end) {
  4537.             block.yield(context, makeShared19(runtime, val, s-offset, end - s).infectBy(this));
  4538.         }
  4539.         return this;
  4540.     }

  4541.     /**
  4542.      * rb_str_each_byte
  4543.      */
  4544.     public RubyString each_byte(ThreadContext context, Block block) {
  4545.         Ruby runtime = context.runtime;
  4546.         // Check the length every iteration, since
  4547.         // the block can modify this string.
  4548.         for (int i = 0; i < value.length(); i++) {
  4549.             block.yield(context, runtime.newFixnum(value.get(i) & 0xFF));
  4550.         }
  4551.         return this;
  4552.     }

  4553.     @JRubyMethod(name = "each_byte")
  4554.     public IRubyObject each_byte19(ThreadContext context, Block block) {
  4555.         return enumerateBytes(context, "each_byte", block, false);
  4556.     }

  4557.     @JRubyMethod
  4558.     public IRubyObject bytes(ThreadContext context, Block block) {
  4559.         return enumerateBytes(context, "bytes", block, true);
  4560.     }

  4561.     @JRubyMethod(name = "each_char")
  4562.     public IRubyObject each_char19(ThreadContext context, Block block) {
  4563.         return enumerateChars(context, "each_char", block, false);
  4564.     }

  4565.     @JRubyMethod(name = "chars")
  4566.     public IRubyObject chars19(ThreadContext context, Block block) {
  4567.         return enumerateChars(context, "chars", block, true);
  4568.     }

  4569.     private SizeFn eachCharSizeFn() {
  4570.         final RubyString self = this;
  4571.         return new SizeFn() {
  4572.             @Override
  4573.             public IRubyObject size(IRubyObject[] args) {
  4574.                 return self.length();
  4575.             }
  4576.         };
  4577.     }

  4578.     /** rb_str_each_codepoint
  4579.      *
  4580.      */
  4581.     @JRubyMethod
  4582.     public IRubyObject each_codepoint(ThreadContext context, Block block) {
  4583.         return enumerateCodepoints(context, "each_codepoint", block, false);
  4584.     }

  4585.     @JRubyMethod
  4586.     public IRubyObject codepoints(ThreadContext context, Block block) {
  4587.         return enumerateCodepoints(context, "codepoints", block, true);
  4588.     }

  4589.     // MRI: rb_str_enumerate_chars
  4590.     private IRubyObject enumerateChars(ThreadContext context, String name, Block block, boolean wantarray) {
  4591.         Ruby runtime = context.runtime;
  4592.         RubyString str = this;
  4593.         IRubyObject orig = str;
  4594.         IRubyObject substr;
  4595.         int i, len, n;
  4596.         byte[] ptrBytes;
  4597.         int ptr;
  4598.         Encoding enc;
  4599.         RubyArray ary = null;

  4600.         str = RubyString.newString(runtime, str.getByteList().dup());
  4601.         ByteList strByteList = str.getByteList();
  4602.         ptrBytes = strByteList.unsafeBytes();
  4603.         ptr = strByteList.begin();
  4604.         len = strByteList.getRealSize();
  4605.         enc = str.getEncoding();

  4606.         if (block.isGiven()) {
  4607.             if (wantarray) {
  4608.                 // this code should be live in 3.0
  4609.                 if (false) { // #if STRING_ENUMERATORS_WANTARRAY
  4610.                     runtime.getWarnings().warn("given block not used");
  4611.                     ary = RubyArray.newArray(runtime, str.length().getLongValue());
  4612.                 } else {
  4613.                     runtime.getWarnings().warning("passing a block to String#chars is deprecated");
  4614.                     wantarray = false;
  4615.                 }
  4616.             }
  4617.         }
  4618.         else {
  4619.             if (wantarray)
  4620.                 ary = RubyArray.newArray(runtime, str.length().getLongValue());
  4621.             else
  4622.                 return enumeratorizeWithSize(context, this, name, eachCharSizeFn());
  4623.         }

  4624.         switch (getCodeRange()) {
  4625.             case CR_VALID:
  4626.             case CR_7BIT:
  4627.                 for (i = 0; i < len; i += n) {
  4628.                     n = StringSupport.encFastMBCLen(ptrBytes, ptr + i, ptr + len, enc);
  4629.                     substr = str.substr(runtime, i, n);
  4630.                     if (wantarray)
  4631.                         ary.push(substr);
  4632.                     else
  4633.                         block.yield(context, substr);
  4634.                 }
  4635.                 break;
  4636.             default:
  4637.                 for (i = 0; i < len; i += n) {
  4638.                     n = StringSupport.length(enc, ptrBytes, ptr + i, ptr + len);
  4639.                     substr = str.substr(runtime, i, n);
  4640.                     if (wantarray)
  4641.                         ary.push(substr);
  4642.                     else
  4643.                         block.yield(context, substr);
  4644.                 }
  4645.         }
  4646.         if (wantarray)
  4647.             return ary;
  4648.         else
  4649.             return orig;
  4650.     }

  4651.     // MRI: rb_str_enumerate_codepoints
  4652.     private IRubyObject enumerateCodepoints(ThreadContext context, String name, Block block, boolean wantarray) {
  4653.         Ruby runtime = context.runtime;
  4654.         RubyString str = this;
  4655.         IRubyObject orig = str;
  4656.         int n;
  4657.         int c;
  4658.         byte[] ptrBytes;
  4659.         int ptr, end;
  4660.         Encoding enc;
  4661.         RubyArray ary = null;

  4662.         if (singleByteOptimizable())
  4663.             return enumerateBytes(context, name, block, wantarray);

  4664.         str = RubyString.newString(runtime, str.getByteList().dup());
  4665.         ByteList strByteList = str.getByteList();
  4666.         ptrBytes = strByteList.unsafeBytes();
  4667.         ptr = strByteList.begin();
  4668.         end = ptr + strByteList.getRealSize();
  4669.         enc = str.getEncoding();

  4670.         if (block.isGiven()) {
  4671.             if (wantarray) {
  4672.                 // this code should be live in 3.0
  4673.                 if (false) { // #if STRING_ENUMERATORS_WANTARRAY
  4674.                     runtime.getWarnings().warn("given block not used");
  4675.                     ary = RubyArray.newArray(runtime, str.length().getLongValue());
  4676.                 } else {
  4677.                     runtime.getWarnings().warning("passing a block to String#codepoints is deprecated");
  4678.                     wantarray = false;
  4679.                 }
  4680.             }
  4681.         }
  4682.         else {
  4683.             if (wantarray)
  4684.                 ary = RubyArray.newArray(runtime, str.length().getLongValue());
  4685.             else
  4686.                 return enumeratorizeWithSize(context, str, name, eachCodepointSizeFn());
  4687.         }

  4688.         while (ptr < end) {
  4689.             c = codePoint(runtime, enc, ptrBytes, ptr, end);
  4690.             n = codeLength(runtime, enc, c);
  4691.             if (wantarray)
  4692.                 ary.push(RubyFixnum.newFixnum(runtime, c));
  4693.             else
  4694.                 block.yield(context, RubyFixnum.newFixnum(runtime, c));
  4695.             ptr += n;
  4696.         }
  4697.         if (wantarray)
  4698.             return ary;
  4699.         else
  4700.             return orig;
  4701.     }

  4702.     private IRubyObject enumerateBytes(ThreadContext context, String name, Block block, boolean wantarray) {
  4703.         Ruby runtime = context.runtime;
  4704.         RubyString str = this;
  4705.         int i;
  4706.         RubyArray ary = null;

  4707.         if (block.isGiven()) {
  4708.             if (wantarray) {
  4709.                 // this code should be live in 3.0
  4710.                 if (false) { // #if STRING_ENUMERATORS_WANTARRAY
  4711.                     runtime.getWarnings().warn("given block not used");
  4712.                     ary = RubyArray.newArray(runtime);
  4713.                 } else {
  4714.                     runtime.getWarnings().warning("passing a block to String#bytes is deprecated");
  4715.                     wantarray = false;
  4716.                 }
  4717.             }
  4718.         }
  4719.         else {
  4720.             if (wantarray)
  4721.                 ary = RubyArray.newArray(runtime, str.size());
  4722.             else
  4723.                 return enumeratorizeWithSize(context, str, name, eachByteSizeFn());
  4724.         }

  4725.         for (i=0; i<str.size(); i++) {
  4726.             RubyFixnum bite = RubyFixnum.newFixnum(runtime, str.getByteList().get(i) & 0xff);
  4727.             if (wantarray)
  4728.                 ary.push(bite);
  4729.             else
  4730.                 block.yield(context, bite);
  4731.         }
  4732.         if (wantarray)
  4733.             return ary;
  4734.         else
  4735.             return str;
  4736.     }

  4737.     private SizeFn eachCodepointSizeFn() {
  4738.         final RubyString self = this;
  4739.         return new SizeFn() {
  4740.             @Override
  4741.             public IRubyObject size(IRubyObject[] args) {
  4742.                 return self.length();
  4743.             }
  4744.         };
  4745.     }

  4746.     /** rb_str_intern
  4747.      *
  4748.      */
  4749.     private RubySymbol to_sym() {
  4750.         RubySymbol specialCaseIntern = checkSpecialCasesIntern(value);
  4751.         if (specialCaseIntern != null) return specialCaseIntern;

  4752.         RubySymbol symbol = getRuntime().getSymbolTable().getSymbol(value);
  4753.         if (symbol.getBytes() == value) shareLevel = SHARE_LEVEL_BYTELIST;
  4754.         return symbol;
  4755.     }

  4756.     private RubySymbol checkSpecialCasesIntern(ByteList value) {
  4757.         String[][] opTable = opTable19;

  4758.         for (int i = 0; i < opTable.length; i++) {
  4759.             String op = opTable[i][1];
  4760.             if (value.toString().equals(op)) {
  4761.                 return getRuntime().getSymbolTable().getSymbol(opTable[i][0]);
  4762.             }
  4763.         }

  4764.         return null;
  4765.     }

  4766.     public RubySymbol intern() {
  4767.         return intern19();
  4768.     }

  4769.     @JRubyMethod(name = {"to_sym", "intern"})
  4770.     public RubySymbol intern19() {
  4771.         return to_sym();
  4772.     }

  4773.     @JRubyMethod
  4774.     public IRubyObject ord(ThreadContext context) {
  4775.         Ruby runtime = context.runtime;
  4776.         return RubyFixnum.newFixnum(runtime, codePoint(runtime, value.getEncoding(), value.getUnsafeBytes(), value.getBegin(),
  4777.                                                                 value.getBegin() + value.getRealSize()));
  4778.     }

  4779.     @JRubyMethod
  4780.     public IRubyObject sum(ThreadContext context) {
  4781.         return sumCommon(context, 16);
  4782.     }

  4783.     @JRubyMethod
  4784.     public IRubyObject sum(ThreadContext context, IRubyObject arg) {
  4785.         return sumCommon(context, RubyNumeric.num2long(arg));
  4786.     }

  4787.     public IRubyObject sumCommon(ThreadContext context, long bits) {
  4788.         Ruby runtime = context.runtime;

  4789.         byte[]bytes = value.getUnsafeBytes();
  4790.         int p = value.getBegin();
  4791.         int len = value.getRealSize();
  4792.         int end = p + len;

  4793.         if (bits >= 8 * 8) { // long size * bits in byte
  4794.             IRubyObject one = RubyFixnum.one(runtime);
  4795.             IRubyObject sum = RubyFixnum.zero(runtime);
  4796.             while (p < end) {
  4797.                 modifyCheck(bytes, len);
  4798.                 sum = sum.callMethod(context, "+", RubyFixnum.newFixnum(runtime, bytes[p++] & 0xff));
  4799.             }
  4800.             if (bits != 0) {
  4801.                 IRubyObject mod = one.callMethod(context, "<<", RubyFixnum.newFixnum(runtime, bits));
  4802.                 sum = sum.callMethod(context, "&", mod.callMethod(context, "-", one));
  4803.             }
  4804.             return sum;
  4805.         } else {
  4806.             long sum = 0;
  4807.             while (p < end) {
  4808.                 modifyCheck(bytes, len);
  4809.                 sum += bytes[p++] & 0xff;
  4810.             }
  4811.             return RubyFixnum.newFixnum(runtime, bits == 0 ? sum : sum & (1L << bits) - 1L);
  4812.         }
  4813.     }

  4814.     /** string_to_c
  4815.      *
  4816.      */
  4817.     @JRubyMethod
  4818.     public IRubyObject to_c(ThreadContext context) {
  4819.         Ruby runtime = context.runtime;

  4820.         RubyString underscore = runtime.newString(new ByteList(new byte[]{'_'}));
  4821.         RubyRegexp underscore_pattern = RubyRegexp.newDummyRegexp(runtime, Numeric.ComplexPatterns.underscores_pat);
  4822.         IRubyObject s = this.gsubCommon19(context, null, underscore, null, underscore_pattern, false, 0, false);

  4823.         RubyArray a = RubyComplex.str_to_c_internal(context, s);

  4824.         if (!a.eltInternal(0).isNil()) {
  4825.             return a.eltInternal(0);
  4826.         } else {
  4827.             return RubyComplex.newComplexCanonicalize(context, RubyFixnum.zero(runtime));
  4828.         }
  4829.     }

  4830.     /** string_to_r
  4831.      *
  4832.      */
  4833.     @JRubyMethod
  4834.     public IRubyObject to_r(ThreadContext context) {
  4835.         Ruby runtime = context.runtime;

  4836.         RubyString underscore = runtime.newString(new ByteList(new byte[]{'_'}));
  4837.         RubyRegexp underscore_pattern = RubyRegexp.newDummyRegexp(runtime, Numeric.ComplexPatterns.underscores_pat);
  4838.         IRubyObject s = this.gsubCommon19(context, null, underscore, null, underscore_pattern, false, 0, false);

  4839.         RubyArray a = RubyRational.str_to_r_internal(context, s);

  4840.         if (!a.eltInternal(0).isNil()) {
  4841.             return a.eltInternal(0);
  4842.         } else {
  4843.             return RubyRational.newRationalCanonicalize(context, RubyFixnum.zero(runtime));
  4844.         }
  4845.     }

  4846.     public static RubyString unmarshalFrom(UnmarshalStream input) throws java.io.IOException {
  4847.         RubyString result = newString(input.getRuntime(), input.unmarshalString());
  4848.         input.registerLinkTarget(result);
  4849.         return result;
  4850.     }

  4851.     /**
  4852.      * @see org.jruby.util.Pack#unpack
  4853.      */
  4854.     @JRubyMethod
  4855.     public RubyArray unpack(IRubyObject obj) {
  4856.         return Pack.unpack(getRuntime(), this.value, stringValue(obj).value);
  4857.     }

  4858.     public void empty() {
  4859.         value = ByteList.EMPTY_BYTELIST;
  4860.         shareLevel = SHARE_LEVEL_BYTELIST;
  4861.     }

  4862.     @JRubyMethod
  4863.     public IRubyObject encoding(ThreadContext context) {
  4864.         return context.runtime.getEncodingService().getEncoding(value.getEncoding());
  4865.     }

  4866.     // TODO: re-split this
  4867.     public IRubyObject encode_bang(ThreadContext context, IRubyObject arg0) {
  4868.         return encode_bang(context, new IRubyObject[]{arg0});
  4869.     }

  4870.     public IRubyObject encode_bang(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
  4871.         return encode_bang(context, new IRubyObject[]{arg0,arg1});
  4872.     }

  4873.     public IRubyObject encode_bang(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
  4874.         return encode_bang(context, new IRubyObject[]{arg0,arg1,arg2});
  4875.     }

  4876.     @JRubyMethod(name = "encode!", optional = 3)
  4877.     public IRubyObject encode_bang(ThreadContext context, IRubyObject[] args) {
  4878.         IRubyObject[] newstr_p;
  4879.         Encoding encindex;

  4880.         modify19();

  4881.         newstr_p = new IRubyObject[]{this};
  4882.         encindex = EncodingUtils.strTranscode(context, args, newstr_p);

  4883.         if (encindex == null) return this;
  4884.         if (newstr_p[0] == this) {
  4885.             this.setEncoding(encindex);
  4886.             return this;
  4887.         }
  4888.         replace(newstr_p[0]);
  4889.         this.setEncoding(encindex);
  4890.         return this;
  4891.     }

  4892.     @JRubyMethod
  4893.     public IRubyObject encode(ThreadContext context) {
  4894.         return EncodingUtils.strEncode(context, this);
  4895.     }

  4896.     @JRubyMethod
  4897.     public IRubyObject encode(ThreadContext context, IRubyObject arg) {
  4898.         return EncodingUtils.strEncode(context, this, arg);
  4899.     }

  4900.     @JRubyMethod
  4901.     public IRubyObject encode(ThreadContext context, IRubyObject toEncoding, IRubyObject arg) {
  4902.         return EncodingUtils.strEncode(context, this, toEncoding, arg);
  4903.     }

  4904.     @JRubyMethod
  4905.     public IRubyObject encode(ThreadContext context, IRubyObject toEncoding,
  4906.             IRubyObject forcedEncoding, IRubyObject opts) {
  4907.         return EncodingUtils.strEncode(context, this, toEncoding, forcedEncoding, opts);
  4908.     }

  4909.     @JRubyMethod
  4910.     public IRubyObject force_encoding(ThreadContext context, IRubyObject enc) {
  4911.         modify19();
  4912.         Encoding encoding = context.runtime.getEncodingService().getEncodingFromObject(enc);
  4913.         associateEncoding(encoding);
  4914.         clearCodeRange();
  4915.         return this;
  4916.     }

  4917.     @JRubyMethod(name = "valid_encoding?")
  4918.     public IRubyObject valid_encoding_p(ThreadContext context) {
  4919.         return context.runtime.newBoolean(scanForCodeRange() != CR_BROKEN);
  4920.     }

  4921.     @JRubyMethod(name = "ascii_only?")
  4922.     public IRubyObject ascii_only_p(ThreadContext context) {
  4923.         return context.runtime.newBoolean(scanForCodeRange() == CR_7BIT);
  4924.     }

  4925.     @JRubyMethod
  4926.     public IRubyObject b(ThreadContext context) {
  4927.         Encoding encoding = ASCIIEncoding.INSTANCE;
  4928.         RubyString dup = (RubyString)dup();
  4929.         dup.associateEncoding(encoding);
  4930.         dup.clearCodeRange();
  4931.         return dup;
  4932.     }

  4933.     // MRI: str_scrub arity 0
  4934.     @JRubyMethod
  4935.     public IRubyObject scrub(ThreadContext context, Block block) {
  4936.         return scrub(context, context.nil, block);
  4937.     }

  4938.     // MRI: str_scrub arity 1
  4939.     @JRubyMethod
  4940.     public IRubyObject scrub(ThreadContext context, IRubyObject repl, Block block) {
  4941.         IRubyObject newStr = strScrub(context, repl, block);
  4942.         if (newStr.isNil()) return strDup(context.runtime);
  4943.         return newStr;
  4944.     }

  4945.     /**
  4946.      * Mutator for internal string representation.
  4947.      *
  4948.      * @param value The new java.lang.String this RubyString should encapsulate
  4949.      * @deprecated
  4950.      */
  4951.     public void setValue(CharSequence value) {
  4952.         view(ByteList.plain(value));
  4953.     }

  4954.     public void setValue(ByteList value) {
  4955.         view(value);
  4956.     }

  4957.     public CharSequence getValue() {
  4958.         return toString();
  4959.     }

  4960.     public byte[] getBytes() {
  4961.         return value.bytes();
  4962.     }

  4963.     public ByteList getByteList() {
  4964.         return value;
  4965.     }

  4966.     /** used by ar-jdbc
  4967.      *
  4968.      */
  4969.     public String getUnicodeValue() {
  4970.         return RubyEncoding.decodeUTF8(value.getUnsafeBytes(), value.getBegin(), value.getRealSize());
  4971.     }

  4972.     public static ByteList encodeBytelist(CharSequence value, Encoding encoding) {

  4973.         Charset charset = encoding.getCharset();

  4974.         // if null charset, fall back on Java default charset
  4975.         if (charset == null) charset = Charset.defaultCharset();

  4976.         byte[] bytes;
  4977.         if (charset == RubyEncoding.UTF8) {
  4978.             bytes = RubyEncoding.encodeUTF8(value);
  4979.         } else if (charset == RubyEncoding.UTF16) {
  4980.             bytes = RubyEncoding.encodeUTF16(value);
  4981.         } else {
  4982.             bytes = RubyEncoding.encode(value, charset);
  4983.         }

  4984.         return new ByteList(bytes, encoding, false);
  4985.     }

  4986.     @Override
  4987.     public Object toJava(Class target) {
  4988.         if (target.isAssignableFrom(String.class)) {
  4989.             return decodeString();
  4990.         } else if (target.isAssignableFrom(ByteList.class)) {
  4991.             return value;
  4992.         } else {
  4993.             return super.toJava(target);
  4994.         }
  4995.     }

  4996.     /**
  4997.      * Scrub the contents of this string, replacing invalid characters as appropriate.
  4998.      *
  4999.      * MRI: rb_str_scrub
  5000.      */
  5001.     public IRubyObject strScrub(ThreadContext context, IRubyObject repl, Block block) {
  5002.         Ruby runtime = context.runtime;
  5003.         int cr = getCodeRange();
  5004.         Encoding enc;
  5005.         Encoding encidx;

  5006.         if (cr == CR_7BIT || cr == CR_VALID)
  5007.             return context.nil;

  5008.         enc = getEncoding();
  5009.         if (!repl.isNil()) {
  5010.             repl = EncodingUtils.strCompatAndValid(context, repl, enc);
  5011.         }

  5012.         if (enc.isDummy()) {
  5013.             return context.nil;
  5014.         }
  5015.         encidx = enc;

  5016.         if (enc.isAsciiCompatible()) {
  5017.             byte[] pBytes = value.unsafeBytes();
  5018.             int p = value.begin();
  5019.             int e = p + value.getRealSize();
  5020.             int p1 = p;
  5021.             byte[] repBytes;
  5022.             int rep;
  5023.             int replen;
  5024.             boolean rep7bit_p;
  5025.             IRubyObject buf = context.nil;
  5026.             if (block.isGiven()) {
  5027.                 repBytes = null;
  5028.                 rep = 0;
  5029.                 replen = 0;
  5030.                 rep7bit_p = false;
  5031.             }
  5032.             else if (!repl.isNil()) {
  5033.                 repBytes = ((RubyString)repl).value.unsafeBytes();
  5034.                 rep = ((RubyString)repl).value.begin();
  5035.                 replen = ((RubyString)repl).value.getRealSize();
  5036.                 rep7bit_p = (((RubyString)repl).getCodeRange() == CR_7BIT);
  5037.             }
  5038.             else if (encidx == UTF8Encoding.INSTANCE) {
  5039.                 repBytes = SCRUB_REPL_UTF8;
  5040.                 rep = 0;
  5041.                 replen = repBytes.length;
  5042.                 rep7bit_p = false;
  5043.             }
  5044.             else {
  5045.                 repBytes = SCRUB_REPL_ASCII;
  5046.                 rep = 0;
  5047.                 replen = repBytes.length;
  5048.                 rep7bit_p = false;
  5049.             }
  5050.             cr = CR_7BIT;

  5051.             p = StringSupport.searchNonAscii(pBytes, p, e);
  5052.             if (p == -1) {
  5053.                 p = e;
  5054.             }
  5055.             while (p < e) {
  5056.                 int ret = enc.length(pBytes, p, e);
  5057.                 if (MBCLEN_NEEDMORE_P(ret)) {
  5058.                     break;
  5059.                 }
  5060.                 else if (MBCLEN_CHARFOUND_P(ret)) {
  5061.                     cr = CR_VALID;
  5062.                     p += MBCLEN_CHARFOUND_LEN(ret);
  5063.                 }
  5064.                 else if (MBCLEN_INVALID_P(ret)) {
  5065.                     /*
  5066.                      * p1~p: valid ascii/multibyte chars
  5067.                      * p ~e: invalid bytes + unknown bytes
  5068.                      */
  5069.                     int clen = enc.maxLength();
  5070.                     if (buf.isNil()) buf = RubyString.newStringLight(runtime, value.getRealSize());
  5071.                     if (p > p1) {
  5072.                         ((RubyString)buf).cat(pBytes, p1, p - p1);
  5073.                     }

  5074.                     if (e - p < clen) clen = e - p;
  5075.                     if (clen <= 2) {
  5076.                         clen = 1;
  5077.                     }
  5078.                     else {
  5079.                         int q = p;
  5080.                         clen--;
  5081.                         for (; clen > 1; clen--) {
  5082.                             ret = enc.length(pBytes, q, q + clen);
  5083.                             if (MBCLEN_NEEDMORE_P(ret)) break;
  5084.                             if (MBCLEN_INVALID_P(ret)) continue;
  5085.                         }
  5086.                     }
  5087.                     if (repBytes != null) {
  5088.                         ((RubyString)buf).cat(repBytes, rep, replen);
  5089.                         if (!rep7bit_p) cr = CR_VALID;
  5090.                     }
  5091.                     else {
  5092.                         repl = block.yieldSpecific(context, RubyString.newString(runtime, pBytes, p, clen, enc));
  5093.                         repl = EncodingUtils.strCompatAndValid(context, repl, enc);
  5094.                         ((RubyString)buf).cat((RubyString)repl);
  5095.                         if (((RubyString)repl).getCodeRange() == CR_VALID)
  5096.                             cr = CR_VALID;
  5097.                     }
  5098.                     p += clen;
  5099.                     p1 = p;
  5100.                     p = StringSupport.searchNonAscii(pBytes, p, e);
  5101.                     if (p == -1) {
  5102.                         p = e;
  5103.                         break;
  5104.                     }
  5105.                 }
  5106.             }
  5107.             if (buf.isNil()) {
  5108.                 if (p == e) {
  5109.                     setCodeRange(cr);
  5110.                     return context.nil;
  5111.                 }
  5112.                 buf = RubyString.newStringLight(runtime, value.getRealSize());
  5113.             }
  5114.             if (p1 < p) {
  5115.                 ((RubyString)buf).cat(pBytes, p1, p - p1);
  5116.             }
  5117.             if (p < e) {
  5118.                 if (repBytes != null) {
  5119.                     ((RubyString)buf).cat(repBytes, rep, replen);
  5120.                     if (!rep7bit_p) cr = CR_VALID;
  5121.                 }
  5122.                 else {
  5123.                     repl = block.yieldSpecific(context, RubyString.newString(runtime, pBytes, p, e - p, enc));
  5124.                     repl = EncodingUtils.strCompatAndValid(context, repl, enc);
  5125.                     ((RubyString)buf).cat((RubyString)repl);
  5126.                     if (((RubyString)repl).getCodeRange() == CR_VALID)
  5127.                         cr = CR_VALID;
  5128.                 }
  5129.             }
  5130.             ((RubyString)buf).setEncodingAndCodeRange(enc, cr);
  5131.             return buf;
  5132.         }
  5133.         else {
  5134.             /* ASCII incompatible */
  5135.             byte[] pBytes = value.unsafeBytes();
  5136.             int p = value.begin();
  5137.             int e = p + value.getRealSize();
  5138.             int p1 = p;
  5139.             IRubyObject buf = context.nil;
  5140.             byte[] repBytes;
  5141.             int rep;
  5142.             int replen;
  5143.             int mbminlen = enc.minLength();
  5144.             if (!repl.isNil()) {
  5145.                 repBytes = ((RubyString)repl).value.unsafeBytes();
  5146.                 rep = ((RubyString)repl).value.begin();
  5147.                 replen = ((RubyString)repl).value.getRealSize();
  5148.             }
  5149.             else if (encidx == UTF16BEEncoding.INSTANCE) {
  5150.                 repBytes = SCRUB_REPL_UTF16BE;
  5151.                 rep = 0;
  5152.                 replen = repBytes.length;
  5153.             }
  5154.             else if (encidx == UTF16LEEncoding.INSTANCE) {
  5155.                 repBytes = SCRUB_REPL_UTF16LE;
  5156.                 rep = 0;
  5157.                 replen = repBytes.length;
  5158.             }
  5159.             else if (encidx == UTF32BEEncoding.INSTANCE) {
  5160.                 repBytes = SCRUB_REPL_UTF32BE;
  5161.                 rep = 0;
  5162.                 replen = repBytes.length;
  5163.             }
  5164.             else if (encidx == UTF32LEEncoding.INSTANCE) {
  5165.                 repBytes = SCRUB_REPL_UTF32LE;
  5166.                 rep = 0;
  5167.                 replen = repBytes.length;
  5168.             }
  5169.             else {
  5170.                 repBytes = SCRUB_REPL_ASCII;
  5171.                 rep = 0;
  5172.                 replen = repBytes.length;
  5173.             }

  5174.             while (p < e) {
  5175.                 int ret = enc.length(pBytes, p, e);
  5176.                 if (MBCLEN_NEEDMORE_P(ret)) {
  5177.                     break;
  5178.                 }
  5179.                 else if (MBCLEN_CHARFOUND_P(ret)) {
  5180.                     p += MBCLEN_CHARFOUND_LEN(ret);
  5181.                 }
  5182.                 else if (MBCLEN_INVALID_P(ret)) {
  5183.                     int q = p;
  5184.                     int clen = enc.maxLength();
  5185.                     if (buf.isNil()) buf = RubyString.newStringLight(runtime, value.getRealSize());
  5186.                     if (p > p1) ((RubyString)buf).cat(pBytes, p1, p - p1);

  5187.                     if (e - p < clen) clen = e - p;
  5188.                     if (clen <= mbminlen * 2) {
  5189.                         clen = mbminlen;
  5190.                     }
  5191.                     else {
  5192.                         clen -= mbminlen;
  5193.                         for (; clen > mbminlen; clen-=mbminlen) {
  5194.                             ret = enc.length(pBytes, q, q + clen);
  5195.                             if (MBCLEN_NEEDMORE_P(ret)) break;
  5196.                             if (MBCLEN_INVALID_P(ret)) continue;
  5197.                         }
  5198.                     }
  5199.                     if (repBytes != null) {
  5200.                         ((RubyString)buf).cat(repBytes, rep, replen);
  5201.                     }
  5202.                     else {
  5203.                         repl = block.yieldSpecific(context, RubyString.newString(runtime, pBytes, p, e-p, enc));
  5204.                         repl = EncodingUtils.strCompatAndValid(context, repl, enc);
  5205.                         ((RubyString)buf).cat((RubyString)repl);
  5206.                     }
  5207.                     p += clen;
  5208.                     p1 = p;
  5209.                 }
  5210.             }
  5211.             if (buf.isNil()) {
  5212.                 if (p == e) {
  5213.                     setCodeRange(CR_VALID);
  5214.                     return context.nil;
  5215.                 }
  5216.                 buf = RubyString.newStringLight(runtime, value.getRealSize());
  5217.             }
  5218.             if (p1 < p) {
  5219.                 ((RubyString)buf).cat(pBytes, p1, p - p1);
  5220.             }
  5221.             if (p < e) {
  5222.                 if (repBytes != null) {
  5223.                     ((RubyString)buf).cat(repBytes, rep, replen);
  5224.                 }
  5225.                 else {
  5226.                     repl = block.yieldSpecific(context, RubyString.newString(runtime, pBytes, p, e - p, enc));
  5227.                     repl = EncodingUtils.strCompatAndValid(context, repl, enc);
  5228.                     ((RubyString)buf).cat((RubyString)repl);
  5229.                 }
  5230.             }
  5231.             ((RubyString)buf).setEncodingAndCodeRange(enc, CR_VALID);
  5232.             return buf;
  5233.         }
  5234.     }

  5235.     @Deprecated
  5236.     public final RubyString strDup() {
  5237.         return strDup(getRuntime(), getMetaClass().getRealClass());
  5238.     }

  5239.     @Deprecated
  5240.     final RubyString strDup(RubyClass clazz) {
  5241.         return strDup(getRuntime(), getMetaClass());
  5242.     }

  5243.     @Deprecated
  5244.     public final void modify19(int length) {
  5245.         modifyExpand(length);
  5246.     }
  5247. }