Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: ab7b0a06e1c9
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 7a0a5aaba4ff
Choose a head ref
  • 3 commits
  • 9 files changed
  • 1 contributor

Commits on Mar 5, 2016

  1. [Truffle] Pass the correct encoding through to Rubinius::Mirror::Stri…

    …ng#splice.
    
    Prior to this change, it was expected the caller would splice a string and then set the correct encoding for the final product. In the time in between, the String could be invalid. When we simply modified the underlying byte[] this was an acceptable trade-off. However, now that we're using ropes, it's possible the splice operation ends up using a bad specialized version of a rope operation, as the rope operations are encoding-aware.
    nirvdrum committed Mar 5, 2016
    Copy the full SHA
    127d742 View commit details
  2. [Truffle] Added a single-byte rope cache.

    This cuts down creating duplicate ropes for common cases such as indexing into a byte-optimizable String.
    nirvdrum committed Mar 5, 2016
    Copy the full SHA
    7fbb658 View commit details
  3. Copy the full SHA
    7a0a5aa View commit details
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/

package org.jruby.truffle.core.rope;

import org.jcodings.specific.ASCIIEncoding;
import org.jcodings.specific.USASCIIEncoding;
import org.jcodings.specific.UTF8Encoding;

import static org.jruby.truffle.core.rope.CodeRange.CR_7BIT;

