12
12
import java .util .Arrays ;
13
13
import java .util .Objects ;
14
14
15
+ import com .oracle .truffle .api .strings .InternalByteArray ;
15
16
import com .oracle .truffle .api .strings .TruffleString ;
17
+ import org .truffleruby .core .array .ArrayUtils ;
16
18
import org .truffleruby .core .encoding .RubyEncoding ;
19
+ import org .truffleruby .core .encoding .TStringUtils ;
17
20
18
21
public final class TBytesKey {
19
22
20
23
private final byte [] bytes ;
24
+ private final int offset ;
25
+ private final int length ;
21
26
private RubyEncoding encoding ;
22
27
private final int bytesHashCode ;
23
28
24
- public TBytesKey (byte [] bytes , RubyEncoding encoding ) {
29
+ public TBytesKey (byte [] bytes , int offset , int length , int bytesHashCode , RubyEncoding encoding ) {
25
30
this .bytes = bytes ;
31
+ this .offset = offset ;
32
+ this .length = length ;
33
+ this .bytesHashCode = bytesHashCode ;
26
34
this .encoding = encoding ;
27
- this .bytesHashCode = Arrays .hashCode (bytes );
35
+ }
36
+
37
+ public TBytesKey (byte [] bytes , RubyEncoding encoding ) {
38
+ this (bytes , 0 , bytes .length , Arrays .hashCode (bytes ), encoding );
39
+ }
40
+
41
+ public TBytesKey (InternalByteArray byteArray , RubyEncoding encoding ) {
42
+ this (byteArray .getArray (), byteArray .getOffset (), byteArray .getLength (), hashCode (byteArray ), encoding );
28
43
}
29
44
30
45
@ Override
@@ -37,15 +52,15 @@ public boolean equals(Object o) {
37
52
if (o instanceof TBytesKey ) {
38
53
final TBytesKey other = (TBytesKey ) o ;
39
54
if (encoding == null ) {
40
- if (Arrays . equals ( bytes , other . bytes )) {
55
+ if (equalBytes ( this , other )) {
41
56
// For getMatchedEncoding()
42
57
this .encoding = Objects .requireNonNull (other .encoding );
43
58
return true ;
44
59
} else {
45
60
return false ;
46
61
}
47
62
} else {
48
- return encoding == other .encoding && Arrays . equals ( bytes , other . bytes );
63
+ return encoding == other .encoding && equalBytes ( this , other );
49
64
}
50
65
}
51
66
@@ -62,4 +77,48 @@ public String toString() {
62
77
return TruffleString .fromByteArrayUncached (bytes , encoding , false ).toString ();
63
78
}
64
79
80
+ private static int hashCode (InternalByteArray byteArray ) {
81
+ return hashCode (byteArray .getArray (), byteArray .getOffset (), byteArray .getLength ());
82
+ }
83
+
84
+ // A variant of <code>Arrays.hashCode</code> that allows for selecting a range within the array.
85
+ private static int hashCode (byte [] bytes , int offset , int length ) {
86
+ if (bytes == null ) {
87
+ return 0 ;
88
+ }
89
+
90
+ int result = 1 ;
91
+ for (int i = offset ; i < offset + length ; i ++) {
92
+ result = 31 * result + bytes [i ];
93
+ }
94
+
95
+ return result ;
96
+ }
97
+
98
+ private boolean equalBytes (TBytesKey a , TBytesKey b ) {
99
+ return Arrays .equals (a .bytes , a .offset , a .offset + a .length , b .bytes , b .offset , b .offset + b .length );
100
+ }
101
+
102
+ private boolean isPerfectFit () {
103
+ return offset == 0 && length == bytes .length ;
104
+ }
105
+
106
+ public TBytesKey makeCacheable () {
107
+ if (isPerfectFit ()) {
108
+ // TODO (nirvdrum 2023-Jun-17): We can avoid cloning the key if we know the byte array came from an immutable string.
109
+ return new TBytesKey (bytes .clone (), encoding );
110
+ }
111
+
112
+ var simplified = ArrayUtils .extractRange (this .bytes , this .offset , this .offset + this .length );
113
+ return new TBytesKey (simplified , encoding );
114
+ }
115
+
116
+ public TBytesKey withNewEncoding (RubyEncoding encoding ) {
117
+ return new TBytesKey (bytes , offset , length , bytesHashCode , encoding );
118
+ }
119
+
120
+ public TruffleString toTruffleString () {
121
+ return TStringUtils .fromByteArray (bytes , offset , length , encoding .tencoding );
122
+ }
123
+
65
124
}
0 commit comments