1 /* 2 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.internal.jimage; 27 28 import java.nio.charset.Charset; 29 import java.util.Arrays; 30 31 public final class UTF8String implements CharSequence { 32 33 // Same as StandardCharsets.UTF_8 without loading all of the standard charsets 34 static final Charset UTF_8 = Charset.forName("UTF-8"); 35 36 static final int NOT_FOUND = -1; 37 static final int HASH_MULTIPLIER = 0x01000193; 38 static final UTF8String EMPTY_STRING = new UTF8String(""); 39 static final UTF8String CLASS_STRING = new UTF8String(".class"); 40 41 final byte[] bytes; 42 final int offset; 43 final int count; 44 int hashcode; 45 46 public UTF8String(byte[] bytes, int offset, int count) { 47 if (offset < 0 || count < 0 || (offset + count) > bytes.length) { 48 throw new IndexOutOfBoundsException("offset/count out of range"); 49 } 50 this.bytes = bytes; 51 this.offset = offset; 52 this.count = count; 53 this.hashcode = -1; 54 } 55 56 public UTF8String(byte[] bytes, int offset) { 57 this(bytes, offset, bytes.length - offset); 58 } 59 60 public UTF8String(byte[] bytes) { 61 this(bytes, 0, bytes.length); 62 } 63 64 public UTF8String(String string) { 65 this(stringToBytes(string)); 66 } 67 68 @Override 69 public int length() { 70 return count; 71 } 72 73 public boolean isEmpty() { 74 return count == 0; 75 } 76 77 public int byteAt(int index) { 78 return bytes[offset + index] & 0xFF; 79 } 80 81 public UTF8String concat(UTF8String s) { 82 int total = count + s.count; 83 byte[] combined = new byte[total]; 84 System.arraycopy(bytes, offset, combined, 0, count); 85 System.arraycopy(s.bytes, s.offset, combined, count, s.count); 86 87 return new UTF8String(combined, 0, total); 88 } 89 90 public UTF8String concat(UTF8String... s) { 91 int total = count; 92 93 for (UTF8String i : s) { 94 total += i.count; 95 } 96 97 byte[] combined = new byte[total]; 98 System.arraycopy(bytes, offset, combined, 0, count); 99 int next = count; 100 101 for (UTF8String i : s) { 102 System.arraycopy(i.bytes, i.offset, combined, next, i.count); 103 next += i.count; 104 } 105 106 return new UTF8String(combined, 0, total); 107 } 108 109 public UTF8String substring(int offset) { 110 return substring(offset, this.count - offset); 111 } 112 113 public UTF8String substring(int offset, int count) { 114 int newOffset = this.offset + offset; 115 return new UTF8String(bytes, newOffset, count); 116 } 117 118 public UTF8String trimToSize() { 119 return offset == 0 && bytes.length == count ? this : 120 new UTF8String(Arrays.copyOfRange(bytes, offset, offset + count)); 121 } 122 123 public int indexOf(int ch) { 124 return indexOf(ch, 0); 125 } 126 127 public int indexOf(int ch, int start) { 128 for (int i = Math.max(start, 0); i < count; i++) { 129 if (byteAt(i) == ch) { 130 return i; 131 } 132 } 133 134 return NOT_FOUND; 135 } 136 137 public int lastIndexOf(int ch) { 138 return lastIndexOf(ch, count - 1); 139 } 140 141 public int lastIndexOf(int ch, int start) { 142 for (int i = Math.min(start, count); i > 0; i--) { 143 if (byteAt(i) == ch) { 144 return i; 145 } 146 } 147 148 return NOT_FOUND; 149 } 150 151 void writeTo(ImageStream buffer) { 152 buffer.put(bytes, offset, count); 153 } 154 155 static int hashCode(int seed, byte[] bytes, int offset, int count) { 156 for (int i = offset, limit = offset + count; i < limit; i++) { 157 seed = (seed * HASH_MULTIPLIER) ^ (bytes[i] & 0xFF); 158 } 159 160 return seed & 0x7FFFFFFF; 161 } 162 163 int hashCode(int base) { 164 return hashCode(base, bytes, offset, count); 165 } 166 167 @Override 168 public int hashCode() { 169 if (hashcode < 0) { 170 hashcode = hashCode(HASH_MULTIPLIER, bytes, offset, count); 171 } 172 173 return hashcode; 174 } 175 176 @Override 177 public boolean equals(Object obj) { 178 if (obj == null) { 179 return false; 180 } 181 182 if (getClass() != obj.getClass()) { 183 return false; 184 } 185 186 return equals(this, (UTF8String)obj); 187 } 188 189 private static boolean equals(UTF8String a, UTF8String b) { 190 if (a == b) { 191 return true; 192 } 193 194 int count = a.count; 195 196 if (count != b.count) { 197 return false; 198 } 199 200 byte[] aBytes = a.bytes; 201 byte[] bBytes = b.bytes; 202 int aOffset = a.offset; 203 int bOffset = b.offset; 204 205 for (int i = 0; i < count; i++) { 206 if (aBytes[aOffset + i] != bBytes[bOffset + i]) { 207 return false; 208 } 209 } 210 211 return true; 212 } 213 214 byte[] getBytes() { 215 if (offset != 0 || bytes.length != count) { 216 return Arrays.copyOfRange(bytes, offset, offset + count); 217 } 218 219 return bytes; 220 } 221 222 private static byte[] stringToBytes(String string) { 223 return string.getBytes(UTF_8); 224 } 225 226 @Override 227 public String toString() { 228 return new String(bytes, offset, count, UTF_8); 229 } 230 231 @Override 232 public char charAt(int index) { 233 int ch = byteAt(index); 234 235 return (ch & 0x80) != 0 ? (char)ch : '\0'; 236 } 237 238 @Override 239 public CharSequence subSequence(int start, int end) { 240 return (CharSequence)substring(start, end - start); 241 } 242 243 static UTF8String match(UTF8String a, UTF8String b) { 244 int aCount = a.count; 245 int bCount = b.count; 246 247 if (aCount < bCount) { 248 return null; 249 } 250 251 byte[] aBytes = a.bytes; 252 byte[] bBytes = b.bytes; 253 int aOffset = a.offset; 254 int bOffset = b.offset; 255 256 for (int i = 0; i < bCount; i++) { 257 if (aBytes[aOffset + i] != bBytes[bOffset + i]) { 258 return null; 259 } 260 } 261 262 return new UTF8String(aBytes, aOffset + bCount, aCount - bCount); 263 } 264 }