public class RopeConstants {

public static final LeafRope EMPTY_ASCII_8BIT_ROPE;
public static final LeafRope EMPTY_US_ASCII_ROPE;
public static final LeafRope EMPTY_UTF8_ROPE;

public static final LeafRope[] UTF8_SINGLE_BYTE_ROPES = new LeafRope[256];
public static final LeafRope[] US_ASCII_SINGLE_BYTE_ROPES = new LeafRope[256];
public static final LeafRope[] ASCII_8BIT_SINGLE_BYTE_ROPES = new LeafRope[256];

static {
final byte[] emptyBytes = new byte[] {};

EMPTY_UTF8_ROPE = new AsciiOnlyLeafRope(emptyBytes, UTF8Encoding.INSTANCE);
EMPTY_US_ASCII_ROPE = new AsciiOnlyLeafRope(emptyBytes, USASCIIEncoding.INSTANCE);
EMPTY_ASCII_8BIT_ROPE = new AsciiOnlyLeafRope(emptyBytes, ASCIIEncoding.INSTANCE);

for (int i = 0; i < 128; i++) {
final byte[] bytes = new byte[] { (byte) i };

UTF8_SINGLE_BYTE_ROPES[i] = new AsciiOnlyLeafRope(bytes, UTF8Encoding.INSTANCE);
US_ASCII_SINGLE_BYTE_ROPES[i] = new AsciiOnlyLeafRope(bytes, USASCIIEncoding.INSTANCE);
ASCII_8BIT_SINGLE_BYTE_ROPES[i] = new AsciiOnlyLeafRope(bytes, ASCIIEncoding.INSTANCE);
}

for (int i = 128; i < 256; i++) {
final byte[] bytes = new byte[] { (byte) i };

UTF8_SINGLE_BYTE_ROPES[i] = new InvalidLeafRope(bytes, UTF8Encoding.INSTANCE);
US_ASCII_SINGLE_BYTE_ROPES[i] = new InvalidLeafRope(bytes, USASCIIEncoding.INSTANCE);
ASCII_8BIT_SINGLE_BYTE_ROPES[i] = new ValidLeafRope(bytes, ASCIIEncoding.INSTANCE, 1);
}
}

}
71 changes: 51 additions & 20 deletions truffle/src/main/java/org/jruby/truffle/core/rope/RopeNodes.java
Original file line number Diff line number Diff line change
@@ -55,45 +55,70 @@ public MakeSubstringNode(RubyContext context, SourceSection sourceSection) {
public abstract Rope executeMake(Rope base, int offset, int byteLength);

@Specialization(guards = "byteLength == 0")
public Rope substringZeroLength(Rope base, int offset, int byteLength,
public Rope substringZeroBytes(Rope base, int offset, int byteLength,
@Cached("createBinaryProfile()") ConditionProfile isUTF8,
@Cached("createBinaryProfile()") ConditionProfile isUSAscii,
@Cached("createBinaryProfile()") ConditionProfile isAscii8Bit) {
if (isUTF8.profile(base.getEncoding() == UTF8Encoding.INSTANCE)) {
return RopeOperations.EMPTY_UTF8_ROPE;
return RopeConstants.EMPTY_UTF8_ROPE;
}

if (isUSAscii.profile(base.getEncoding() == USASCIIEncoding.INSTANCE)) {
return RopeOperations.EMPTY_US_ASCII_ROPE;
return RopeConstants.EMPTY_US_ASCII_ROPE;
}

if (isAscii8Bit.profile(base.getEncoding() == ASCIIEncoding.INSTANCE)) {
return RopeOperations.EMPTY_ASCII_8BIT_ROPE;
return RopeConstants.EMPTY_ASCII_8BIT_ROPE;
}

return RopeOperations.withEncoding(RopeOperations.EMPTY_UTF8_ROPE, base.getEncoding());
return RopeOperations.withEncoding(RopeConstants.EMPTY_UTF8_ROPE, base.getEncoding());
}

@Specialization(guards = { "byteLength > 0", "sameAsBase(base, offset, byteLength)" })
@Specialization(guards = "byteLength == 1")
public Rope substringOneByte(Rope base, int offset, int byteLength,
@Cached("createBinaryProfile()") ConditionProfile isUTF8,
@Cached("createBinaryProfile()") ConditionProfile isUSAscii,
@Cached("createBinaryProfile()") ConditionProfile isAscii8Bit) {
final int index = base.get(offset) & 0xff;

if (isUTF8.profile(base.getEncoding() == UTF8Encoding.INSTANCE)) {
return RopeConstants.UTF8_SINGLE_BYTE_ROPES[index];
}

if (isUSAscii.profile(base.getEncoding() == USASCIIEncoding.INSTANCE)) {
return RopeConstants.US_ASCII_SINGLE_BYTE_ROPES[index];
}

if (isAscii8Bit.profile(base.getEncoding() == ASCIIEncoding.INSTANCE)) {
return RopeConstants.ASCII_8BIT_SINGLE_BYTE_ROPES[index];
}

return RopeOperations.withEncoding(RopeConstants.ASCII_8BIT_SINGLE_BYTE_ROPES[index], base.getEncoding());
}

@Specialization(guards = { "byteLength > 1", "sameAsBase(base, offset, byteLength)" })
public Rope substringSameAsBase(Rope base, int offset, int byteLength) {
return base;
}

@Specialization(guards = { "byteLength > 0", "!sameAsBase(base, offset, byteLength)" })
@Specialization(guards = { "byteLength > 1", "!sameAsBase(base, offset, byteLength)" })
public Rope substringLeafRope(LeafRope base, int offset, int byteLength,
@Cached("createBinaryProfile()") ConditionProfile is7BitProfile) {
return makeSubstring(base, offset, byteLength, is7BitProfile);
@Cached("createBinaryProfile()") ConditionProfile is7BitProfile,
@Cached("createBinaryProfile()") ConditionProfile isBinaryStringProfile) {
return makeSubstring(base, offset, byteLength, is7BitProfile, isBinaryStringProfile);
}

@Specialization(guards = { "byteLength > 0", "!sameAsBase(base, offset, byteLength)" })
@Specialization(guards = { "byteLength > 1", "!sameAsBase(base, offset, byteLength)" })
public Rope substringSubstringRope(SubstringRope base, int offset, int byteLength,
@Cached("createBinaryProfile()") ConditionProfile is7BitProfile) {
return makeSubstring(base.getChild(), offset + base.getOffset(), byteLength, is7BitProfile);
@Cached("createBinaryProfile()") ConditionProfile is7BitProfile,
@Cached("createBinaryProfile()") ConditionProfile isBinaryStringProfile) {
return makeSubstring(base.getChild(), offset + base.getOffset(), byteLength, is7BitProfile, isBinaryStringProfile);
}

@Specialization(guards = { "byteLength > 0", "!sameAsBase(base, offset, byteLength)" })
@Specialization(guards = { "byteLength > 1", "!sameAsBase(base, offset, byteLength)" })
public Rope substringConcatRope(ConcatRope base, int offset, int byteLength,
@Cached("createBinaryProfile()") ConditionProfile is7BitProfile) {
@Cached("createBinaryProfile()") ConditionProfile is7BitProfile,
@Cached("createBinaryProfile()") ConditionProfile isBinaryStringProfile) {
Rope root = base;

while (root instanceof ConcatRope) {
@@ -118,18 +143,24 @@ public Rope substringConcatRope(ConcatRope base, int offset, int byteLength,
if (byteLength == root.byteLength()) {
return root;
} else {
return makeSubstring(root, offset, byteLength, is7BitProfile);
return makeSubstring(root, offset, byteLength, is7BitProfile, isBinaryStringProfile);
}
}

return makeSubstring(root, offset, byteLength, is7BitProfile);
return makeSubstring(root, offset, byteLength, is7BitProfile, isBinaryStringProfile);
}

private Rope makeSubstring(Rope base, int offset, int byteLength, ConditionProfile is7BitProfile) {
private Rope makeSubstring(Rope base, int offset, int byteLength, ConditionProfile is7BitProfile, ConditionProfile isBinaryStringProfile) {
if (is7BitProfile.profile(base.getCodeRange() == CR_7BIT)) {
return new SubstringRope(base, offset, byteLength, byteLength, CR_7BIT);
}

// We short-circuit here to avoid the costly process of recalculating information we already know, such as
// whether the string has a valid code range.
if (isBinaryStringProfile.profile(base.getEncoding() == ASCIIEncoding.INSTANCE)) {
return new SubstringRope(base, offset, byteLength, byteLength, CR_VALID);
}

return makeSubstringNon7Bit(base, offset, byteLength);
}

@@ -359,15 +390,15 @@ public LeafRope makeUnknownLeafRopeEmpty(byte[] bytes, Encoding encoding, CodeRa
@Cached("createBinaryProfile()") ConditionProfile isAscii8Bit,
@Cached("createBinaryProfile()") ConditionProfile isAsciiCompatible) {
if (isUTF8.profile(encoding == UTF8Encoding.INSTANCE)) {
return RopeOperations.EMPTY_UTF8_ROPE;
return RopeConstants.EMPTY_UTF8_ROPE;
}

if (isUSAscii.profile(encoding == USASCIIEncoding.INSTANCE)) {
return RopeOperations.EMPTY_US_ASCII_ROPE;
return RopeConstants.EMPTY_US_ASCII_ROPE;
}

if (isAscii8Bit.profile(encoding == ASCIIEncoding.INSTANCE)) {
return RopeOperations.EMPTY_ASCII_8BIT_ROPE;
return RopeConstants.EMPTY_ASCII_8BIT_ROPE;
}

if (isAsciiCompatible.profile(encoding.isAsciiCompatible())) {
Original file line number Diff line number Diff line change
@@ -36,13 +36,25 @@

public class RopeOperations {

public static final LeafRope EMPTY_ASCII_8BIT_ROPE = create(new byte[] {}, ASCIIEncoding.INSTANCE, CR_7BIT);
public static final LeafRope EMPTY_US_ASCII_ROPE = create(new byte [] {}, USASCIIEncoding.INSTANCE, CR_7BIT);
public static final LeafRope EMPTY_UTF8_ROPE = create(new byte[] {}, UTF8Encoding.INSTANCE, CR_7BIT);

private static final ConcurrentHashMap<Encoding, Charset> encodingToCharsetMap = new ConcurrentHashMap<>();

public static LeafRope create(byte[] bytes, Encoding encoding, CodeRange codeRange) {
if (bytes.length == 1) {
final int index = bytes[0] & 0xff;

if (encoding == UTF8Encoding.INSTANCE) {
return RopeConstants.UTF8_SINGLE_BYTE_ROPES[index];
}

if (encoding == USASCIIEncoding.INSTANCE) {
return RopeConstants.US_ASCII_SINGLE_BYTE_ROPES[index];
}

if (encoding == ASCIIEncoding.INSTANCE) {
return RopeConstants.ASCII_8BIT_SINGLE_BYTE_ROPES[index];
}
}

int characterLength = -1;

if (codeRange == CR_UNKNOWN) {
Original file line number Diff line number Diff line change
@@ -24,9 +24,9 @@
import org.jruby.truffle.core.Layouts;
import org.jruby.truffle.core.encoding.EncodingConverterNodes;
import org.jruby.truffle.core.rope.Rope;
import org.jruby.truffle.core.rope.RopeConstants;
import org.jruby.truffle.core.rope.RopeNodes;
import org.jruby.truffle.core.rope.RopeNodesFactory;
import org.jruby.truffle.core.rope.RopeOperations;
import org.jruby.truffle.core.string.StringOperations;
import org.jruby.truffle.language.NotProvided;
import org.jruby.truffle.language.RubyGuards;
@@ -93,7 +93,7 @@ private Object primitiveConvertHelper(DynamicObject encodingConverter, DynamicOb
// Taken from org.jruby.RubyConverter#primitive_convert.

final boolean nonNullSource = source != nil();
Rope sourceRope = nonNullSource ? rope(source) : RopeOperations.EMPTY_UTF8_ROPE;
Rope sourceRope = nonNullSource ? rope(source) : RopeConstants.EMPTY_UTF8_ROPE;
final Rope targetRope = rope(target);
final ByteList outBytes = targetRope.toByteListCopy();

Original file line number Diff line number Diff line change
@@ -68,6 +68,7 @@
import org.jcodings.Encoding;
import org.jcodings.exception.EncodingException;
import org.jcodings.specific.ASCIIEncoding;
import org.jcodings.specific.USASCIIEncoding;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.truffle.RubyContext;
@@ -78,8 +79,10 @@
import org.jruby.truffle.core.encoding.EncodingOperations;
import org.jruby.truffle.core.rope.CodeRange;
import org.jruby.truffle.core.rope.Rope;
import org.jruby.truffle.core.rope.RopeConstants;
import org.jruby.truffle.core.rope.RopeNodes;
import org.jruby.truffle.core.rope.RopeNodesFactory;
import org.jruby.truffle.core.rope.RopeOperations;
import org.jruby.truffle.core.string.StringGuards;
import org.jruby.truffle.core.string.StringOperations;
import org.jruby.truffle.language.NotProvided;
@@ -677,8 +680,24 @@ public StringFromCodepointPrimitiveNode(RubyContext context, SourceSection sourc
}

@Specialization(guards = {"isRubyEncoding(encoding)", "isSimple(code, encoding)"})
public DynamicObject stringFromCodepointSimple(int code, DynamicObject encoding) {
return createString(new ByteList(new byte[]{(byte) code}, EncodingOperations.getEncoding(encoding)));
public DynamicObject stringFromCodepointSimple(int code, DynamicObject encoding,
@Cached("createBinaryProfile()") ConditionProfile isUTF8Profile,
@Cached("createBinaryProfile()") ConditionProfile isUSAsciiProfile,
@Cached("createBinaryProfile()") ConditionProfile isAscii8BitProfile) {
final Encoding realEncoding = EncodingOperations.getEncoding(encoding);
final Rope rope;

if (isUTF8Profile.profile(realEncoding == UTF8Encoding.INSTANCE)) {
rope = RopeConstants.UTF8_SINGLE_BYTE_ROPES[code];
} else if (isUSAsciiProfile.profile(realEncoding == USASCIIEncoding.INSTANCE)) {
rope = RopeConstants.US_ASCII_SINGLE_BYTE_ROPES[code];
} else if (isAscii8BitProfile.profile(realEncoding == ASCIIEncoding.INSTANCE)) {
rope = RopeConstants.ASCII_8BIT_SINGLE_BYTE_ROPES[code];
} else {
rope = RopeOperations.create(new byte[] { (byte) code }, realEncoding, CodeRange.CR_UNKNOWN);
}

return createString(rope);
}

@TruffleBoundary
@@ -711,13 +730,16 @@ public DynamicObject stringFromCodepoint(int code, DynamicObject encoding) {
}

@Specialization(guards = "isRubyEncoding(encoding)")
public DynamicObject stringFromCodepointSimple(long code, DynamicObject encoding) {
public DynamicObject stringFromCodepointSimple(long code, DynamicObject encoding,
@Cached("createBinaryProfile()") ConditionProfile isUTF8Profile,
@Cached("createBinaryProfile()") ConditionProfile isUSAsciiProfile,
@Cached("createBinaryProfile()") ConditionProfile isAscii8BitProfile) {
if (code < Integer.MIN_VALUE || code > Integer.MAX_VALUE) {
CompilerDirectives.transferToInterpreter();
throw new UnsupportedOperationException();
}

return stringFromCodepointSimple((int) code, encoding);
return stringFromCodepointSimple((int) code, encoding, isUTF8Profile, isUSAsciiProfile, isAscii8BitProfile);
}

protected boolean isSimple(int code, DynamicObject encoding) {
@@ -1302,8 +1324,8 @@ public StringSplicePrimitiveNode(RubyContext context, SourceSection sourceSectio
super(context, sourceSection);
}

@Specialization(guards = { "indexAtStartBound(spliceByteIndex)", "isRubyString(other)" })
public Object splicePrepend(DynamicObject string, DynamicObject other, int spliceByteIndex, int byteCountToReplace) {
@Specialization(guards = { "indexAtStartBound(spliceByteIndex)", "isRubyString(other)", "isRubyEncoding(rubyEncoding)" })
public Object splicePrepend(DynamicObject string, DynamicObject other, int spliceByteIndex, int byteCountToReplace, DynamicObject rubyEncoding) {
if (prependMakeSubstringNode == null) {
CompilerDirectives.transferToInterpreter();
prependMakeSubstringNode = insert(RopeNodesFactory.MakeSubstringNodeGen.create(getContext(), getSourceSection(), null, null, null));
@@ -1314,17 +1336,19 @@ public Object splicePrepend(DynamicObject string, DynamicObject other, int splic
prependMakeConcatNode = insert(RopeNodesFactory.MakeConcatNodeGen.create(getContext(), getSourceSection(), null, null, null));
}

final Encoding encoding = EncodingOperations.getEncoding(rubyEncoding);
final Rope original = rope(string);
final Rope left = rope(other);
final Rope right = prependMakeSubstringNode.executeMake(original, byteCountToReplace, original.byteLength() - byteCountToReplace);

StringOperations.setRope(string, prependMakeConcatNode.executeMake(left, right, right.getEncoding()));
StringOperations.setRope(string, prependMakeConcatNode.executeMake(left, right, encoding));

return string;
}

@Specialization(guards = { "indexAtEndBound(string, spliceByteIndex)", "isRubyString(other)" })
public Object spliceAppend(DynamicObject string, DynamicObject other, int spliceByteIndex, int byteCountToReplace) {
@Specialization(guards = { "indexAtEndBound(string, spliceByteIndex)", "isRubyString(other)", "isRubyEncoding(rubyEncoding)" })
public Object spliceAppend(DynamicObject string, DynamicObject other, int spliceByteIndex, int byteCountToReplace, DynamicObject rubyEncoding) {
final Encoding encoding = EncodingOperations.getEncoding(rubyEncoding);
final Rope left = rope(string);
final Rope right = rope(other);

@@ -1333,13 +1357,13 @@ public Object spliceAppend(DynamicObject string, DynamicObject other, int splice
appendMakeConcatNode = insert(RopeNodesFactory.MakeConcatNodeGen.create(getContext(), getSourceSection(), null, null, null));
}

StringOperations.setRope(string, appendMakeConcatNode.executeMake(left, right, left.getEncoding()));
StringOperations.setRope(string, appendMakeConcatNode.executeMake(left, right, encoding));

return string;
}

@Specialization(guards = { "!indexAtEitherBounds(string, spliceByteIndex)", "isRubyString(other)" })
public DynamicObject splice(DynamicObject string, DynamicObject other, int spliceByteIndex, int byteCountToReplace) {
@Specialization(guards = { "!indexAtEitherBounds(string, spliceByteIndex)", "isRubyString(other)", "isRubyEncoding(rubyEncoding)" })
public DynamicObject splice(DynamicObject string, DynamicObject other, int spliceByteIndex, int byteCountToReplace, DynamicObject rubyEncoding) {
if (leftMakeSubstringNode == null) {
CompilerDirectives.transferToInterpreter();
leftMakeSubstringNode = insert(RopeNodesFactory.MakeSubstringNodeGen.create(getContext(), getSourceSection(), null, null, null));
@@ -1360,14 +1384,15 @@ public DynamicObject splice(DynamicObject string, DynamicObject other, int splic
rightMakeConcatNode = insert(RopeNodesFactory.MakeConcatNodeGen.create(getContext(), getSourceSection(), null, null, null));
}

final Encoding encoding = EncodingOperations.getEncoding(rubyEncoding);
final Rope source = rope(string);
final Rope insert = rope(other);
final int rightSideStartingIndex = spliceByteIndex + byteCountToReplace;

final Rope splitLeft = leftMakeSubstringNode.executeMake(source, 0, spliceByteIndex);
final Rope splitRight = rightMakeSubstringNode.executeMake(source, rightSideStartingIndex, source.byteLength() - rightSideStartingIndex);
final Rope joinedLeft = leftMakeConcatNode.executeMake(splitLeft, insert, source.getEncoding());
final Rope joinedRight = rightMakeConcatNode.executeMake(joinedLeft, splitRight, source.getEncoding());
final Rope joinedLeft = leftMakeConcatNode.executeMake(splitLeft, insert, encoding);
final Rope joinedRight = rightMakeConcatNode.executeMake(joinedLeft, splitRight, encoding);

StringOperations.setRope(string, joinedRight);

Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@
package org.jruby.truffle.core.string;

import com.oracle.truffle.api.object.DynamicObject;
import org.jcodings.specific.ASCIIEncoding;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.truffle.core.Layouts;
import org.jruby.truffle.core.rope.CodeRange;
@@ -71,4 +72,9 @@ public static boolean isBrokenCodeRange(DynamicObject string) {
assert RubyGuards.isRubyString(string);
return StringOperations.codeRange(string) == CodeRange.CR_BROKEN;
}

public static boolean isBinaryString(DynamicObject string) {
assert RubyGuards.isRubyString(string);
return StringOperations.encoding(string) == ASCIIEncoding.INSTANCE;
}
}
Original file line number Diff line number Diff line change
@@ -102,8 +102,8 @@
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

import static org.jruby.truffle.core.rope.RopeOperations.EMPTY_ASCII_8BIT_ROPE;
import static org.jruby.truffle.core.rope.RopeOperations.EMPTY_UTF8_ROPE;
import static org.jruby.truffle.core.rope.RopeConstants.EMPTY_ASCII_8BIT_ROPE;
import static org.jruby.truffle.core.rope.RopeConstants.EMPTY_UTF8_ROPE;

@CoreClass(name = "String")
public abstract class StringNodes {
Original file line number Diff line number Diff line change
@@ -10,8 +10,8 @@ module Rubinius
class Mirror
class String < Mirror

def splice(starting_byte_index, byte_count_to_replace, replacement)
Rubinius.invoke_primitive :string_splice, @object, replacement, starting_byte_index, byte_count_to_replace
def splice(starting_byte_index, byte_count_to_replace, replacement, encoding=nil)
Rubinius.invoke_primitive :string_splice, @object, replacement, starting_byte_index, byte_count_to_replace, (encoding || @object.encoding)
end

end
11 changes: 6 additions & 5 deletions truffle/src/main/ruby/core/rubinius/common/string.rb
Original file line number Diff line number Diff line change
@@ -1427,6 +1427,8 @@ def scrub!(replace = nil, &block)
end

def []=(index, count_or_replacement, replacement=undefined)
Rubinius.check_frozen

if undefined.equal?(replacement)
replacement = count_or_replacement
count = nil
@@ -1468,7 +1470,7 @@ def []=(index, count_or_replacement, replacement=undefined)
replacement = StringValue replacement
enc = Rubinius::Type.compatible_encoding self, replacement

m.splice bi, bs, replacement
m.splice bi, bs, replacement, enc
when String
unless start = m.byte_index(index)
raise IndexError, "string not matched"
@@ -1477,7 +1479,7 @@ def []=(index, count_or_replacement, replacement=undefined)
replacement = StringValue replacement
enc = Rubinius::Type.compatible_encoding self, replacement

m.splice start, index.bytesize, replacement
m.splice start, index.bytesize, replacement, enc
when Range
start = Rubinius::Type.coerce_to index.first, Fixnum, :to_int

@@ -1506,7 +1508,7 @@ def []=(index, count_or_replacement, replacement=undefined)
replacement = StringValue replacement
enc = Rubinius::Type.compatible_encoding self, replacement

m.splice bi, bs, replacement
m.splice bi, bs, replacement, enc
when Regexp
if count
count = Rubinius::Type.coerce_to count, Fixnum, :to_int
@@ -1535,7 +1537,7 @@ def []=(index, count_or_replacement, replacement=undefined)
bi = m.byte_index match.begin(count)
bs = m.byte_index(match.end(count)) - bi

m.splice bi, bs, replacement
m.splice bi, bs, replacement, enc
else
index = Rubinius::Type.coerce_to index, Fixnum, :to_int

@@ -1547,7 +1549,6 @@ def []=(index, count_or_replacement, replacement=undefined)
end

Rubinius::Type.infect self, replacement
force_encoding enc

return replacement
end