2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright (C) 1997-2015 Oracle and/or its affiliates. All rights reserved.
6 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7 * Other names may be trademarks of their respective owners.
9 * The contents of this file are subject to the terms of either the GNU
10 * General Public License Version 2 only ("GPL") or the Common
11 * Development and Distribution License("CDDL") (collectively, the
12 * "License"). You may not use this file except in compliance with the
13 * License. You can obtain a copy of the License at
14 * http://www.netbeans.org/cddl-gplv2.html
15 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16 * specific language governing permissions and limitations under the
17 * License. When distributing the software, include this License Header
18 * Notice in each file and include the License file at
19 * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
20 * particular file as subject to the "Classpath" exception as provided
21 * by Oracle in the GPL Version 2 section of the License file that
22 * accompanied this code. If applicable, add the following below the
23 * License Header, with the fields enclosed by brackets [] replaced by
24 * your own identifying information:
25 * "Portions Copyrighted [year] [name of copyright owner]"
29 * The Original Software is NetBeans. The Initial Developer of the Original
30 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
31 * Microsystems, Inc. All Rights Reserved.
33 * If you wish your version of this file to be governed by only the CDDL
34 * or only the GPL Version 2, indicate your decision by adding
35 * "[Contributor] elects to include this software in this distribution
36 * under the [CDDL or GPL Version 2] license." If you do not indicate a
37 * single choice of license, a recipient has the option to distribute
38 * your version of this file under either the CDDL, the GPL Version 2 or
39 * to extend the choice of license to its licensees as provided above.
40 * However, if you add GPL Version 2 code and therefore, elected the GPL
41 * Version 2 license, then the option applies only if the new code is
42 * made subject to such option by the copyright holder.
44 package org.netbeans.lib.callgraph.util;
46 import java.io.ByteArrayOutputStream;
47 import java.io.IOException;
48 import java.io.UnsupportedEncodingException;
49 import java.nio.ByteBuffer;
50 import java.nio.charset.CharacterCodingException;
51 import java.nio.charset.Charset;
52 import java.util.ArrayList;
53 import java.util.Arrays;
54 import java.util.Collection;
55 import java.util.Iterator;
56 import java.util.List;
57 import java.util.Locale;
60 * byte[] backed string to cut memory consumption from Strings in half for most
61 * codebases, to reduce memory footprint to process larger codebases by using 8
62 * bits per character instead of 16.
64 * @author Tim Boudreau
66 public class EightBitStrings {
68 private static final Charset UTF = Charset.forName("UTF-8");
70 private final InternTable INTERN_TABLE = new InternTable();
71 public final CharSequence DOT = create(".");
72 public final CharSequence QUOTE = create("\"");
73 public final CharSequence SPACE = create(" ");
74 public final CharSequence QUOTE_SPACE = create("\" ");
75 public final CharSequence CLOSE_OPEN_QUOTE = create("\" \"");
77 private final boolean disabled;
78 private final boolean aggressive;
80 public EightBitStrings(boolean disabled) {
81 this (disabled, false);
84 public EightBitStrings(boolean disabled, boolean aggressive) {
85 this.disabled = disabled;
86 this.aggressive = aggressive;
90 INTERN_TABLE.dispose();
93 public ComparableCharSequence create(CharSequence string) {
95 return string instanceof ComparableCharSequence ? (ComparableCharSequence) string
96 : new StringWrapper(string.toString());
99 return concat(string);
101 return INTERN_TABLE.intern(string);
104 public ComparableCharSequence concat(CharSequence... seqs) {
105 if (seqs.length == 1 && seqs[0] instanceof ComparableCharSequence) {
106 return (ComparableCharSequence) seqs[0];
109 StringBuilder sb = new StringBuilder();
110 for (CharSequence c : seqs) {
113 return new StringWrapper(sb.toString());
114 } else if (aggressive) {
115 List<CharSequence> nue = new ArrayList<>(seqs.length + (seqs.length / 2));
116 for (CharSequence seq : seqs) {
117 int ln = seq.length();
118 StringBuilder sb = new StringBuilder();
119 for(int i=0; i < ln; i++) {
120 char c = seq.charAt(i);
121 if (Character.isLetter(c) || Character.isDigit(c)) {
124 nue.add(sb.toString());
125 sb = new StringBuilder();
126 nue.add(new String(new char[] { c }));
129 if (sb.length() > 0) {
130 nue.add(sb.toString());
133 if (nue.size() != seqs.length) {
134 seqs = nue.toArray(new CharSequence[nue.size()]);
137 return new Concatenation(seqs);
140 public ComparableCharSequence concatQuoted(Collection<Object> seqs) {
142 StringBuilder sb = new StringBuilder("\"");
143 boolean first = true;
144 for (Iterator<Object> it = seqs.iterator(); it.hasNext();) {
145 Object c = it.next();
149 if (c instanceof CharSequence) {
153 if (c instanceof CharSequence) {
158 return new StringWrapper(sb.toString());
160 List<CharSequence> quoted = new ArrayList<>((seqs.size() * 3) + 1);
161 for (Iterator<Object> it = seqs.iterator(); it.hasNext();) {
162 Object c = it.next();
163 if (c instanceof CharSequence) {
165 quoted.add((CharSequence)c);
167 quoted.add(QUOTE_SPACE);
172 quoted.add(create(c.toString()));
176 return new Concatenation(quoted.toArray(new CharSequence[quoted.size()]));
180 private static byte[] toBytes(CharSequence seq) {
182 ByteArrayOutputStream out = new ByteArrayOutputStream();
183 out.write(seq.toString().getBytes(UTF));
184 return out.toByteArray();
185 } catch (IOException ex) {
186 throw new IllegalArgumentException(ex);
190 private static CharSequence toCharSequence(byte[] bytes) {
192 return UTF.newDecoder().decode(ByteBuffer.wrap(bytes));
193 } catch (CharacterCodingException ex) {
194 throw new IllegalArgumentException(ex);
198 int internTableSize() {
199 return INTERN_TABLE.last + 1;
202 List<CharSequence> dumpInternTable() {
203 return INTERN_TABLE.dumpInternTable();
206 static class InternTable {
208 private static final int SIZE_INCREMENT = 50;
210 private int last = -1;
211 private Entry[] entries = new Entry[SIZE_INCREMENT];
214 entries = new Entry[SIZE_INCREMENT];
218 Entry intern(CharSequence seq) {
219 if (seq instanceof Entry) {
222 // We are using an array and binary search to conserve memory
223 // here. This is slower than a HashMap (we sort on insert so
224 // we can binary search later), but involves far fewer allocations
225 Entry entry = new Entry(toBytes(seq), (short) seq.length());
226 int offset = last == -1 ? -1 : Arrays.binarySearch(entries, 0, last + 1, entry);
228 return entries[offset];
230 if (last == entries.length - 1) {
231 Entry[] nue = new Entry[entries.length + SIZE_INCREMENT];
232 System.arraycopy(entries, 0, nue, 0, entries.length);
235 entries[++last] = entry;
236 Arrays.sort(entries, 0, last + 1);
240 List<CharSequence> dumpInternTable() {
241 return Arrays.asList(entries);
244 private static final class Entry implements ComparableCharSequence {
246 private final byte[] bytes;
247 private final short length;
249 public Entry(byte[] bytes, short length) {
251 throw new Error("String too large");
254 this.length = length;
257 public int hashCode() {
259 if (h == 0 && bytes.length > 0) {
260 CharSequence val = toChars();
261 int max = val.length();
262 for (int i = 0; i < max; i++) {
263 h = 31 * h + val.charAt(i);
270 public boolean equals(Object o) {
273 } else if (o == this) {
275 } else if (o instanceof Entry) {
276 Entry other = (Entry) o;
277 if (other.bytes.length < bytes.length) {
280 return Arrays.equals(bytes, other.bytes);
281 } else if (o instanceof CharSequence) {
282 return charSequencesEqual(this, (CharSequence) o);
288 public int compare(Entry o) {
292 int max = Math.min(bytes.length, o.bytes.length);
293 for (int i = 0; i < max; i++) {
294 if (bytes[i] > o.bytes[i]) {
296 } else if (bytes[i] < o.bytes[i]) {
300 if (bytes.length == o.bytes.length) {
302 } else if (bytes.length > o.bytes.length) {
310 public String toString() {
311 return new String(bytes, UTF);
315 public int length() {
319 CharSequence toChars() {
320 return toCharSequence(bytes);
324 public char charAt(int index) {
325 return toCharSequence(bytes).charAt(index);
329 public CharSequence subSequence(int start, int end) {
330 return toCharSequence(bytes).subSequence(start, end);
334 public int compareTo(CharSequence o) {
335 if (o instanceof Entry) {
336 return compare((Entry) o);
338 return compareCharSequences(this, o);
343 static int compareCharSequences(CharSequence a, CharSequence b) {
347 int aLength = a.length();
348 int bLength = b.length();
349 int max = Math.min(aLength, bLength);
350 for (int i = 0; i < max; i++) {
351 if (a.charAt(i) > b.charAt(i)) {
353 } else if (a.charAt(i) < b.charAt(i)) {
357 if (aLength == bLength) {
359 } else if (aLength > bLength) {
366 static boolean debug;
367 class Concatenation implements ComparableCharSequence, Comparable<CharSequence> {
369 private final InternTable.Entry[] entries;
371 Concatenation(CharSequence... entries) {
372 List<InternTable.Entry> l = new ArrayList<>(entries.length);
373 for (CharSequence cs : entries) {
374 if (cs instanceof Concatenation) {
375 Concatenation c1 = (Concatenation) cs;
376 l.addAll(Arrays.asList(c1.entries));
378 l.add(INTERN_TABLE.intern(cs));
381 this.entries = l.toArray(new InternTable.Entry[l.size()]);
385 public int length() {
387 for (int i = 0; i < entries.length; i++) {
388 result += entries[i].length;
394 public char charAt(int index) {
395 if (entries.length == 0) {
396 throw new IndexOutOfBoundsException("0 length but asked for " + index);
398 for (int i = 0; i < entries.length; i++) {
399 InternTable.Entry e = entries[i];
400 if (index >= e.length) {
403 return e.charAt(index);
406 throw new IndexOutOfBoundsException(index + " of " + length() + " in " + this + " with entries " + Arrays.asList(entries));
410 public CharSequence subSequence(int start, int end) {
411 return INTERN_TABLE.intern(toString().subSequence(start, end));
414 public String toString() {
415 StringBuilder sb = new StringBuilder();
416 for (InternTable.Entry e : entries) {
420 sb.append(" - ").append (Arrays.asList(entries));
422 return sb.toString();
425 private int hash = 0;
427 public int hashCode() {
429 if (h == 0 && length() > 0) {
430 for (InternTable.Entry e : entries) {
431 CharSequence chars = e.toChars();
432 int max = chars.length();
433 for (int i = 0; i < max; i++) {
434 h = 31 * h + chars.charAt(i);
442 public boolean equals(Object o) {
444 if (o instanceof CharSequence) {
445 return charSequencesEqual(this, (CharSequence) o);
450 } else if (o == null) {
452 } else if (o instanceof Concatenation) {
453 Concatenation c = (Concatenation) o;
454 if (c.entries.length != entries.length) {
455 return charSequencesEqual(this, c);
457 for (int i = 0; i < entries.length; i++) {
458 if (entries[i].length() != entries[i].length) {
459 return charSequencesEqual(this, c);
461 if (!entries[i].equals(c.entries[i])) {
466 } else if (o instanceof CharSequence) {
467 return charSequencesEqual(this, (CharSequence) o);
474 public int compareTo(CharSequence o) {
475 return compareCharSequences(this, o);
479 private static boolean charSequencesEqual(CharSequence a, CharSequence b) {
480 int maxA = a.length();
481 int maxB = b.length();
485 for (int i = 0; i < maxA; i++) {
486 char aa = a.charAt(i);
487 char bb = b.charAt(i);
495 static class StringWrapper implements ComparableCharSequence {
497 private final String s;
499 public StringWrapper(String s) {
503 public int length() {
507 public boolean isEmpty() {
511 public char charAt(int index) {
512 return s.charAt(index);
515 public int codePointAt(int index) {
516 return s.codePointAt(index);
519 public int codePointBefore(int index) {
520 return s.codePointBefore(index);
523 public int codePointCount(int beginIndex, int endIndex) {
524 return s.codePointCount(beginIndex, endIndex);
527 public int offsetByCodePoints(int index, int codePointOffset) {
528 return s.offsetByCodePoints(index, codePointOffset);
531 public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) {
532 s.getChars(srcBegin, srcEnd, dst, dstBegin);
535 public void getBytes(int srcBegin, int srcEnd, byte[] dst, int dstBegin) {
536 s.getBytes(srcBegin, srcEnd, dst, dstBegin);
539 public byte[] getBytes(String charsetName) throws UnsupportedEncodingException {
540 return s.getBytes(charsetName);
543 public byte[] getBytes(Charset charset) {
544 return s.getBytes(charset);
547 public byte[] getBytes() {
551 public boolean equals(Object anObject) {
552 if (anObject == this) {
555 if (anObject == null) {
558 if (!(anObject instanceof CharSequence)) {
561 return charSequencesEqual(this, (CharSequence) anObject);
564 public boolean contentEquals(StringBuffer sb) {
565 return s.contentEquals(sb);
568 public boolean contentEquals(CharSequence cs) {
569 return s.contentEquals(cs);
572 public boolean equalsIgnoreCase(String anotherString) {
573 return s.equalsIgnoreCase(anotherString);
576 public int compareTo(CharSequence anotherString) {
577 return compareCharSequences(this, anotherString);
580 public int compareToIgnoreCase(String str) {
581 return s.compareToIgnoreCase(str);
584 public boolean regionMatches(int toffset, String other, int ooffset, int len) {
585 return s.regionMatches(toffset, other, ooffset, len);
588 public boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) {
589 return s.regionMatches(ignoreCase, toffset, other, ooffset, len);
592 public boolean startsWith(String prefix, int toffset) {
593 return s.startsWith(prefix, toffset);
596 public boolean startsWith(String prefix) {
597 return s.startsWith(prefix);
600 public boolean endsWith(String suffix) {
601 return s.endsWith(suffix);
605 public int hashCode() {
609 public int indexOf(int ch) {
610 return s.indexOf(ch);
613 public int indexOf(int ch, int fromIndex) {
614 return s.indexOf(ch, fromIndex);
617 public int lastIndexOf(int ch) {
618 return s.lastIndexOf(ch);
621 public int lastIndexOf(int ch, int fromIndex) {
622 return s.lastIndexOf(ch, fromIndex);
625 public int indexOf(String str) {
626 return s.indexOf(str);
629 public int indexOf(String str, int fromIndex) {
630 return s.indexOf(str, fromIndex);
633 public int lastIndexOf(String str) {
634 return s.lastIndexOf(str);
637 public int lastIndexOf(String str, int fromIndex) {
638 return s.lastIndexOf(str, fromIndex);
641 public String substring(int beginIndex) {
642 return s.substring(beginIndex);
645 public String substring(int beginIndex, int endIndex) {
646 return s.substring(beginIndex, endIndex);
649 public CharSequence subSequence(int beginIndex, int endIndex) {
650 return s.subSequence(beginIndex, endIndex);
653 public String concat(String str) {
654 return s.concat(str);
657 public String replace(char oldChar, char newChar) {
658 return s.replace(oldChar, newChar);
661 public boolean matches(String regex) {
662 return s.matches(regex);
665 public boolean contains(CharSequence s) {
666 return this.s.contains(s);
669 public String replaceFirst(String regex, String replacement) {
670 return s.replaceFirst(regex, replacement);
673 public String replaceAll(String regex, String replacement) {
674 return s.replaceAll(regex, replacement);
677 public String replace(CharSequence target, CharSequence replacement) {
678 return s.replace(target, replacement);
681 public String[] split(String regex, int limit) {
682 return s.split(regex, limit);
685 public String[] split(String regex) {
686 return s.split(regex);
689 public static String join(CharSequence delimiter, CharSequence... elements) {
690 return String.join(delimiter, elements);
693 public static String join(CharSequence delimiter, Iterable<? extends CharSequence> elements) {
694 return String.join(delimiter, elements);
697 public String toLowerCase(Locale locale) {
698 return s.toLowerCase(locale);
701 public String toLowerCase() {
702 return s.toLowerCase();
705 public String toUpperCase(Locale locale) {
706 return s.toUpperCase(locale);
709 public String toUpperCase() {
710 return s.toUpperCase();
713 public String trim() {
717 public String toString() {
721 public char[] toCharArray() {
722 return s.toCharArray();
725 public static String format(String format, Object... args) {
726 return String.format(format, args);
729 public static String format(Locale l, String format, Object... args) {
730 return String.format(l, format, args);
733 public static String valueOf(Object obj) {
734 return String.valueOf(obj);
737 public static String valueOf(char[] data) {
738 return String.valueOf(data);
741 public static String valueOf(char[] data, int offset, int count) {
742 return String.valueOf(data, offset, count);
745 public static String copyValueOf(char[] data, int offset, int count) {
746 return String.copyValueOf(data, offset, count);
749 public static String copyValueOf(char[] data) {
750 return String.copyValueOf(data);
753 public static String valueOf(boolean b) {
754 return String.valueOf(b);
757 public static String valueOf(char c) {
758 return String.valueOf(c);
761 public static String valueOf(int i) {
762 return String.valueOf(i);
765 public static String valueOf(long l) {
766 return String.valueOf(l);
769 public static String valueOf(float f) {
770 return String.valueOf(f);
773 public static String valueOf(double d) {
774 return String.valueOf(d);
777 public String intern() {