Path: blob/master/src/java.base/share/classes/sun/text/CompactByteArray.java
41152 views
/*1* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26* (C) Copyright Taligent, Inc. 1996 - All Rights Reserved27* (C) Copyright IBM Corp. 1996 - All Rights Reserved28*29* The original version of this source code and documentation is copyrighted30* and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These31* materials are provided under terms of a License Agreement between Taligent32* and Sun. This technology is protected by multiple US and International33* patents. This notice and attribution to Taligent may not be removed.34* Taligent is a registered trademark of Taligent, Inc.35*36*/3738package sun.text;394041/**42* class CompactATypeArray : use only on primitive data types43* Provides a compact way to store information that is indexed by Unicode44* values, such as character properties, types, keyboard values, etc.This45* is very useful when you have a block of Unicode data that contains46* significant values while the rest of the Unicode data is unused in the47* application or when you have a lot of redundance, such as where all 21,00048* Han ideographs have the same value. However, lookup is much faster than a49* hash table.50* A compact array of any primitive data type serves two purposes:51* <UL type = circle>52* <LI>Fast access of the indexed values.53* <LI>Smaller memory footprint.54* </UL>55* A compact array is composed of a index array and value array. The index56* array contains the indicies of Unicode characters to the value array.57*58* @see CompactIntArray59* @see CompactShortArray60* @author Helena Shih61*/62public final class CompactByteArray implements Cloneable {6364/**65* The total number of Unicode characters.66*/67public static final int UNICODECOUNT =65536;6869/**70* Constructor for CompactByteArray.71* @param defaultValue the default value of the compact array.72*/73public CompactByteArray(byte defaultValue)74{75int i;76values = new byte[UNICODECOUNT];77indices = new short[INDEXCOUNT];78hashes = new int[INDEXCOUNT];79for (i = 0; i < UNICODECOUNT; ++i) {80values[i] = defaultValue;81}82for (i = 0; i < INDEXCOUNT; ++i) {83indices[i] = (short)(i<<BLOCKSHIFT);84hashes[i] = 0;85}86isCompact = false;87}8889/**90* Constructor for CompactByteArray.91* @param indexArray the indicies of the compact array.92* @param newValues the values of the compact array.93* @exception IllegalArgumentException If index is out of range.94*/95public CompactByteArray(short indexArray[],96byte newValues[])97{98int i;99if (indexArray.length != INDEXCOUNT)100throw new IllegalArgumentException("Index out of bounds!");101for (i = 0; i < INDEXCOUNT; ++i) {102short index = indexArray[i];103if ((index < 0) || (index >= newValues.length+BLOCKCOUNT))104throw new IllegalArgumentException("Index out of bounds!");105}106indices = indexArray;107values = newValues;108isCompact = true;109}110111/**112* Get the mapped value of a Unicode character.113* @param index the character to get the mapped value with114* @return the mapped value of the given character115*/116public byte elementAt(char index)117{118return (values[(indices[index >> BLOCKSHIFT] & 0xFFFF)119+ (index & BLOCKMASK)]);120}121/**122* Set a new value for a Unicode character.123* Set automatically expands the array if it is compacted.124* @param index the character to set the mapped value with125* @param value the new mapped value126*/127public void setElementAt(char index, byte value)128{129if (isCompact)130expand();131values[(int)index] = value;132touchBlock(index >> BLOCKSHIFT, value);133}134135/**136* Set new values for a range of Unicode character.137* @param start the starting offset o of the range138* @param end the ending offset of the range139* @param value the new mapped value140*/141public void setElementAt(char start, char end, byte value)142{143int i;144if (isCompact) {145expand();146}147for (i = start; i <= end; ++i) {148values[i] = value;149touchBlock(i >> BLOCKSHIFT, value);150}151}152153/**154* Compact the array.155*/156public void compact()157{158if (!isCompact) {159int limitCompacted = 0;160int iBlockStart = 0;161short iUntouched = -1;162163for (int i = 0; i < indices.length; ++i, iBlockStart += BLOCKCOUNT) {164indices[i] = -1;165boolean touched = blockTouched(i);166if (!touched && iUntouched != -1) {167// If no values in this block were set, we can just set its168// index to be the same as some other block with no values169// set, assuming we've seen one yet.170indices[i] = iUntouched;171} else {172int jBlockStart = 0;173int j = 0;174for (j = 0; j < limitCompacted;175++j, jBlockStart += BLOCKCOUNT) {176if (hashes[i] == hashes[j] &&177arrayRegionMatches(values, iBlockStart,178values, jBlockStart, BLOCKCOUNT)) {179indices[i] = (short)jBlockStart;180break;181}182}183if (indices[i] == -1) {184// we didn't match, so copy & update185System.arraycopy(values, iBlockStart,186values, jBlockStart, BLOCKCOUNT);187indices[i] = (short)jBlockStart;188hashes[j] = hashes[i];189++limitCompacted;190191if (!touched) {192// If this is the first untouched block we've seen,193// remember its index.194iUntouched = (short)jBlockStart;195}196}197}198}199// we are done compacting, so now make the array shorter200int newSize = limitCompacted*BLOCKCOUNT;201byte[] result = new byte[newSize];202System.arraycopy(values, 0, result, 0, newSize);203values = result;204isCompact = true;205hashes = null;206}207}208209/**210* Convenience utility to compare two arrays of doubles.211* @param len the length to compare.212* The start indices and start+len must be valid.213*/214static final boolean arrayRegionMatches(byte[] source, int sourceStart,215byte[] target, int targetStart,216int len)217{218int sourceEnd = sourceStart + len;219int delta = targetStart - sourceStart;220for (int i = sourceStart; i < sourceEnd; i++) {221if (source[i] != target[i + delta])222return false;223}224return true;225}226227/**228* Remember that a specified block was "touched", i.e. had a value set.229* Untouched blocks can be skipped when compacting the array230*/231private final void touchBlock(int i, int value) {232hashes[i] = (hashes[i] + (value<<1)) | 1;233}234235/**236* Query whether a specified block was "touched", i.e. had a value set.237* Untouched blocks can be skipped when compacting the array238*/239private final boolean blockTouched(int i) {240return hashes[i] != 0;241}242243/**244* For internal use only. Do not modify the result, the behavior of245* modified results are undefined.246*/247public short[] getIndexArray()248{249return indices;250}251252/**253* For internal use only. Do not modify the result, the behavior of254* modified results are undefined.255*/256public byte[] getStringArray()257{258return values;259}260261/**262* Overrides Cloneable263*/264public Object clone()265{266try {267CompactByteArray other = (CompactByteArray) super.clone();268other.values = values.clone();269other.indices = indices.clone();270if (hashes != null) other.hashes = hashes.clone();271return other;272} catch (CloneNotSupportedException e) {273throw new InternalError(e);274}275}276277/**278* Compares the equality of two compact array objects.279* @param obj the compact array object to be compared with this.280* @return true if the current compact array object is the same281* as the compact array object obj; false otherwise.282*/283public boolean equals(Object obj) {284if (obj == null) return false;285if (this == obj) // quick check286return true;287if (getClass() != obj.getClass()) // same class?288return false;289CompactByteArray other = (CompactByteArray) obj;290for (int i = 0; i < UNICODECOUNT; i++) {291// could be sped up later292if (elementAt((char)i) != other.elementAt((char)i))293return false;294}295return true; // we made it through the guantlet.296}297298/**299* Generates the hash code for the compact array object300*/301public int hashCode() {302int result = 0;303int increment = Math.min(3, values.length/16);304for (int i = 0; i < values.length; i+= increment) {305result = result * 37 + values[i];306}307return result;308}309310/**311* Expanding takes the array back to a 65536 element array.312*/313private void expand()314{315int i;316if (isCompact) {317byte[] tempArray;318hashes = new int[INDEXCOUNT];319tempArray = new byte[UNICODECOUNT];320for (i = 0; i < UNICODECOUNT; ++i) {321byte value = elementAt((char)i);322tempArray[i] = value;323touchBlock(i >> BLOCKSHIFT, value);324}325for (i = 0; i < INDEXCOUNT; ++i) {326indices[i] = (short)(i<<BLOCKSHIFT);327}328values = null;329values = tempArray;330isCompact = false;331}332}333334private byte[] getArray()335{336return values;337}338339private static final int BLOCKSHIFT =7;340private static final int BLOCKCOUNT =(1<<BLOCKSHIFT);341private static final int INDEXSHIFT =(16-BLOCKSHIFT);342private static final int INDEXCOUNT =(1<<INDEXSHIFT);343private static final int BLOCKMASK = BLOCKCOUNT - 1;344345private byte[] values; // char -> short (char parameterized short)346private short indices[];347private boolean isCompact;348private int[] hashes;349};350351352