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 }