1 /*
   2  * Copyright (c) 2010, 2016, 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 java.lang;
  27 
  28 import java.io.DataInputStream;
  29 import java.io.InputStream;
  30 import java.lang.ref.SoftReference;
  31 import java.util.Arrays;
  32 import java.util.zip.InflaterInputStream;
  33 import java.security.AccessController;
  34 import java.security.PrivilegedAction;
  35 
  36 import sun.nio.cs.ISO_8859_1;
  37 
  38 class CharacterName {
  39 
  40     private static SoftReference<CharacterName> refCharName;
  41 
  42     // codepoint -> bkIndex -> lookup -> offset/len
  43     private final byte[] strPool;
  44     private final int[] lookup;      // code point -> offset/len in strPool
  45     private final int[] bkIndices;   // code point -> lookup index
  46 
  47     // name -> hash -> hsIndices -> cpEntries -> code point
  48     private final int[] cpEntries;   // code points that have name in strPool
  49     private final int[] hsIndices;   // chain heads, hash indices into "cps"
  50 
  51     private CharacterName()  {
  52         try (DataInputStream dis = new DataInputStream(new InflaterInputStream(
  53             AccessController.doPrivileged(new PrivilegedAction<>() {
  54                 public InputStream run() {
  55                     return getClass().getResourceAsStream("uniName.dat");
  56                 }
  57             })))) {
  58 
  59             int total = dis.readInt();
  60             int bkNum = dis.readInt();
  61             int cpNum = dis.readInt();
  62             int cpEnd = dis.readInt();
  63             byte ba[] = new byte[cpEnd];
  64             lookup = new int[bkNum * 256];
  65             bkIndices = new int[(Character.MAX_CODE_POINT + 1) >> 8];
  66             strPool = new byte[total - cpEnd];
  67             cpEntries = new int[cpNum * 3];
  68             hsIndices = new int[(cpNum / 2) | 1];
  69             Arrays.fill(bkIndices, -1);
  70             Arrays.fill(hsIndices, -1);
  71             dis.readFully(ba);
  72             dis.readFully(strPool);
  73 
  74             int nameOff = 0;
  75             int cpOff = 0;
  76             int cp = 0;
  77             int bk = -1;
  78             int prevBk = -1;   // prev bkNo;
  79             int idx = 0;
  80             int next = -1;
  81             int hash = 0;
  82             int hsh = 0;
  83             do {
  84                 int len = ba[cpOff++] & 0xff;
  85                 if (len == 0) {
  86                     len = ba[cpOff++] & 0xff;
  87                     // always big-endian
  88                     cp = ((ba[cpOff++] & 0xff) << 16) |
  89                          ((ba[cpOff++] & 0xff) <<  8) |
  90                          ((ba[cpOff++] & 0xff));
  91                 }  else {
  92                     cp++;
  93                 }
  94                 // cp -> name
  95                 int hi = cp >> 8;
  96                 if (prevBk != hi) {
  97                     bk++;
  98                     bkIndices[hi] = bk;
  99                     prevBk = hi;
 100                 }
 101                 lookup[(bk << 8) + (cp & 0xff)] = (nameOff << 8) | len;
 102                 // name -> cp
 103                 hash = hashN(strPool, nameOff, len);
 104                 hsh = (hash & 0x7fffffff) % hsIndices.length;
 105                 next = hsIndices[hsh];
 106                 hsIndices[hsh] = idx;
 107                 idx = addCp(idx, hash, next, cp);
 108                 nameOff += len;
 109             } while (cpOff < cpEnd);
 110         } catch (Exception x) {
 111             throw new InternalError(x.getMessage(), x);
 112         }
 113     }
 114 
 115     private static final int hashN(byte[] a, int off, int len) {
 116         int h = 1;
 117         while (len-- > 0) {
 118             h = 31 * h + a[off++];
 119         }
 120         return h;
 121     }
 122 
 123     private int addCp(int idx, int hash, int next, int cp) {
 124         cpEntries[idx++] = hash;
 125         cpEntries[idx++] = next;
 126         cpEntries[idx++] = cp;
 127         return idx;
 128     }
 129 
 130     private int getCpHash(int idx) { return cpEntries[idx]; }
 131     private int getCpNext(int idx) { return cpEntries[idx + 1]; }
 132     private int getCp(int idx)  { return cpEntries[idx + 2]; }
 133 
 134     public static CharacterName getInstance() {
 135         SoftReference<CharacterName> ref = refCharName;
 136         CharacterName cname = null;
 137         if (ref == null || (cname = ref.get()) == null) {
 138             cname = new CharacterName();
 139             refCharName = new SoftReference<>(cname);
 140         }
 141         return cname;
 142     }
 143 
 144     public String getName(int cp) {
 145         int off = 0;
 146         int bk = bkIndices[cp >> 8];
 147         if (bk == -1 || (off = lookup[(bk << 8) + (cp & 0xff)]) == 0)
 148             return null;
 149         @SuppressWarnings("deprecation")
 150         String result = new String(strPool, 0, off >>> 8, off & 0xff);  // ASCII
 151         return result;
 152     }
 153 
 154     public int getCodePoint(String name) {
 155         byte[] bname = name.getBytes(ISO_8859_1.INSTANCE);
 156         int hsh = hashN(bname, 0, bname.length);
 157         int idx = hsIndices[(hsh & 0x7fffffff) % hsIndices.length];
 158         while (idx != -1) {
 159             if (getCpHash(idx) == hsh) {
 160                 int cp = getCp(idx);
 161                 int off = -1;
 162                 int bk = bkIndices[cp >> 8];
 163                 if (bk != -1 && (off = lookup[(bk << 8) + (cp & 0xff)]) != 0) {
 164                     int len = off & 0xff;
 165                     off = off >>> 8;
 166                     if (bname.length == len) {
 167                         int i = 0;
 168                         while (i < len && bname[i] == strPool[off++]) {
 169                             i++;
 170                         }
 171                         if (i == len) {
 172                             return cp;
 173                         }
 174                     }
 175                  }
 176             }
 177             idx = getCpNext(idx);
 178         }
 179         return -1;
 180     }
 181 }