• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GENERATED SOURCE. DO NOT MODIFY. */
2 // © 2016 and later: Unicode, Inc. and others.
3 // License & terms of use: http://www.unicode.org/copyright.html#License
4 /*
5  *******************************************************************************
6  * Copyright (C) 2004-2016, International Business Machines Corporation and
7  * others. All Rights Reserved.
8  *******************************************************************************
9  */
10 package ohos.global.icu.impl;
11 
12 import java.io.IOException;
13 import java.io.InputStream;
14 import java.lang.ref.SoftReference;
15 import java.nio.ByteBuffer;
16 import java.nio.CharBuffer;
17 import java.nio.IntBuffer;
18 import java.util.Arrays;
19 
20 import ohos.global.icu.util.ICUException;
21 import ohos.global.icu.util.ICUUncheckedIOException;
22 import ohos.global.icu.util.ULocale;
23 import ohos.global.icu.util.UResourceBundle;
24 import ohos.global.icu.util.UResourceTypeMismatchException;
25 import ohos.global.icu.util.VersionInfo;
26 
27 /**
28  * This class reads the *.res resource bundle format.
29  *
30  * For the file format documentation see ICU4C's source/common/uresdata.h file.
31  * @hide exposed on OHOS
32  */
33 public final class ICUResourceBundleReader {
34     /**
35      * File format version that this class understands.
36      * "ResB"
37      */
38     private static final int DATA_FORMAT = 0x52657342;
39     private static final class IsAcceptable implements ICUBinary.Authenticate {
40         @Override
isDataVersionAcceptable(byte formatVersion[])41         public boolean isDataVersionAcceptable(byte formatVersion[]) {
42             return
43                     (formatVersion[0] == 1 && (formatVersion[1] & 0xff) >= 1) ||
44                     (2 <= formatVersion[0] && formatVersion[0] <= 3);
45         }
46     }
47     private static final IsAcceptable IS_ACCEPTABLE = new IsAcceptable();
48 
49     /* indexes[] value names; indexes are generally 32-bit (Resource) indexes */
50     /**
51      * [0] contains the length of indexes[]
52      * which is at most URES_INDEX_TOP of the latest format version
53      *
54      * formatVersion==1: all bits contain the length of indexes[]
55      *   but the length is much less than 0xff;
56      * formatVersion>1:
57      *   only bits  7..0 contain the length of indexes[],
58      *        bits 31..8 are reserved and set to 0
59      * formatVersion>=3:
60      *        bits 31..8 poolStringIndexLimit bits 23..0
61      */
62     private static final int URES_INDEX_LENGTH           = 0;
63     /**
64      * [1] contains the top of the key strings,
65      *     same as the bottom of resources or UTF-16 strings, rounded up
66      */
67     private static final int URES_INDEX_KEYS_TOP         = 1;
68     /** [2] contains the top of all resources */
69     //ivate static final int URES_INDEX_RESOURCES_TOP    = 2;
70     /**
71      * [3] contains the top of the bundle,
72      *     in case it were ever different from [2]
73      */
74     private static final int URES_INDEX_BUNDLE_TOP       = 3;
75     /** [4] max. length of any table */
76     private static final int URES_INDEX_MAX_TABLE_LENGTH = 4;
77     /**
78      * [5] attributes bit set, see URES_ATT_* (new in formatVersion 1.2)
79      *
80      * formatVersion>=3:
81      *   bits 31..16 poolStringIndex16Limit
82      *   bits 15..12 poolStringIndexLimit bits 27..24
83      */
84     private static final int URES_INDEX_ATTRIBUTES       = 5;
85     /**
86      * [6] top of the 16-bit units (UTF-16 string v2 UChars, URES_TABLE16, URES_ARRAY16),
87      *     rounded up (new in formatVersion 2.0, ICU 4.4)
88      */
89     private static final int URES_INDEX_16BIT_TOP        = 6;
90     /** [7] checksum of the pool bundle (new in formatVersion 2.0, ICU 4.4) */
91     private static final int URES_INDEX_POOL_CHECKSUM    = 7;
92     //ivate static final int URES_INDEX_TOP              = 8;
93 
94     /*
95      * Nofallback attribute, attribute bit 0 in indexes[URES_INDEX_ATTRIBUTES].
96      * New in formatVersion 1.2 (ICU 3.6).
97      *
98      * If set, then this resource bundle is a standalone bundle.
99      * If not set, then the bundle participates in locale fallback, eventually
100      * all the way to the root bundle.
101      * If indexes[] is missing or too short, then the attribute cannot be determined
102      * reliably. Dependency checking should ignore such bundles, and loading should
103      * use fallbacks.
104      */
105     private static final int URES_ATT_NO_FALLBACK = 1;
106 
107     /*
108      * Attributes for bundles that are, or use, a pool bundle.
109      * A pool bundle provides key strings that are shared among several other bundles
110      * to reduce their total size.
111      * New in formatVersion 2 (ICU 4.4).
112      */
113     private static final int URES_ATT_IS_POOL_BUNDLE = 2;
114     private static final int URES_ATT_USES_POOL_BUNDLE = 4;
115 
116     private static final CharBuffer EMPTY_16_BIT_UNITS = CharBuffer.wrap("\0");  // read-only
117 
118     /**
119      * Objects with more value bytes are stored in SoftReferences.
120      * Smaller objects (which are not much larger than a SoftReference)
121      * are stored directly, avoiding the overhead of the reference.
122      */
123     static final int LARGE_SIZE = 24;
124 
125     private static final boolean DEBUG = false;
126 
127     private int /* formatVersion, */ dataVersion;
128 
129     // See the ResourceData struct in ICU4C/source/common/uresdata.h.
130     /**
131      * Buffer of all of the resource bundle bytes after the header.
132      * (equivalent of C++ pRoot)
133      */
134     private ByteBuffer bytes;
135     private byte[] keyBytes;
136     private CharBuffer b16BitUnits;
137     private ICUResourceBundleReader poolBundleReader;
138     private int rootRes;
139     private int localKeyLimit;
140     private int poolStringIndexLimit;
141     private int poolStringIndex16Limit;
142     private boolean noFallback; /* see URES_ATT_NO_FALLBACK */
143     private boolean isPoolBundle;
144     private boolean usesPoolBundle;
145     private int poolCheckSum;
146 
147     private ResourceCache resourceCache;
148 
149     private static ReaderCache CACHE = new ReaderCache();
150     private static final ICUResourceBundleReader NULL_READER = new ICUResourceBundleReader();
151 
152     private static class ReaderCacheKey {
153         final String baseName;
154         final String localeID;
155 
ReaderCacheKey(String baseName, String localeID)156         ReaderCacheKey(String baseName, String localeID) {
157             this.baseName = (baseName == null) ? "" : baseName;
158             this.localeID = (localeID == null) ? "" : localeID;
159         }
160 
161         @Override
equals(Object obj)162         public boolean equals(Object obj) {
163             if (this == obj) {
164                 return true;
165             }
166             if (!(obj instanceof ReaderCacheKey)) {
167                 return false;
168             }
169             ReaderCacheKey info = (ReaderCacheKey)obj;
170             return this.baseName.equals(info.baseName)
171                     && this.localeID.equals(info.localeID);
172         }
173 
174         @Override
hashCode()175         public int hashCode() {
176             return baseName.hashCode() ^ localeID.hashCode();
177         }
178     }
179 
180     private static class ReaderCache extends SoftCache<ReaderCacheKey, ICUResourceBundleReader, ClassLoader> {
181         /* (non-Javadoc)
182          * @see ohos.global.icu.impl.CacheBase#createInstance(java.lang.Object, java.lang.Object)
183          */
184         @Override
createInstance(ReaderCacheKey key, ClassLoader loader)185         protected ICUResourceBundleReader createInstance(ReaderCacheKey key, ClassLoader loader) {
186             String fullName = ICUResourceBundleReader.getFullName(key.baseName, key.localeID);
187             try {
188                 ByteBuffer inBytes;
189                 if (key.baseName != null && key.baseName.startsWith(ICUData.ICU_BASE_NAME)) {
190                     String itemPath = fullName.substring(ICUData.ICU_BASE_NAME.length() + 1);
191                     inBytes = ICUBinary.getData(loader, fullName, itemPath);
192                     if (inBytes == null) {
193                         return NULL_READER;
194                     }
195                 } else {
196                     @SuppressWarnings("resource")  // Closed by getByteBufferFromInputStreamAndCloseStream().
197                     InputStream stream = ICUData.getStream(loader, fullName);
198                     if (stream == null) {
199                         return NULL_READER;
200                     }
201                     inBytes = ICUBinary.getByteBufferFromInputStreamAndCloseStream(stream);
202                 }
203                 return new ICUResourceBundleReader(inBytes, key.baseName, key.localeID, loader);
204             } catch (IOException ex) {
205                 throw new ICUUncheckedIOException("Data file " + fullName + " is corrupt - " + ex.getMessage(), ex);
206             }
207         }
208     }
209 
210     /*
211      * Default constructor, just used for NULL_READER.
212      */
ICUResourceBundleReader()213     private ICUResourceBundleReader() {
214     }
215 
ICUResourceBundleReader(ByteBuffer inBytes, String baseName, String localeID, ClassLoader loader)216     private ICUResourceBundleReader(ByteBuffer inBytes,
217             String baseName, String localeID,
218             ClassLoader loader) throws IOException {
219         init(inBytes);
220 
221         // set pool bundle if necessary
222         if (usesPoolBundle) {
223             poolBundleReader = getReader(baseName, "pool", loader);
224             if (poolBundleReader == null || !poolBundleReader.isPoolBundle) {
225                 throw new IllegalStateException("pool.res is not a pool bundle");
226             }
227             if (poolBundleReader.poolCheckSum != poolCheckSum) {
228                 throw new IllegalStateException("pool.res has a different checksum than this bundle");
229             }
230         }
231     }
232 
getReader(String baseName, String localeID, ClassLoader root)233     static ICUResourceBundleReader getReader(String baseName, String localeID, ClassLoader root) {
234         ReaderCacheKey info = new ReaderCacheKey(baseName, localeID);
235         ICUResourceBundleReader reader = CACHE.getInstance(info, root);
236         if (reader == NULL_READER) {
237             return null;
238         }
239         return reader;
240     }
241 
242     // See res_init() in ICU4C/source/common/uresdata.c.
init(ByteBuffer inBytes)243     private void init(ByteBuffer inBytes) throws IOException {
244         dataVersion = ICUBinary.readHeader(inBytes, DATA_FORMAT, IS_ACCEPTABLE);
245         int majorFormatVersion = inBytes.get(16);
246         bytes = ICUBinary.sliceWithOrder(inBytes);
247         int dataLength = bytes.remaining();
248 
249         if(DEBUG) System.out.println("The ByteBuffer is direct (memory-mapped): " + bytes.isDirect());
250         if(DEBUG) System.out.println("The available bytes in the buffer before reading the data: " + dataLength);
251 
252         rootRes = bytes.getInt(0);
253 
254         // Bundles with formatVersion 1.1 and later contain an indexes[] array.
255         // We need it so that we can read the key string bytes up front, for lookup performance.
256 
257         // read the variable-length indexes[] array
258         int indexes0 = getIndexesInt(URES_INDEX_LENGTH);
259         int indexLength = indexes0 & 0xff;
260         if(indexLength <= URES_INDEX_MAX_TABLE_LENGTH) {
261             throw new ICUException("not enough indexes");
262         }
263         int bundleTop;
264         if(dataLength < ((1 + indexLength) << 2) ||
265                 dataLength < ((bundleTop = getIndexesInt(URES_INDEX_BUNDLE_TOP)) << 2)) {
266             throw new ICUException("not enough bytes");
267         }
268         int maxOffset = bundleTop - 1;
269 
270         if (majorFormatVersion >= 3) {
271             // In formatVersion 1, the indexLength took up this whole int.
272             // In version 2, bits 31..8 were reserved and always 0.
273             // In version 3, they contain bits 23..0 of the poolStringIndexLimit.
274             // Bits 27..24 are in indexes[URES_INDEX_ATTRIBUTES] bits 15..12.
275             poolStringIndexLimit = indexes0 >>> 8;
276         }
277         if(indexLength > URES_INDEX_ATTRIBUTES) {
278             // determine if this resource bundle falls back to a parent bundle
279             // along normal locale ID fallback
280             int att = getIndexesInt(URES_INDEX_ATTRIBUTES);
281             noFallback = (att & URES_ATT_NO_FALLBACK) != 0;
282             isPoolBundle = (att & URES_ATT_IS_POOL_BUNDLE) != 0;
283             usesPoolBundle = (att & URES_ATT_USES_POOL_BUNDLE) != 0;
284             poolStringIndexLimit |= (att & 0xf000) << 12;  // bits 15..12 -> 27..24
285             poolStringIndex16Limit = att >>> 16;
286         }
287 
288         int keysBottom = 1 + indexLength;
289         int keysTop = getIndexesInt(URES_INDEX_KEYS_TOP);
290         if(keysTop > keysBottom) {
291             // Deserialize the key strings up front.
292             // Faster table item search at the cost of slower startup and some heap memory.
293             if(isPoolBundle) {
294                 // Shift the key strings down:
295                 // Pool bundle key strings are used with a 0-based index,
296                 // unlike regular bundles' key strings for which indexes
297                 // are based on the start of the bundle data.
298                 keyBytes = new byte[(keysTop - keysBottom) << 2];
299                 bytes.position(keysBottom << 2);
300             } else {
301                 localKeyLimit = keysTop << 2;
302                 keyBytes = new byte[localKeyLimit];
303             }
304             bytes.get(keyBytes);
305         }
306 
307         // Read the array of 16-bit units.
308         if(indexLength > URES_INDEX_16BIT_TOP) {
309             int _16BitTop = getIndexesInt(URES_INDEX_16BIT_TOP);
310             if(_16BitTop > keysTop) {
311                 int num16BitUnits = (_16BitTop - keysTop) * 2;
312                 bytes.position(keysTop << 2);
313                 b16BitUnits = bytes.asCharBuffer();
314                 b16BitUnits.limit(num16BitUnits);
315                 maxOffset |= num16BitUnits - 1;
316             } else {
317                 b16BitUnits = EMPTY_16_BIT_UNITS;
318             }
319         } else {
320             b16BitUnits = EMPTY_16_BIT_UNITS;
321         }
322 
323         if(indexLength > URES_INDEX_POOL_CHECKSUM) {
324             poolCheckSum = getIndexesInt(URES_INDEX_POOL_CHECKSUM);
325         }
326 
327         if(!isPoolBundle || b16BitUnits.length() > 1) {
328             resourceCache = new ResourceCache(maxOffset);
329         }
330 
331         // Reset the position for future .asCharBuffer() etc.
332         bytes.position(0);
333     }
334 
getIndexesInt(int i)335     private int getIndexesInt(int i) {
336         return bytes.getInt((1 + i) << 2);
337     }
338 
getVersion()339     VersionInfo getVersion() {
340         return ICUBinary.getVersionInfoFromCompactInt(dataVersion);
341     }
342 
getRootResource()343     int getRootResource() {
344         return rootRes;
345     }
getNoFallback()346     boolean getNoFallback() {
347         return noFallback;
348     }
getUsesPoolBundle()349     boolean getUsesPoolBundle() {
350         return usesPoolBundle;
351     }
352 
RES_GET_TYPE(int res)353     static int RES_GET_TYPE(int res) {
354         return res >>> 28;
355     }
RES_GET_OFFSET(int res)356     private static int RES_GET_OFFSET(int res) {
357         return res & 0x0fffffff;
358     }
getResourceByteOffset(int offset)359     private int getResourceByteOffset(int offset) {
360         return offset << 2;
361     }
362     /* get signed and unsigned integer values directly from the Resource handle */
RES_GET_INT(int res)363     static int RES_GET_INT(int res) {
364         return (res << 4) >> 4;
365     }
RES_GET_UINT(int res)366     static int RES_GET_UINT(int res) {
367         return res & 0x0fffffff;
368     }
URES_IS_ARRAY(int type)369     static boolean URES_IS_ARRAY(int type) {
370         return type == UResourceBundle.ARRAY || type == ICUResourceBundle.ARRAY16;
371     }
URES_IS_TABLE(int type)372     static boolean URES_IS_TABLE(int type) {
373         return type==UResourceBundle.TABLE || type==ICUResourceBundle.TABLE16 || type==ICUResourceBundle.TABLE32;
374     }
375 
376     private static final byte[] emptyBytes = new byte[0];
377     private static final ByteBuffer emptyByteBuffer = ByteBuffer.allocate(0).asReadOnlyBuffer();
378     private static final char[] emptyChars = new char[0];
379     private static final int[] emptyInts = new int[0];
380     private static final String emptyString = "";
381     private static final Array EMPTY_ARRAY = new Array();
382     private static final Table EMPTY_TABLE = new Table();
383 
getChars(int offset, int count)384     private char[] getChars(int offset, int count) {
385         char[] chars = new char[count];
386         if (count <= 16) {
387             for(int i = 0; i < count; offset += 2, ++i) {
388                 chars[i] = bytes.getChar(offset);
389             }
390         } else {
391             CharBuffer temp = bytes.asCharBuffer();
392             temp.position(offset / 2);
393             temp.get(chars);
394         }
395         return chars;
396     }
getInt(int offset)397     private int getInt(int offset) {
398         return bytes.getInt(offset);
399     }
getInts(int offset, int count)400     private int[] getInts(int offset, int count) {
401         int[] ints = new int[count];
402         if (count <= 16) {
403             for(int i = 0; i < count; offset += 4, ++i) {
404                 ints[i] = bytes.getInt(offset);
405             }
406         } else {
407             IntBuffer temp = bytes.asIntBuffer();
408             temp.position(offset / 4);
409             temp.get(ints);
410         }
411         return ints;
412     }
getTable16KeyOffsets(int offset)413     private char[] getTable16KeyOffsets(int offset) {
414         int length = b16BitUnits.charAt(offset++);
415         if(length > 0) {
416             char[] result = new char[length];
417             if(length <= 16) {
418                 for(int i = 0; i < length; ++i) {
419                     result[i] = b16BitUnits.charAt(offset++);
420                 }
421             } else {
422                 CharBuffer temp = b16BitUnits.duplicate();
423                 temp.position(offset);
424                 temp.get(result);
425             }
426             return result;
427         } else {
428             return emptyChars;
429         }
430     }
getTableKeyOffsets(int offset)431     private char[] getTableKeyOffsets(int offset) {
432         int length = bytes.getChar(offset);
433         if(length > 0) {
434             return getChars(offset + 2, length);
435         } else {
436             return emptyChars;
437         }
438     }
getTable32KeyOffsets(int offset)439     private int[] getTable32KeyOffsets(int offset) {
440         int length = getInt(offset);
441         if(length > 0) {
442             return getInts(offset + 4, length);
443         } else {
444             return emptyInts;
445         }
446     }
447 
makeKeyStringFromBytes(byte[] keyBytes, int keyOffset)448     private static String makeKeyStringFromBytes(byte[] keyBytes, int keyOffset) {
449         StringBuilder sb = new StringBuilder();
450         byte b;
451         while((b = keyBytes[keyOffset]) != 0) {
452             ++keyOffset;
453             sb.append((char)b);
454         }
455         return sb.toString();
456     }
getKey16String(int keyOffset)457     private String getKey16String(int keyOffset) {
458         if(keyOffset < localKeyLimit) {
459             return makeKeyStringFromBytes(keyBytes, keyOffset);
460         } else {
461             return makeKeyStringFromBytes(poolBundleReader.keyBytes, keyOffset - localKeyLimit);
462         }
463     }
getKey32String(int keyOffset)464     private String getKey32String(int keyOffset) {
465         if(keyOffset >= 0) {
466             return makeKeyStringFromBytes(keyBytes, keyOffset);
467         } else {
468             return makeKeyStringFromBytes(poolBundleReader.keyBytes, keyOffset & 0x7fffffff);
469         }
470     }
setKeyFromKey16(int keyOffset, UResource.Key key)471     private void setKeyFromKey16(int keyOffset, UResource.Key key) {
472         if(keyOffset < localKeyLimit) {
473             key.setBytes(keyBytes, keyOffset);
474         } else {
475             key.setBytes(poolBundleReader.keyBytes, keyOffset - localKeyLimit);
476         }
477     }
setKeyFromKey32(int keyOffset, UResource.Key key)478     private void setKeyFromKey32(int keyOffset, UResource.Key key) {
479         if(keyOffset >= 0) {
480             key.setBytes(keyBytes, keyOffset);
481         } else {
482             key.setBytes(poolBundleReader.keyBytes, keyOffset & 0x7fffffff);
483         }
484     }
compareKeys(CharSequence key, char keyOffset)485     private int compareKeys(CharSequence key, char keyOffset) {
486         if(keyOffset < localKeyLimit) {
487             return ICUBinary.compareKeys(key, keyBytes, keyOffset);
488         } else {
489             return ICUBinary.compareKeys(key, poolBundleReader.keyBytes, keyOffset - localKeyLimit);
490         }
491     }
compareKeys32(CharSequence key, int keyOffset)492     private int compareKeys32(CharSequence key, int keyOffset) {
493         if(keyOffset >= 0) {
494             return ICUBinary.compareKeys(key, keyBytes, keyOffset);
495         } else {
496             return ICUBinary.compareKeys(key, poolBundleReader.keyBytes, keyOffset & 0x7fffffff);
497         }
498     }
499 
500     /**
501      * @return a string from the local bundle's b16BitUnits at the local offset
502      */
getStringV2(int res)503     String getStringV2(int res) {
504         // Use the pool bundle's resource cache for pool bundle strings;
505         // use the local bundle's cache for local strings.
506         // The cache requires a resource word with the proper type,
507         // and with an offset that is local to this bundle so that the offset fits
508         // within the maximum number of bits for which the cache was constructed.
509         assert RES_GET_TYPE(res) == ICUResourceBundle.STRING_V2;
510         int offset = RES_GET_OFFSET(res);
511         assert offset != 0;  // handled by the caller
512         Object value = resourceCache.get(res);
513         if(value != null) {
514             return (String)value;
515         }
516         String s;
517         int first = b16BitUnits.charAt(offset);
518         if((first&0xfffffc00)!=0xdc00) {  // C: if(!U16_IS_TRAIL(first)) {
519             if(first==0) {
520                 return emptyString;  // Should not occur, but is not forbidden.
521             }
522             StringBuilder sb = new StringBuilder();
523             sb.append((char)first);
524             char c;
525             while((c = b16BitUnits.charAt(++offset)) != 0) {
526                 sb.append(c);
527             }
528             s = sb.toString();
529         } else {
530             int length;
531             if(first<0xdfef) {
532                 length=first&0x3ff;
533                 ++offset;
534             } else if(first<0xdfff) {
535                 length=((first-0xdfef)<<16)|b16BitUnits.charAt(offset+1);
536                 offset+=2;
537             } else {
538                 length=(b16BitUnits.charAt(offset+1)<<16)|b16BitUnits.charAt(offset+2);
539                 offset+=3;
540             }
541             // Cast up to CharSequence to insulate against the CharBuffer.subSequence() return type change
542             // which makes code compiled for a newer JDK (7 and up) not run on an older one (6 and below).
543             s = ((CharSequence) b16BitUnits).subSequence(offset, offset + length).toString();
544         }
545         return (String)resourceCache.putIfAbsent(res, s, s.length() * 2);
546     }
547 
makeStringFromBytes(int offset, int length)548     private String makeStringFromBytes(int offset, int length) {
549         if (length <= 16) {
550             StringBuilder sb = new StringBuilder(length);
551             for (int i = 0; i < length; offset += 2, ++i) {
552                 sb.append(bytes.getChar(offset));
553             }
554             return sb.toString();
555         } else {
556             CharSequence cs = bytes.asCharBuffer();
557             offset /= 2;
558             return cs.subSequence(offset, offset + length).toString();
559         }
560     }
561 
getString(int res)562     String getString(int res) {
563         int offset=RES_GET_OFFSET(res);
564         if(res != offset /* RES_GET_TYPE(res) != URES_STRING */ &&
565                 RES_GET_TYPE(res) != ICUResourceBundle.STRING_V2) {
566             return null;
567         }
568         if(offset == 0) {
569             return emptyString;
570         }
571         if (res != offset) {  // STRING_V2
572             if (offset < poolStringIndexLimit) {
573                 return poolBundleReader.getStringV2(res);
574             } else {
575                 return getStringV2(res - poolStringIndexLimit);
576             }
577         }
578         Object value = resourceCache.get(res);
579         if(value != null) {
580             return (String)value;
581         }
582         offset=getResourceByteOffset(offset);
583         int length = getInt(offset);
584         String s = makeStringFromBytes(offset+4, length);
585         return (String)resourceCache.putIfAbsent(res, s, s.length() * 2);
586     }
587 
588     /**
589      * CLDR string value "∅∅∅"=="\u2205\u2205\u2205" prevents fallback to the parent bundle.
590      */
isNoInheritanceMarker(int res)591     private boolean isNoInheritanceMarker(int res) {
592         int offset = RES_GET_OFFSET(res);
593         if (offset == 0) {
594             // empty string
595         } else if (res == offset) {
596             offset = getResourceByteOffset(offset);
597             return getInt(offset) == 3 && bytes.getChar(offset + 4) == 0x2205 &&
598                     bytes.getChar(offset + 6) == 0x2205 && bytes.getChar(offset + 8) == 0x2205;
599         } else if (RES_GET_TYPE(res) == ICUResourceBundle.STRING_V2) {
600             if (offset < poolStringIndexLimit) {
601                 return poolBundleReader.isStringV2NoInheritanceMarker(offset);
602             } else {
603                 return isStringV2NoInheritanceMarker(offset - poolStringIndexLimit);
604             }
605         }
606         return false;
607     }
608 
isStringV2NoInheritanceMarker(int offset)609     private boolean isStringV2NoInheritanceMarker(int offset) {
610         int first = b16BitUnits.charAt(offset);
611         if (first == 0x2205) {  // implicit length
612             return b16BitUnits.charAt(offset + 1) == 0x2205 &&
613                     b16BitUnits.charAt(offset + 2) == 0x2205 &&
614                     b16BitUnits.charAt(offset + 3) == 0;
615         } else if (first == 0xdc03) {  // explicit length 3 (should not occur)
616             return b16BitUnits.charAt(offset + 1) == 0x2205 &&
617                     b16BitUnits.charAt(offset + 2) == 0x2205 &&
618                     b16BitUnits.charAt(offset + 3) == 0x2205;
619         } else {
620             // Assume that the string has not been stored with more length units than necessary.
621             return false;
622         }
623     }
624 
getAlias(int res)625     String getAlias(int res) {
626         int offset=RES_GET_OFFSET(res);
627         int length;
628         if(RES_GET_TYPE(res)==ICUResourceBundle.ALIAS) {
629             if(offset==0) {
630                 return emptyString;
631             } else {
632                 Object value = resourceCache.get(res);
633                 if(value != null) {
634                     return (String)value;
635                 }
636                 offset=getResourceByteOffset(offset);
637                 length=getInt(offset);
638                 String s = makeStringFromBytes(offset + 4, length);
639                 return (String)resourceCache.putIfAbsent(res, s, length * 2);
640             }
641         } else {
642             return null;
643         }
644     }
645 
getBinary(int res, byte[] ba)646     byte[] getBinary(int res, byte[] ba) {
647         int offset=RES_GET_OFFSET(res);
648         int length;
649         if(RES_GET_TYPE(res)==UResourceBundle.BINARY) {
650             if(offset==0) {
651                 return emptyBytes;
652             } else {
653                 offset=getResourceByteOffset(offset);
654                 length=getInt(offset);
655                 if(length==0) {
656                     return emptyBytes;
657                 }
658                 // Not cached: The array would have to be cloned anyway because
659                 // the cache must not be writable via the returned reference.
660                 if(ba==null || ba.length!=length) {
661                     ba=new byte[length];
662                 }
663                 offset += 4;
664                 if(length <= 16) {
665                     for(int i = 0; i < length; ++i) {
666                         ba[i] = bytes.get(offset++);
667                     }
668                 } else {
669                     ByteBuffer temp = bytes.duplicate();
670                     temp.position(offset);
671                     temp.get(ba);
672                 }
673                 return ba;
674             }
675         } else {
676             return null;
677         }
678     }
679 
getBinary(int res)680     ByteBuffer getBinary(int res) {
681         int offset=RES_GET_OFFSET(res);
682         int length;
683         if(RES_GET_TYPE(res)==UResourceBundle.BINARY) {
684             if(offset==0) {
685                 // Don't just
686                 //   return emptyByteBuffer;
687                 // in case it matters whether the buffer's mark is defined or undefined.
688                 return emptyByteBuffer.duplicate();
689             } else {
690                 // Not cached: The returned buffer is small (shares its bytes with the bundle)
691                 // and usually quickly discarded after use.
692                 // Also, even a cached buffer would have to be cloned because it is mutable
693                 // (position & mark).
694                 offset=getResourceByteOffset(offset);
695                 length=getInt(offset);
696                 if(length == 0) {
697                     return emptyByteBuffer.duplicate();
698                 }
699                 offset += 4;
700                 ByteBuffer result = bytes.duplicate();
701                 result.position(offset).limit(offset + length);
702                 result = ICUBinary.sliceWithOrder(result);
703                 if(!result.isReadOnly()) {
704                     result = result.asReadOnlyBuffer();
705                 }
706                 return result;
707             }
708         } else {
709             return null;
710         }
711     }
712 
getIntVector(int res)713     int[] getIntVector(int res) {
714         int offset=RES_GET_OFFSET(res);
715         int length;
716         if(RES_GET_TYPE(res)==UResourceBundle.INT_VECTOR) {
717             if(offset==0) {
718                 return emptyInts;
719             } else {
720                 // Not cached: The array would have to be cloned anyway because
721                 // the cache must not be writable via the returned reference.
722                 offset=getResourceByteOffset(offset);
723                 length=getInt(offset);
724                 return getInts(offset+4, length);
725             }
726         } else {
727             return null;
728         }
729     }
730 
getArray(int res)731     Array getArray(int res) {
732         int type=RES_GET_TYPE(res);
733         if(!URES_IS_ARRAY(type)) {
734             return null;
735         }
736         int offset=RES_GET_OFFSET(res);
737         if(offset == 0) {
738             return EMPTY_ARRAY;
739         }
740         Object value = resourceCache.get(res);
741         if(value != null) {
742             return (Array)value;
743         }
744         Array array = (type == UResourceBundle.ARRAY) ?
745                 new Array32(this, offset) : new Array16(this, offset);
746         return (Array)resourceCache.putIfAbsent(res, array, 0);
747     }
748 
getTable(int res)749     Table getTable(int res) {
750         int type = RES_GET_TYPE(res);
751         if(!URES_IS_TABLE(type)) {
752             return null;
753         }
754         int offset = RES_GET_OFFSET(res);
755         if(offset == 0) {
756             return EMPTY_TABLE;
757         }
758         Object value = resourceCache.get(res);
759         if(value != null) {
760             return (Table)value;
761         }
762         Table table;
763         int size;  // Use size = 0 to never use SoftReferences for Tables?
764         if(type == UResourceBundle.TABLE) {
765             table = new Table1632(this, offset);
766             size = table.getSize() * 2;
767         } else if(type == ICUResourceBundle.TABLE16) {
768             table = new Table16(this, offset);
769             size = table.getSize() * 2;
770         } else /* type == ICUResourceBundle.TABLE32 */ {
771             table = new Table32(this, offset);
772             size = table.getSize() * 4;
773         }
774         return (Table)resourceCache.putIfAbsent(res, table, size);
775     }
776 
777     // ICUResource.Value --------------------------------------------------- ***
778 
779     /**
780      * From C++ uresdata.c gPublicTypes[URES_LIMIT].
781      */
782     private static int PUBLIC_TYPES[] = {
783         UResourceBundle.STRING,
784         UResourceBundle.BINARY,
785         UResourceBundle.TABLE,
786         ICUResourceBundle.ALIAS,
787 
788         UResourceBundle.TABLE,     /* URES_TABLE32 */
789         UResourceBundle.TABLE,     /* URES_TABLE16 */
790         UResourceBundle.STRING,    /* URES_STRING_V2 */
791         UResourceBundle.INT,
792 
793         UResourceBundle.ARRAY,
794         UResourceBundle.ARRAY,     /* URES_ARRAY16 */
795         UResourceBundle.NONE,
796         UResourceBundle.NONE,
797 
798         UResourceBundle.NONE,
799         UResourceBundle.NONE,
800         UResourceBundle.INT_VECTOR,
801         UResourceBundle.NONE
802     };
803 
804     static class ReaderValue extends UResource.Value {
805         ICUResourceBundleReader reader;
806         int res;
807 
808         @Override
getType()809         public int getType() {
810             return PUBLIC_TYPES[RES_GET_TYPE(res)];
811         }
812 
813         @Override
getString()814         public String getString() {
815             String s = reader.getString(res);
816             if (s == null) {
817                 throw new UResourceTypeMismatchException("");
818             }
819             return s;
820         }
821 
822         @Override
getAliasString()823         public String getAliasString() {
824             String s = reader.getAlias(res);
825             if (s == null) {
826                 throw new UResourceTypeMismatchException("");
827             }
828             return s;
829         }
830 
831         @Override
getInt()832         public int getInt() {
833             if (RES_GET_TYPE(res) != UResourceBundle.INT) {
834                 throw new UResourceTypeMismatchException("");
835             }
836             return RES_GET_INT(res);
837         }
838 
839         @Override
getUInt()840         public int getUInt() {
841             if (RES_GET_TYPE(res) != UResourceBundle.INT) {
842                 throw new UResourceTypeMismatchException("");
843             }
844             return RES_GET_UINT(res);
845         }
846 
847         @Override
getIntVector()848         public int[] getIntVector() {
849             int[] iv = reader.getIntVector(res);
850             if (iv == null) {
851                 throw new UResourceTypeMismatchException("");
852             }
853             return iv;
854         }
855 
856         @Override
getBinary()857         public ByteBuffer getBinary() {
858             ByteBuffer bb = reader.getBinary(res);
859             if (bb == null) {
860                 throw new UResourceTypeMismatchException("");
861             }
862             return bb;
863         }
864 
865         @Override
getArray()866         public ohos.global.icu.impl.UResource.Array getArray() {
867             Array array = reader.getArray(res);
868             if (array == null) {
869                 throw new UResourceTypeMismatchException("");
870             }
871             return array;
872         }
873 
874         @Override
getTable()875         public ohos.global.icu.impl.UResource.Table getTable() {
876             Table table = reader.getTable(res);
877             if (table == null) {
878                 throw new UResourceTypeMismatchException("");
879             }
880             return table;
881         }
882 
883         @Override
isNoInheritanceMarker()884         public boolean isNoInheritanceMarker() {
885             return reader.isNoInheritanceMarker(res);
886         }
887 
888         @Override
getStringArray()889         public String[] getStringArray() {
890             Array array = reader.getArray(res);
891             if (array == null) {
892                 throw new UResourceTypeMismatchException("");
893             }
894             return getStringArray(array);
895         }
896 
897         @Override
getStringArrayOrStringAsArray()898         public String[] getStringArrayOrStringAsArray() {
899             Array array = reader.getArray(res);
900             if (array != null) {
901                 return getStringArray(array);
902             }
903             String s = reader.getString(res);
904             if (s != null) {
905                 return new String[] { s };
906             }
907             throw new UResourceTypeMismatchException("");
908         }
909 
910         @Override
getStringOrFirstOfArray()911         public String getStringOrFirstOfArray() {
912             String s = reader.getString(res);
913             if (s != null) {
914                 return s;
915             }
916             Array array = reader.getArray(res);
917             if (array != null && array.size > 0) {
918                 int r = array.getContainerResource(reader, 0);
919                 s = reader.getString(r);
920                 if (s != null) {
921                     return s;
922                 }
923             }
924             throw new UResourceTypeMismatchException("");
925         }
926 
getStringArray(Array array)927         private String[] getStringArray(Array array) {
928             String[] result = new String[array.size];
929             for (int i = 0; i < array.size; ++i) {
930                 int r = array.getContainerResource(reader, i);
931                 String s = reader.getString(r);
932                 if (s == null) {
933                     throw new UResourceTypeMismatchException("");
934                 }
935                 result[i] = s;
936             }
937             return result;
938         }
939     }
940 
941     // Container value classes --------------------------------------------- ***
942 
943     static class Container {
944         protected int size;
945         protected int itemsOffset;
946 
getSize()947         public final int getSize() {
948             return size;
949         }
getContainerResource(ICUResourceBundleReader reader, int index)950         int getContainerResource(ICUResourceBundleReader reader, int index) {
951             return ICUResourceBundle.RES_BOGUS;
952         }
getContainer16Resource(ICUResourceBundleReader reader, int index)953         protected int getContainer16Resource(ICUResourceBundleReader reader, int index) {
954             if (index < 0 || size <= index) {
955                 return ICUResourceBundle.RES_BOGUS;
956             }
957             int res16 = reader.b16BitUnits.charAt(itemsOffset + index);
958             if (res16 < reader.poolStringIndex16Limit) {
959                 // Pool string, nothing to do.
960             } else {
961                 // Local string, adjust the 16-bit offset to a regular one,
962                 // with a larger pool string index limit.
963                 res16 = res16 - reader.poolStringIndex16Limit + reader.poolStringIndexLimit;
964             }
965             return (ICUResourceBundle.STRING_V2 << 28) | res16;
966         }
getContainer32Resource(ICUResourceBundleReader reader, int index)967         protected int getContainer32Resource(ICUResourceBundleReader reader, int index) {
968             if (index < 0 || size <= index) {
969                 return ICUResourceBundle.RES_BOGUS;
970             }
971             return reader.getInt(itemsOffset + 4 * index);
972         }
getResource(ICUResourceBundleReader reader, String resKey)973         int getResource(ICUResourceBundleReader reader, String resKey) {
974             return getContainerResource(reader, Integer.parseInt(resKey));
975         }
Container()976         Container() {
977         }
978     }
979     static class Array extends Container implements UResource.Array {
Array()980         Array() {}
981         @Override
getValue(int i, UResource.Value value)982         public boolean getValue(int i, UResource.Value value) {
983             if (0 <= i && i < size) {
984                 ReaderValue readerValue = (ReaderValue)value;
985                 readerValue.res = getContainerResource(readerValue.reader, i);
986                 return true;
987             }
988             return false;
989         }
990     }
991     private static final class Array32 extends Array {
992         @Override
getContainerResource(ICUResourceBundleReader reader, int index)993         int getContainerResource(ICUResourceBundleReader reader, int index) {
994             return getContainer32Resource(reader, index);
995         }
Array32(ICUResourceBundleReader reader, int offset)996         Array32(ICUResourceBundleReader reader, int offset) {
997             offset = reader.getResourceByteOffset(offset);
998             size = reader.getInt(offset);
999             itemsOffset = offset + 4;
1000         }
1001     }
1002     private static final class Array16 extends Array {
1003         @Override
getContainerResource(ICUResourceBundleReader reader, int index)1004         int getContainerResource(ICUResourceBundleReader reader, int index) {
1005             return getContainer16Resource(reader, index);
1006         }
Array16(ICUResourceBundleReader reader, int offset)1007         Array16(ICUResourceBundleReader reader, int offset) {
1008             size = reader.b16BitUnits.charAt(offset);
1009             itemsOffset = offset + 1;
1010         }
1011     }
1012     static class Table extends Container implements UResource.Table {
1013         protected char[] keyOffsets;
1014         protected int[] key32Offsets;
1015 
Table()1016         Table() {
1017         }
getKey(ICUResourceBundleReader reader, int index)1018         String getKey(ICUResourceBundleReader reader, int index) {
1019             if (index < 0 || size <= index) {
1020                 return null;
1021             }
1022             return keyOffsets != null ?
1023                         reader.getKey16String(keyOffsets[index]) :
1024                         reader.getKey32String(key32Offsets[index]);
1025         }
1026         private static final int URESDATA_ITEM_NOT_FOUND = -1;
findTableItem(ICUResourceBundleReader reader, CharSequence key)1027         int findTableItem(ICUResourceBundleReader reader, CharSequence key) {
1028             int mid, start, limit;
1029             int result;
1030 
1031             /* do a binary search for the key */
1032             start=0;
1033             limit=size;
1034             while(start<limit) {
1035                 mid = (start + limit) >>> 1;
1036                 if (keyOffsets != null) {
1037                     result = reader.compareKeys(key, keyOffsets[mid]);
1038                 } else {
1039                     result = reader.compareKeys32(key, key32Offsets[mid]);
1040                 }
1041                 if (result < 0) {
1042                     limit = mid;
1043                 } else if (result > 0) {
1044                     start = mid + 1;
1045                 } else {
1046                     /* We found it! */
1047                     return mid;
1048                 }
1049             }
1050             return URESDATA_ITEM_NOT_FOUND;  /* not found or table is empty. */
1051         }
1052         @Override
getResource(ICUResourceBundleReader reader, String resKey)1053         int getResource(ICUResourceBundleReader reader, String resKey) {
1054             return getContainerResource(reader, findTableItem(reader, resKey));
1055         }
1056         @Override
getKeyAndValue(int i, UResource.Key key, UResource.Value value)1057         public boolean getKeyAndValue(int i, UResource.Key key, UResource.Value value) {
1058             if (0 <= i && i < size) {
1059                 ReaderValue readerValue = (ReaderValue)value;
1060                 if (keyOffsets != null) {
1061                     readerValue.reader.setKeyFromKey16(keyOffsets[i], key);
1062                 } else {
1063                     readerValue.reader.setKeyFromKey32(key32Offsets[i], key);
1064                 }
1065                 readerValue.res = getContainerResource(readerValue.reader, i);
1066                 return true;
1067             }
1068             return false;
1069         }
1070         @Override
findValue(CharSequence key, UResource.Value value)1071         public boolean findValue(CharSequence key, UResource.Value value) {
1072             ReaderValue readerValue = (ReaderValue)value;
1073             int i = findTableItem(readerValue.reader, key);
1074             if (i >= 0) {
1075                 readerValue.res = getContainerResource(readerValue.reader, i);
1076                 return true;
1077             } else {
1078                 return false;
1079             }
1080         }
1081     }
1082     private static final class Table1632 extends Table {
1083         @Override
getContainerResource(ICUResourceBundleReader reader, int index)1084         int getContainerResource(ICUResourceBundleReader reader, int index) {
1085             return getContainer32Resource(reader, index);
1086         }
Table1632(ICUResourceBundleReader reader, int offset)1087         Table1632(ICUResourceBundleReader reader, int offset) {
1088             offset = reader.getResourceByteOffset(offset);
1089             keyOffsets = reader.getTableKeyOffsets(offset);
1090             size = keyOffsets.length;
1091             itemsOffset = offset + 2 * ((size + 2) & ~1);  // Skip padding for 4-alignment.
1092         }
1093     }
1094     private static final class Table16 extends Table {
1095         @Override
getContainerResource(ICUResourceBundleReader reader, int index)1096         int getContainerResource(ICUResourceBundleReader reader, int index) {
1097             return getContainer16Resource(reader, index);
1098         }
Table16(ICUResourceBundleReader reader, int offset)1099         Table16(ICUResourceBundleReader reader, int offset) {
1100             keyOffsets = reader.getTable16KeyOffsets(offset);
1101             size = keyOffsets.length;
1102             itemsOffset = offset + 1 + size;
1103         }
1104     }
1105     private static final class Table32 extends Table {
1106         @Override
getContainerResource(ICUResourceBundleReader reader, int index)1107         int getContainerResource(ICUResourceBundleReader reader, int index) {
1108             return getContainer32Resource(reader, index);
1109         }
Table32(ICUResourceBundleReader reader, int offset)1110         Table32(ICUResourceBundleReader reader, int offset) {
1111             offset = reader.getResourceByteOffset(offset);
1112             key32Offsets = reader.getTable32KeyOffsets(offset);
1113             size = key32Offsets.length;
1114             itemsOffset = offset + 4 * (1 + size);
1115         }
1116     }
1117 
1118     // Resource cache ------------------------------------------------------ ***
1119 
1120     /**
1121      * Cache of some of one resource bundle's resources.
1122      * Avoids creating multiple Java objects for the same resource items,
1123      * including multiple copies of their contents.
1124      *
1125      * <p>Mutable objects must not be cached and then returned to the caller
1126      * because the cache must not be writable via the returned reference.
1127      *
1128      * <p>Resources are mapped by their resource integers.
1129      * Empty resources with offset 0 cannot be mapped.
1130      * Integers need not and should not be cached.
1131      * Multiple .res items may share resource offsets (genrb eliminates some duplicates).
1132      *
1133      * <p>This cache uses int[] and Object[] arrays to minimize object creation
1134      * and avoid auto-boxing.
1135      *
1136      * <p>Large resource objects are usually stored in SoftReferences.
1137      *
1138      * <p>For few resources, a small table is used with binary search.
1139      * When more resources are cached, then the data structure changes to be faster
1140      * but also use more memory.
1141      */
1142     private static final class ResourceCache {
1143         // Number of items to be stored in a simple array with binary search and insertion sort.
1144         private static final int SIMPLE_LENGTH = 32;
1145 
1146         // When more than SIMPLE_LENGTH items are cached,
1147         // then switch to a trie-like tree of levels with different array lengths.
1148         private static final int ROOT_BITS = 7;
1149         private static final int NEXT_BITS = 6;
1150 
1151         // Simple table, used when length >= 0.
1152         private int[] keys = new int[SIMPLE_LENGTH];
1153         private Object[] values = new Object[SIMPLE_LENGTH];
1154         private int length;
1155 
1156         // Trie-like tree of levels, used when length < 0.
1157         private int maxOffsetBits;
1158         /**
1159          * Number of bits in each level, each stored in a nibble.
1160          */
1161         private int levelBitsList;
1162         private Level rootLevel;
1163 
storeDirectly(int size)1164         private static boolean storeDirectly(int size) {
1165             return size < LARGE_SIZE || CacheValue.futureInstancesWillBeStrong();
1166         }
1167 
1168         @SuppressWarnings("unchecked")
putIfCleared(Object[] values, int index, Object item, int size)1169         private static final Object putIfCleared(Object[] values, int index, Object item, int size) {
1170             Object value = values[index];
1171             if(!(value instanceof SoftReference)) {
1172                 // The caller should be consistent for each resource,
1173                 // that is, create equivalent objects of equal size every time,
1174                 // but the CacheValue "strength" may change over time.
1175                 // assert size < LARGE_SIZE;
1176                 return value;
1177             }
1178             assert size >= LARGE_SIZE;
1179             value = ((SoftReference<Object>)value).get();
1180             if(value != null) {
1181                 return value;
1182             }
1183             values[index] = CacheValue.futureInstancesWillBeStrong() ?
1184                     item : new SoftReference<>(item);
1185             return item;
1186         }
1187 
1188         private static final class Level {
1189             int levelBitsList;
1190             int shift;
1191             int mask;
1192             int[] keys;
1193             Object[] values;
1194 
Level(int levelBitsList, int shift)1195             Level(int levelBitsList, int shift) {
1196                 this.levelBitsList = levelBitsList;
1197                 this.shift = shift;
1198                 int bits = levelBitsList & 0xf;
1199                 assert bits != 0;
1200                 int length = 1 << bits;
1201                 mask = length - 1;
1202                 keys = new int[length];
1203                 values = new Object[length];
1204             }
1205 
get(int key)1206             Object get(int key) {
1207                 int index = (key >> shift) & mask;
1208                 int k = keys[index];
1209                 if(k == key) {
1210                     return values[index];
1211                 }
1212                 if(k == 0) {
1213                     Level level = (Level)values[index];
1214                     if(level != null) {
1215                         return level.get(key);
1216                     }
1217                 }
1218                 return null;
1219             }
1220 
putIfAbsent(int key, Object item, int size)1221             Object putIfAbsent(int key, Object item, int size) {
1222                 int index = (key >> shift) & mask;
1223                 int k = keys[index];
1224                 if(k == key) {
1225                     return putIfCleared(values, index, item, size);
1226                 }
1227                 if(k == 0) {
1228                     Level level = (Level)values[index];
1229                     if(level != null) {
1230                         return level.putIfAbsent(key, item, size);
1231                     }
1232                     keys[index] = key;
1233                     values[index] = storeDirectly(size) ? item : new SoftReference<>(item);
1234                     return item;
1235                 }
1236                 // Collision: Add a child level, move the old item there,
1237                 // and then insert the current item.
1238                 Level level = new Level(levelBitsList >> 4, shift + (levelBitsList & 0xf));
1239                 int i = (k >> level.shift) & level.mask;
1240                 level.keys[i] = k;
1241                 level.values[i] = values[index];
1242                 keys[index] = 0;
1243                 values[index] = level;
1244                 return level.putIfAbsent(key, item, size);
1245             }
1246         }
1247 
ResourceCache(int maxOffset)1248         ResourceCache(int maxOffset) {
1249             assert maxOffset != 0;
1250             maxOffsetBits = 28;
1251             while(maxOffset <= 0x7ffffff) {
1252                 maxOffset <<= 1;
1253                 --maxOffsetBits;
1254             }
1255             int keyBits = maxOffsetBits + 2;  // +2 for mini type: at most 30 bits used in a key
1256             // Precompute for each level the number of bits it handles.
1257             if(keyBits <= ROOT_BITS) {
1258                 levelBitsList = keyBits;
1259             } else if(keyBits < (ROOT_BITS + 3)) {
1260                 levelBitsList = 0x30 | (keyBits - 3);
1261             } else {
1262                 levelBitsList = ROOT_BITS;
1263                 keyBits -= ROOT_BITS;
1264                 int shift = 4;
1265                 for(;;) {
1266                     if(keyBits <= NEXT_BITS) {
1267                         levelBitsList |= keyBits << shift;
1268                         break;
1269                     } else if(keyBits < (NEXT_BITS + 3)) {
1270                         levelBitsList |= (0x30 | (keyBits - 3)) << shift;
1271                         break;
1272                     } else {
1273                         levelBitsList |= NEXT_BITS << shift;
1274                         keyBits -= NEXT_BITS;
1275                         shift += 4;
1276                     }
1277                 }
1278             }
1279         }
1280 
1281         /**
1282          * Turns a resource integer (with unused bits in the middle)
1283          * into a key with fewer bits (at most keyBits).
1284          */
makeKey(int res)1285         private int makeKey(int res) {
1286             // It is possible for resources of different types in the 16-bit array
1287             // to share a start offset; distinguish between those with a 2-bit value,
1288             // as a tie-breaker in the bits just above the highest possible offset.
1289             // It is not possible for "regular" resources of different types
1290             // to share a start offset with each other,
1291             // but offsets for 16-bit and "regular" resources overlap;
1292             // use 2-bit value 0 for "regular" resources.
1293             int type = RES_GET_TYPE(res);
1294             int miniType =
1295                     (type == ICUResourceBundle.STRING_V2) ? 1 :
1296                         (type == ICUResourceBundle.TABLE16) ? 3 :
1297                             (type == ICUResourceBundle.ARRAY16) ? 2 : 0;
1298             return RES_GET_OFFSET(res) | (miniType << maxOffsetBits);
1299         }
1300 
findSimple(int key)1301         private int findSimple(int key) {
1302             return Arrays.binarySearch(keys, 0, length, key);
1303         }
1304 
1305         @SuppressWarnings("unchecked")
get(int res)1306         synchronized Object get(int res) {
1307             // Integers and empty resources need not be cached.
1308             // The cache itself uses res=0 for "no match".
1309             assert RES_GET_OFFSET(res) != 0;
1310             Object value;
1311             if(length >= 0) {
1312                 int index = findSimple(res);
1313                 if(index >= 0) {
1314                     value = values[index];
1315                 } else {
1316                     return null;
1317                 }
1318             } else {
1319                 value = rootLevel.get(makeKey(res));
1320                 if(value == null) {
1321                     return null;
1322                 }
1323             }
1324             if(value instanceof SoftReference) {
1325                 value = ((SoftReference<Object>)value).get();
1326             }
1327             return value;  // null if the reference was cleared
1328         }
1329 
putIfAbsent(int res, Object item, int size)1330         synchronized Object putIfAbsent(int res, Object item, int size) {
1331             if(length >= 0) {
1332                 int index = findSimple(res);
1333                 if(index >= 0) {
1334                     return putIfCleared(values, index, item, size);
1335                 } else if(length < SIMPLE_LENGTH) {
1336                     index = ~index;
1337                     if(index < length) {
1338                         System.arraycopy(keys, index, keys, index + 1, length - index);
1339                         System.arraycopy(values, index, values, index + 1, length - index);
1340                     }
1341                     ++length;
1342                     keys[index] = res;
1343                     values[index] = storeDirectly(size) ? item : new SoftReference<>(item);
1344                     return item;
1345                 } else /* not found && length == SIMPLE_LENGTH */ {
1346                     // Grow to become trie-like.
1347                     rootLevel = new Level(levelBitsList, 0);
1348                     for(int i = 0; i < SIMPLE_LENGTH; ++i) {
1349                         rootLevel.putIfAbsent(makeKey(keys[i]), values[i], 0);
1350                     }
1351                     keys = null;
1352                     values = null;
1353                     length = -1;
1354                 }
1355             }
1356             return rootLevel.putIfAbsent(makeKey(res), item, size);
1357         }
1358     }
1359 
1360     private static final String ICU_RESOURCE_SUFFIX = ".res";
1361 
1362     /**
1363      * Gets the full name of the resource with suffix.
1364      */
getFullName(String baseName, String localeName)1365     public static String getFullName(String baseName, String localeName) {
1366         if (baseName == null || baseName.length() == 0) {
1367             if (localeName.length() == 0) {
1368                 return localeName = ULocale.getDefault().toString();
1369             }
1370             return localeName + ICU_RESOURCE_SUFFIX;
1371         } else {
1372             if (baseName.indexOf('.') == -1) {
1373                 if (baseName.charAt(baseName.length() - 1) != '/') {
1374                     return baseName + "/" + localeName + ICU_RESOURCE_SUFFIX;
1375                 } else {
1376                     return baseName + localeName + ICU_RESOURCE_SUFFIX;
1377                 }
1378             } else {
1379                 baseName = baseName.replace('.', '/');
1380                 if (localeName.length() == 0) {
1381                     return baseName + ICU_RESOURCE_SUFFIX;
1382                 } else {
1383                     return baseName + "_" + localeName + ICU_RESOURCE_SUFFIX;
1384                 }
1385             }
1386         }
1387     }
1388 }
1389