Line 0
Link Here
|
|
|
1 |
/* |
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
3 |
* |
4 |
* Copyright 2010 Sun Microsystems, Inc. All rights reserved. |
5 |
* |
6 |
* The contents of this file are subject to the terms of either the GNU |
7 |
* General Public License Version 2 only ("GPL") or the Common |
8 |
* Development and Distribution License("CDDL") (collectively, the |
9 |
* "License"). You may not use this file except in compliance with the |
10 |
* License. You can obtain a copy of the License at |
11 |
* http://www.netbeans.org/cddl-gplv2.html |
12 |
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the |
13 |
* specific language governing permissions and limitations under the |
14 |
* License. When distributing the software, include this License Header |
15 |
* Notice in each file and include the License file at |
16 |
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this |
17 |
* particular file as subject to the "Classpath" exception as provided |
18 |
* by Sun in the GPL Version 2 section of the License file that |
19 |
* accompanied this code. If applicable, add the following below the |
20 |
* License Header, with the fields enclosed by brackets [] replaced by |
21 |
* your own identifying information: |
22 |
* "Portions Copyrighted [year] [name of copyright owner]" |
23 |
* |
24 |
* If you wish your version of this file to be governed by only the CDDL |
25 |
* or only the GPL Version 2, indicate your decision by adding |
26 |
* "[Contributor] elects to include this software in this distribution |
27 |
* under the [CDDL or GPL Version 2] license." If you do not indicate a |
28 |
* single choice of license, a recipient has the option to distribute |
29 |
* your version of this file under either the CDDL, the GPL Version 2 or |
30 |
* to extend the choice of license to its licensees as provided above. |
31 |
* However, if you add GPL Version 2 code and therefore, elected the GPL |
32 |
* Version 2 license, then the option applies only if the new code is |
33 |
* made subject to such option by the copyright holder. |
34 |
* |
35 |
* Contributor(s): |
36 |
* |
37 |
* Portions Copyrighted 2010 Sun Microsystems, Inc. |
38 |
*/ |
39 |
package org.openide.util; |
40 |
|
41 |
import java.util.Arrays; |
42 |
import java.util.Comparator; |
43 |
|
44 |
/** |
45 |
* Useful static methods to provide and work with memory efficient CharSequence |
46 |
* implementations for ASCII strings. |
47 |
* |
48 |
* @since 8.3 |
49 |
* @author Alexander Simon |
50 |
* @author Vladimir Voskresensky |
51 |
*/ |
52 |
public final class CharSequences { |
53 |
|
54 |
/** |
55 |
* Provides compact char sequence object like {@link String#String(char[], int, int)} |
56 |
*/ |
57 |
public static CharSequence create(char buf[], int start, int count) { |
58 |
if (start < 0) { |
59 |
throw new StringIndexOutOfBoundsException(start); |
60 |
} |
61 |
if (count < 0) { |
62 |
throw new StringIndexOutOfBoundsException(count); |
63 |
} |
64 |
// Note: offset or count might be near -1>>>1. |
65 |
if (start > buf.length - count) { |
66 |
throw new StringIndexOutOfBoundsException(start + count); |
67 |
} |
68 |
int n = count; |
69 |
if (n == 0) { |
70 |
// special constant shared among all empty char sequences |
71 |
return EMPTY; |
72 |
} |
73 |
byte[] b = new byte[n]; |
74 |
boolean bytes = true; |
75 |
int o; |
76 |
// check 2 bytes vs 1 byte chars |
77 |
for (int i = 0; i < n; i++) { |
78 |
o = buf[start + i]; |
79 |
if ((o & 0xFF) != o) { |
80 |
// can not compact this char sequence |
81 |
bytes = false; |
82 |
break; |
83 |
} |
84 |
b[i] = (byte) o; |
85 |
} |
86 |
if (bytes) { |
87 |
return createFromBytes(b, n); |
88 |
} |
89 |
char[] v = new char[count]; |
90 |
System.arraycopy(buf, start, v, 0, count); |
91 |
return new CharBasedSequence(v); |
92 |
} |
93 |
|
94 |
/** |
95 |
* Provides compact char sequence object like {@link String#String(String)} |
96 |
*/ |
97 |
public static CharSequence create(CharSequence s) { |
98 |
if (s == null) { |
99 |
return null; |
100 |
} |
101 |
// already compact instance |
102 |
if (s instanceof CompactCharSequence) { |
103 |
return s; |
104 |
} |
105 |
int n = s.length(); |
106 |
if (n == 0) { |
107 |
// special constant shared among all empty char sequences |
108 |
return EMPTY; |
109 |
} |
110 |
byte[] b = new byte[n]; |
111 |
boolean bytes = true; |
112 |
int o; |
113 |
for (int i = 0; i < n; i++) { |
114 |
o = s.charAt(i); |
115 |
if ((o & 0xFF) != o) { |
116 |
bytes = false; |
117 |
break; |
118 |
} |
119 |
b[i] = (byte) o; |
120 |
} |
121 |
if (bytes) { |
122 |
return createFromBytes(b, n); |
123 |
} |
124 |
char[] v = new char[n]; |
125 |
for (int i = 0; i < n; i++) { |
126 |
v[i] = s.charAt(i); |
127 |
} |
128 |
return new CharBasedSequence(v); |
129 |
} |
130 |
|
131 |
/** |
132 |
* Provides optimized char sequences comparator |
133 |
* @param ignoreCase true to get case insensitive comparator |
134 |
* false to get case sensitive comparator |
135 |
* @return comparator |
136 |
*/ |
137 |
public static Comparator<CharSequence> comparator(boolean ignoreCase) { |
138 |
return ignoreCase ? ComparatorIgnoreCase : Comparator; |
139 |
} |
140 |
|
141 |
/** |
142 |
* Returns object to represent empty sequence "" |
143 |
* @return char sequence to represent empty sequence |
144 |
*/ |
145 |
public static CharSequence empty() { |
146 |
return EMPTY; |
147 |
} |
148 |
|
149 |
/** |
150 |
* Predicate to check if provides char sequence is based on compact implementation |
151 |
* @param cs char sequence object to check |
152 |
* @return true if compact implementation, false otherwise |
153 |
*/ |
154 |
public static boolean isCompact(CharSequence cs) { |
155 |
return cs instanceof CompactCharSequence; |
156 |
} |
157 |
|
158 |
/** |
159 |
* Implementation of {@link String#indexOf(String)} for character sequences. |
160 |
*/ |
161 |
public static int indexOf(CharSequence text, CharSequence seq) { |
162 |
return indexOf(text, seq, 0); |
163 |
} |
164 |
|
165 |
/** |
166 |
* Implementation of {@link String#indexOf(String,int)} for character sequences. |
167 |
*/ |
168 |
public static int indexOf(CharSequence text, CharSequence seq, int fromIndex) { |
169 |
int textLength = text.length(); |
170 |
int seqLength = seq.length(); |
171 |
if (fromIndex >= textLength) { |
172 |
return (seqLength == 0 ? textLength : -1); |
173 |
} |
174 |
if (fromIndex < 0) { |
175 |
fromIndex = 0; |
176 |
} |
177 |
if (seqLength == 0) { |
178 |
return fromIndex; |
179 |
} |
180 |
|
181 |
char first = seq.charAt(0); |
182 |
int max = textLength - seqLength; |
183 |
|
184 |
for (int i = fromIndex; i <= max; i++) { |
185 |
// look for first character |
186 |
if (text.charAt(i) != first) { |
187 |
while (++i <= max && text.charAt(i) != first) { |
188 |
} |
189 |
} |
190 |
|
191 |
// found first character, now look at the rest of seq |
192 |
if (i <= max) { |
193 |
int j = i + 1; |
194 |
int end = j + seqLength - 1; |
195 |
for (int k = 1; j < end && text.charAt(j) == seq.charAt(k); j++, k++) { |
196 |
} |
197 |
if (j == end) { |
198 |
// found whole sequence |
199 |
return i; |
200 |
} |
201 |
} |
202 |
} |
203 |
return -1; |
204 |
} |
205 |
|
206 |
private static CompactCharSequence createFromBytes(byte[] b, int n) { |
207 |
if (n < 8) { |
208 |
return new Fixed_0_7(b, n); |
209 |
} else if (n < 16) { |
210 |
return new Fixed_8_15(b, n); |
211 |
} else if (n < 24) { |
212 |
return new Fixed_16_23(b, n); |
213 |
} |
214 |
return new ByteBasedSequence(b); |
215 |
} |
216 |
|
217 |
//////////////////////////////////////////////////////////////////////////////// |
218 |
// Memory efficient implementations of CharSequence |
219 |
// Comparision between Fixed and String memory consumption: |
220 |
// 32-bit JVM |
221 |
//String String CharSequence |
222 |
//Length Size Size |
223 |
//1..2 40 16 |
224 |
//3..6 48 16 |
225 |
//7..7 56 16 |
226 |
//8..10 56 24 |
227 |
//11..14 64 24 |
228 |
//15..15 72 24 |
229 |
//16..18 72 32 |
230 |
//19..22 80 32 |
231 |
//23..23 88 32 |
232 |
//24..26 88 56 |
233 |
//27..28 96 56 |
234 |
//29..30 96 64 |
235 |
//31..34 104 64 |
236 |
//35..36 112 64 |
237 |
//37..38 112 72 |
238 |
//39..42 120 72 |
239 |
//...................... |
240 |
//79..82 - 200 112 |
241 |
// |
242 |
// 64-bit JVM |
243 |
//1 72 24 |
244 |
//2 72 24 |
245 |
//3 80 24 |
246 |
//4 80 24 |
247 |
//5 80 24 |
248 |
//6 80 24 |
249 |
//7 88 24 |
250 |
//8 88 32 |
251 |
//9 88 32 |
252 |
//11 96 32 |
253 |
//13 96 32 |
254 |
//15 104 32 |
255 |
//16 104 40 |
256 |
//17 104 40 |
257 |
//18 112 40 |
258 |
//19 112 40 |
259 |
//20 112 40 |
260 |
//22 120 40 |
261 |
//23 120 40 |
262 |
//24 120 80 |
263 |
//25 120 88 |
264 |
//26 128 88 |
265 |
//60 192 120 |
266 |
//100 272 160 |
267 |
|
268 |
//<editor-fold defaultstate="collapsed" desc="Private Classes"> |
269 |
|
270 |
/** |
271 |
* compact char sequence implementation for strings in range 0-7 characters |
272 |
* 8 + 2*4 = 16 bytes for all strings vs String impl occupying |
273 |
*/ |
274 |
private static final class Fixed_0_7 implements CompactCharSequence, Comparable<CharSequence> { |
275 |
|
276 |
private final int i1; |
277 |
private final int i2; |
278 |
|
279 |
@SuppressWarnings("fallthrough") |
280 |
private Fixed_0_7(byte[] b, int n) { |
281 |
int a1 = n; |
282 |
int a2 = 0; |
283 |
switch (n) { |
284 |
case 7: |
285 |
a2 += (b[6] & 0xFF) << 24; |
286 |
case 6: |
287 |
a2 += (b[5] & 0xFF) << 16; |
288 |
case 5: |
289 |
a2 += (b[4] & 0xFF) << 8; |
290 |
case 4: |
291 |
a2 += b[3] & 0xFF; |
292 |
case 3: |
293 |
a1 += (b[2] & 0xFF) << 24; |
294 |
case 2: |
295 |
a1 += (b[1] & 0xFF) << 16; |
296 |
case 1: |
297 |
a1 += (b[0] & 0xFF) << 8; |
298 |
case 0: |
299 |
break; |
300 |
default: |
301 |
throw new IllegalArgumentException(); |
302 |
} |
303 |
i1 = a1; |
304 |
i2 = a2; |
305 |
} |
306 |
|
307 |
@Override |
308 |
public int length() { |
309 |
return i1 & 0xFF; |
310 |
} |
311 |
|
312 |
@Override |
313 |
public char charAt(int index) { |
314 |
int r = 0; |
315 |
switch (index) { |
316 |
case 0: |
317 |
r = (i1 & 0xFF00) >> 8; |
318 |
break; |
319 |
case 1: |
320 |
r = (i1 & 0xFF0000) >> 16; |
321 |
break; |
322 |
case 2: |
323 |
r = (i1 >> 24) & 0xFF; |
324 |
break; |
325 |
case 3: |
326 |
r = i2 & 0xFF; |
327 |
break; |
328 |
case 4: |
329 |
r = (i2 & 0xFF00) >> 8; |
330 |
break; |
331 |
case 5: |
332 |
r = (i2 & 0xFF0000) >> 16; |
333 |
break; |
334 |
case 6: |
335 |
r = (i2 >> 24) & 0xFF; |
336 |
break; |
337 |
} |
338 |
return (char) r; |
339 |
} |
340 |
|
341 |
@Override |
342 |
public String toString() { |
343 |
int n = length(); |
344 |
char[] r = new char[n]; |
345 |
for (int i = 0; i < n; i++) { |
346 |
r[i] = charAt(i); |
347 |
} |
348 |
return new String(r); |
349 |
} |
350 |
|
351 |
@Override |
352 |
public boolean equals(Object object) { |
353 |
if (this == object) { |
354 |
return true; |
355 |
} |
356 |
if (object instanceof Fixed_0_7) { |
357 |
Fixed_0_7 otherString = (Fixed_0_7) object; |
358 |
return i1 == otherString.i1 && i2 == otherString.i2; |
359 |
} |
360 |
return false; |
361 |
} |
362 |
|
363 |
@Override |
364 |
public int hashCode() { |
365 |
int hash = 0; |
366 |
for (int i = 0; i < length(); i++) { |
367 |
hash = 31 * hash + charAt(i); |
368 |
} |
369 |
return hash; |
370 |
// return (i1 >> 4) + (i1 >> 8) + (i2 << 5) - i2; |
371 |
} |
372 |
|
373 |
@Override |
374 |
public CharSequence subSequence(int start, int end) { |
375 |
return CharSequences.create(toString().substring(start, end)); |
376 |
} |
377 |
|
378 |
@Override |
379 |
public int compareTo(CharSequence o) { |
380 |
return Comparator.compare(this, o); |
381 |
} |
382 |
} |
383 |
|
384 |
/** |
385 |
* compact char sequence implementation for strings in range 8-15 characters |
386 |
* size: 8 + 4*4 = 24 bytes for all strings vs String impl occupying |
387 |
*/ |
388 |
private static final class Fixed_8_15 implements CompactCharSequence, Comparable<CharSequence> { |
389 |
|
390 |
private final int i1; |
391 |
private final int i2; |
392 |
private final int i3; |
393 |
private final int i4; |
394 |
|
395 |
@SuppressWarnings("fallthrough") |
396 |
private Fixed_8_15(byte[] b, int n) { |
397 |
int a1 = n; |
398 |
int a2 = 0; |
399 |
int a3 = 0; |
400 |
int a4 = 0; |
401 |
switch (n) { |
402 |
case 15: |
403 |
a4 += (b[14] & 0xFF) << 24; |
404 |
case 14: |
405 |
a4 += (b[13] & 0xFF) << 16; |
406 |
case 13: |
407 |
a4 += (b[12] & 0xFF) << 8; |
408 |
case 12: |
409 |
a4 += b[11] & 0xFF; |
410 |
case 11: |
411 |
a3 += (b[10] & 0xFF) << 24; |
412 |
case 10: |
413 |
a3 += (b[9] & 0xFF) << 16; |
414 |
case 9: |
415 |
a3 += (b[8] & 0xFF) << 8; |
416 |
case 8: |
417 |
a3 += b[7] & 0xFF; |
418 |
case 7: |
419 |
a2 += (b[6] & 0xFF) << 24; |
420 |
case 6: |
421 |
a2 += (b[5] & 0xFF) << 16; |
422 |
case 5: |
423 |
a2 += (b[4] & 0xFF) << 8; |
424 |
case 4: |
425 |
a2 += b[3] & 0xFF; |
426 |
case 3: |
427 |
a1 += (b[2] & 0xFF) << 24; |
428 |
case 2: |
429 |
a1 += (b[1] & 0xFF) << 16; |
430 |
case 1: |
431 |
a1 += (b[0] & 0xFF) << 8; |
432 |
case 0: |
433 |
break; |
434 |
default: |
435 |
throw new IllegalArgumentException(); |
436 |
} |
437 |
i1 = a1; |
438 |
i2 = a2; |
439 |
i3 = a3; |
440 |
i4 = a4; |
441 |
} |
442 |
|
443 |
@Override |
444 |
public int length() { |
445 |
return i1 & 0xFF; |
446 |
} |
447 |
|
448 |
@Override |
449 |
public char charAt(int index) { |
450 |
int r = 0; |
451 |
switch (index) { |
452 |
case 0: |
453 |
r = (i1 & 0xFF00) >> 8; |
454 |
break; |
455 |
case 1: |
456 |
r = (i1 & 0xFF0000) >> 16; |
457 |
break; |
458 |
case 2: |
459 |
r = (i1 >> 24) & 0xFF; |
460 |
break; |
461 |
case 3: |
462 |
r = i2 & 0xFF; |
463 |
break; |
464 |
case 4: |
465 |
r = (i2 & 0xFF00) >> 8; |
466 |
break; |
467 |
case 5: |
468 |
r = (i2 & 0xFF0000) >> 16; |
469 |
break; |
470 |
case 6: |
471 |
r = (i2 >> 24) & 0xFF; |
472 |
break; |
473 |
case 7: |
474 |
r = i3 & 0xFF; |
475 |
break; |
476 |
case 8: |
477 |
r = (i3 & 0xFF00) >> 8; |
478 |
break; |
479 |
case 9: |
480 |
r = (i3 & 0xFF0000) >> 16; |
481 |
break; |
482 |
case 10: |
483 |
r = (i3 >> 24) & 0xFF; |
484 |
break; |
485 |
case 11: |
486 |
r = i4 & 0xFF; |
487 |
break; |
488 |
case 12: |
489 |
r = (i4 & 0xFF00) >> 8; |
490 |
break; |
491 |
case 13: |
492 |
r = (i4 & 0xFF0000) >> 16; |
493 |
break; |
494 |
case 14: |
495 |
r = (i4 >> 24) & 0xFF; |
496 |
break; |
497 |
} |
498 |
return (char) r; |
499 |
} |
500 |
|
501 |
@Override |
502 |
public String toString() { |
503 |
int n = length(); |
504 |
char[] r = new char[n]; |
505 |
for (int i = 0; i < n; i++) { |
506 |
r[i] = charAt(i); |
507 |
} |
508 |
return new String(r); |
509 |
} |
510 |
|
511 |
@Override |
512 |
public boolean equals(Object object) { |
513 |
if (this == object) { |
514 |
return true; |
515 |
} |
516 |
if (object instanceof Fixed_8_15) { |
517 |
Fixed_8_15 otherString = (Fixed_8_15) object; |
518 |
return i1 == otherString.i1 && i2 == otherString.i2 && i3 == otherString.i3 && i4 == otherString.i4; |
519 |
} |
520 |
return false; |
521 |
} |
522 |
|
523 |
@Override |
524 |
public int hashCode() { |
525 |
return i1 + 31 * (i2 + 31 * (i3 + 31 * i4)); |
526 |
} |
527 |
|
528 |
@Override |
529 |
public CharSequence subSequence(int start, int end) { |
530 |
return CharSequences.create(toString().substring(start, end)); |
531 |
} |
532 |
|
533 |
@Override |
534 |
public int compareTo(CharSequence o) { |
535 |
return Comparator.compare(this, o); |
536 |
} |
537 |
} |
538 |
|
539 |
/** |
540 |
* compact char sequence implementation for strings in range 16-23 characters |
541 |
* size: 8 + 3*8 = 32 bytes for all strings vs String impl occupying |
542 |
*/ |
543 |
private static final class Fixed_16_23 implements CompactCharSequence, Comparable<CharSequence> { |
544 |
|
545 |
private final long i1; |
546 |
private final long i2; |
547 |
private final long i3; |
548 |
|
549 |
@SuppressWarnings("fallthrough") |
550 |
private Fixed_16_23(byte[] b, int n) { |
551 |
long a1 = 0; |
552 |
long a2 = 0; |
553 |
long a3 = 0; |
554 |
switch (n) { |
555 |
case 23: |
556 |
a3 += (b[22] & 0xFF) << 24; |
557 |
case 22: |
558 |
a3 += (b[21] & 0xFF) << 16; |
559 |
case 21: |
560 |
a3 += (b[20] & 0xFF) << 8; |
561 |
case 20: |
562 |
a3 += (b[19] & 0xFF); |
563 |
a3 <<= 32; |
564 |
case 19: |
565 |
a3 += (b[18] & 0xFF) << 24; |
566 |
case 18: |
567 |
a3 += (b[17] & 0xFF) << 16; |
568 |
case 17: |
569 |
a3 += (b[16] & 0xFF) << 8; |
570 |
case 16: |
571 |
a3 += b[15] & 0xFF; |
572 |
case 15: |
573 |
a2 += (b[14] & 0xFF) << 24; |
574 |
case 14: |
575 |
a2 += (b[13] & 0xFF) << 16; |
576 |
case 13: |
577 |
a2 += (b[12] & 0xFF) << 8; |
578 |
case 12: |
579 |
a2 += (b[11] & 0xFF); |
580 |
a2 <<= 32; |
581 |
case 11: |
582 |
a2 += (b[10] & 0xFF) << 24; |
583 |
case 10: |
584 |
a2 += (b[9] & 0xFF) << 16; |
585 |
case 9: |
586 |
a2 += (b[8] & 0xFF) << 8; |
587 |
case 8: |
588 |
a2 += b[7] & 0xFF; |
589 |
case 7: |
590 |
a1 += (b[6] & 0xFF) << 24; |
591 |
case 6: |
592 |
a1 += (b[5] & 0xFF) << 16; |
593 |
case 5: |
594 |
a1 += (b[4] & 0xFF) << 8; |
595 |
case 4: |
596 |
a1 += (b[3] & 0xFF); |
597 |
a1 <<= 32; |
598 |
case 3: |
599 |
a1 += (b[2] & 0xFF) << 24; |
600 |
case 2: |
601 |
a1 += (b[1] & 0xFF) << 16; |
602 |
case 1: |
603 |
a1 += (b[0] & 0xFF) << 8; |
604 |
case 0: |
605 |
a1 += n; |
606 |
break; |
607 |
default: |
608 |
throw new IllegalArgumentException(); |
609 |
} |
610 |
i1 = a1; |
611 |
i2 = a2; |
612 |
i3 = a3; |
613 |
} |
614 |
|
615 |
@Override |
616 |
public int length() { |
617 |
return (int) (i1 & 0xFF); |
618 |
} |
619 |
|
620 |
@Override |
621 |
public char charAt(int index) { |
622 |
int r = 0; |
623 |
switch (index) { |
624 |
case 0: |
625 |
r = (int) ((i1 >> 8) & 0xFFL); |
626 |
break; |
627 |
case 1: |
628 |
r = (int) ((i1 >> 16) & 0xFFL); |
629 |
break; |
630 |
case 2: |
631 |
r = (int) ((i1 >> 24) & 0xFFL); |
632 |
break; |
633 |
case 3: |
634 |
r = (int) ((i1 >> 32) & 0xFFL); |
635 |
break; |
636 |
case 4: |
637 |
r = (int) ((i1 >> 40) & 0xFFL); |
638 |
break; |
639 |
case 5: |
640 |
r = (int) ((i1 >> 48) & 0xFFL); |
641 |
break; |
642 |
case 6: |
643 |
r = (int) ((i1 >> 56) & 0xFFL); |
644 |
break; |
645 |
case 7: |
646 |
r = (int) (i2 & 0xFFL); |
647 |
break; |
648 |
case 8: |
649 |
r = (int) ((i2 >> 8) & 0xFFL); |
650 |
break; |
651 |
case 9: |
652 |
r = (int) ((i2 >> 16) & 0xFFL); |
653 |
break; |
654 |
case 10: |
655 |
r = (int) ((i2 >> 24) & 0xFFL); |
656 |
break; |
657 |
case 11: |
658 |
r = (int) ((i2 >> 32) & 0xFFL); |
659 |
break; |
660 |
case 12: |
661 |
r = (int) ((i2 >> 40) & 0xFFL); |
662 |
break; |
663 |
case 13: |
664 |
r = (int) ((i2 >> 48) & 0xFFL); |
665 |
break; |
666 |
case 14: |
667 |
r = (int) ((i2 >> 56) & 0xFFL); |
668 |
break; |
669 |
case 15: |
670 |
r = (int) (i3 & 0xFFL); |
671 |
break; |
672 |
case 16: |
673 |
r = (int) ((i3 >> 8) & 0xFFL); |
674 |
break; |
675 |
case 17: |
676 |
r = (int) ((i3 >> 16) & 0xFFL); |
677 |
break; |
678 |
case 18: |
679 |
r = (int) ((i3 >> 24) & 0xFFL); |
680 |
break; |
681 |
case 19: |
682 |
r = (int) ((i3 >> 32) & 0xFFL); |
683 |
break; |
684 |
case 20: |
685 |
r = (int) ((i3 >> 40) & 0xFFL); |
686 |
break; |
687 |
case 21: |
688 |
r = (int) ((i3 >> 48) & 0xFFL); |
689 |
break; |
690 |
case 22: |
691 |
r = (int) ((i3 >> 56) & 0xFFL); |
692 |
break; |
693 |
} |
694 |
return (char) r; |
695 |
} |
696 |
|
697 |
@Override |
698 |
public String toString() { |
699 |
int n = length(); |
700 |
char[] r = new char[n]; |
701 |
for (int i = 0; i < n; i++) { |
702 |
r[i] = charAt(i); |
703 |
} |
704 |
return new String(r); |
705 |
} |
706 |
|
707 |
@Override |
708 |
public boolean equals(Object object) { |
709 |
if (this == object) { |
710 |
return true; |
711 |
} |
712 |
if (object instanceof Fixed_16_23) { |
713 |
Fixed_16_23 otherString = (Fixed_16_23) object; |
714 |
return i1 == otherString.i1 && i2 == otherString.i2 && i3 == otherString.i3; |
715 |
} |
716 |
return false; |
717 |
} |
718 |
|
719 |
@Override |
720 |
public int hashCode() { |
721 |
long res = i1 + 31 * (i2 + 31 * i3); |
722 |
res = (res + (res >> 32)) & 0xFFFFFFFFL; |
723 |
return (int) res; |
724 |
} |
725 |
|
726 |
@Override |
727 |
public CharSequence subSequence(int start, int end) { |
728 |
return CharSequences.create(toString().substring(start, end)); |
729 |
} |
730 |
|
731 |
@Override |
732 |
public int compareTo(CharSequence o) { |
733 |
return Comparator.compare(this, o); |
734 |
} |
735 |
} |
736 |
|
737 |
/** |
738 |
* compact char sequence implementation based on char[] array |
739 |
* size: 8 + 4 + 4 (= 16 bytes) + sizeof ('value') |
740 |
* it is still more effective than String, because string stores length in field |
741 |
* and it costs 20 bytes aligned into 24 |
742 |
*/ |
743 |
private final static class CharBasedSequence implements CompactCharSequence, Comparable<CharSequence> { |
744 |
|
745 |
private final char[] value; |
746 |
private int hash; |
747 |
|
748 |
private CharBasedSequence(char[] v) { |
749 |
value = v; |
750 |
} |
751 |
|
752 |
@Override |
753 |
public int length() { |
754 |
return value.length; |
755 |
} |
756 |
|
757 |
@Override |
758 |
public char charAt(int index) { |
759 |
return value[index]; |
760 |
} |
761 |
|
762 |
@Override |
763 |
public boolean equals(Object object) { |
764 |
if (this == object) { |
765 |
return true; |
766 |
} |
767 |
if (object instanceof CharBasedSequence) { |
768 |
CharBasedSequence otherString = (CharBasedSequence) object; |
769 |
if (hash != 0 && otherString.hash != 0) { |
770 |
if (hash != otherString.hash) { |
771 |
return false; |
772 |
} |
773 |
} |
774 |
return Arrays.equals(value, otherString.value); |
775 |
} |
776 |
return false; |
777 |
} |
778 |
|
779 |
@Override |
780 |
public int hashCode() { |
781 |
int h = hash; |
782 |
if (h == 0) { |
783 |
int n = value.length; |
784 |
for (int i = 0; i < n; i++) { |
785 |
h = 31 * h + value[i]; |
786 |
} |
787 |
hash = h; |
788 |
} |
789 |
return h; |
790 |
} |
791 |
|
792 |
@Override |
793 |
public CharSequence subSequence(int beginIndex, int endIndex) { |
794 |
return CharSequences.create(value, beginIndex, endIndex); |
795 |
} |
796 |
|
797 |
@Override |
798 |
public String toString() { |
799 |
return new String(value); |
800 |
} |
801 |
|
802 |
@Override |
803 |
public int compareTo(CharSequence o) { |
804 |
return Comparator.compare(this, o); |
805 |
} |
806 |
} |
807 |
|
808 |
/** |
809 |
* compact char sequence implementation based on byte[] |
810 |
* size: 8 + 4 + 4 (= 16 bytes) + sizeof ('value') |
811 |
*/ |
812 |
private final static class ByteBasedSequence implements CompactCharSequence, Comparable<CharSequence> { |
813 |
|
814 |
private final byte[] value; |
815 |
private int hash; |
816 |
|
817 |
private ByteBasedSequence(byte[] b) { |
818 |
value = b; |
819 |
} |
820 |
|
821 |
@Override |
822 |
public int length() { |
823 |
return value.length; |
824 |
} |
825 |
|
826 |
@Override |
827 |
public char charAt(int index) { |
828 |
int r = value[index] & 0xFF; |
829 |
return (char) r; |
830 |
} |
831 |
|
832 |
@Override |
833 |
public boolean equals(Object object) { |
834 |
if (this == object) { |
835 |
return true; |
836 |
} |
837 |
if (object instanceof ByteBasedSequence) { |
838 |
ByteBasedSequence otherString = (ByteBasedSequence) object; |
839 |
if (hash != 0 && otherString.hash != 0) { |
840 |
if (hash != otherString.hash) { |
841 |
return false; |
842 |
} |
843 |
} |
844 |
return Arrays.equals(value, otherString.value); |
845 |
} |
846 |
return false; |
847 |
} |
848 |
|
849 |
@Override |
850 |
public int hashCode() { |
851 |
int h = hash; |
852 |
if (h == 0) { |
853 |
int n = value.length; |
854 |
for (int i = 0; i < n; i++) { |
855 |
h = 31 * h + value[i]; |
856 |
} |
857 |
hash = h; |
858 |
} |
859 |
return h; |
860 |
} |
861 |
|
862 |
@Override |
863 |
public CharSequence subSequence(int beginIndex, int endIndex) { |
864 |
return CharSequences.create(toChars(), beginIndex, endIndex); |
865 |
} |
866 |
|
867 |
@Override |
868 |
public String toString() { |
869 |
char[] r = toChars(); |
870 |
return new String(r); |
871 |
} |
872 |
|
873 |
private char[] toChars() { |
874 |
int n = value.length; |
875 |
char[] r = new char[n]; |
876 |
for (int i = 0; i < n; i++) { |
877 |
int c = value[i] & 0xFF; |
878 |
r[i] = (char) c; |
879 |
} |
880 |
return r; |
881 |
} |
882 |
|
883 |
@Override |
884 |
public int compareTo(CharSequence o) { |
885 |
return Comparator.compare(this, o); |
886 |
} |
887 |
} |
888 |
|
889 |
private static final CompactCharSequence EMPTY = new Fixed_0_7(new byte[0], 0); |
890 |
private static final Comparator<CharSequence> Comparator = new CharSequenceComparator(); |
891 |
private static final Comparator<CharSequence> ComparatorIgnoreCase = new CharSequenceComparatorIgnoreCase(); |
892 |
|
893 |
/** |
894 |
* performance tuned comparator to prevent charAt calls when possible |
895 |
*/ |
896 |
private static class CharSequenceComparator implements Comparator<CharSequence> { |
897 |
|
898 |
@Override |
899 |
public int compare(CharSequence o1, CharSequence o2) { |
900 |
if (o1 instanceof ByteBasedSequence) { |
901 |
return compareByteBasedWithOther((ByteBasedSequence)o1, o2); |
902 |
} else if (o2 instanceof ByteBasedSequence) { |
903 |
return -compareByteBasedWithOther((ByteBasedSequence) o2, o1); |
904 |
} else if (o1 instanceof CharBasedSequence) { |
905 |
return compareCharBasedWithOther((CharBasedSequence)o1, o2); |
906 |
} else if (o2 instanceof CharBasedSequence) { |
907 |
return -compareCharBasedWithOther((CharBasedSequence)o2, o1); |
908 |
} |
909 |
int len1 = o1.length(); |
910 |
int len2 = o2.length(); |
911 |
int n = Math.min(len1, len2); |
912 |
int k = 0; |
913 |
while (k < n) { |
914 |
char c1 = o1.charAt(k); |
915 |
char c2 = o2.charAt(k); |
916 |
if (c1 != c2) { |
917 |
return c1 - c2; |
918 |
} |
919 |
k++; |
920 |
} |
921 |
return len1 - len2; |
922 |
} |
923 |
|
924 |
//<editor-fold defaultstate="collapsed" desc="Private methods"> |
925 |
private static int compareByteBased(ByteBasedSequence bbs1, ByteBasedSequence bbs2) { |
926 |
int len1 = bbs1.value.length; |
927 |
int len2 = bbs2.value.length; |
928 |
int n = Math.min(len1, len2); |
929 |
int k = 0; |
930 |
while (k < n) { |
931 |
if (bbs1.value[k] != bbs2.value[k]) { |
932 |
return (bbs1.value[k] & 0xFF) - (bbs2.value[k] & 0xFF); |
933 |
} |
934 |
k++; |
935 |
} |
936 |
return len1 - len2; |
937 |
} |
938 |
|
939 |
private static int compareCharBased(CharBasedSequence cbs1, CharBasedSequence cbs2) { |
940 |
int len1 = cbs1.value.length; |
941 |
int len2 = cbs2.value.length; |
942 |
int n = Math.min(len1, len2); |
943 |
int k = 0; |
944 |
while (k < n) { |
945 |
if (cbs1.value[k] != cbs2.value[k]) { |
946 |
return cbs1.value[k] - cbs2.value[k]; |
947 |
} |
948 |
k++; |
949 |
} |
950 |
return len1 - len2; |
951 |
} |
952 |
|
953 |
private static int compareByteBasedWithCharBased(ByteBasedSequence bbs1, CharBasedSequence cbs2) { |
954 |
int len1 = bbs1.value.length; |
955 |
int len2 = cbs2.value.length; |
956 |
int n = Math.min(len1, len2); |
957 |
int k = 0; |
958 |
while (k < n) { |
959 |
int c1 = bbs1.value[k] & 0xFF; |
960 |
int c2 = cbs2.value[k]; |
961 |
if (c1 != c2) { |
962 |
return c1 - c2; |
963 |
} |
964 |
k++; |
965 |
} |
966 |
return len1 - len2; |
967 |
} |
968 |
|
969 |
private static int compareByteBasedWithOther(ByteBasedSequence bbs1, CharSequence o2) { |
970 |
if (o2 instanceof ByteBasedSequence) { |
971 |
return compareByteBased(bbs1, (ByteBasedSequence) o2); |
972 |
} else if (o2 instanceof CharBasedSequence) { |
973 |
return compareByteBasedWithCharBased(bbs1, (CharBasedSequence) o2); |
974 |
} |
975 |
int len1 = bbs1.value.length; |
976 |
int len2 = o2.length(); |
977 |
int n = Math.min(len1, len2); |
978 |
int k = 0; |
979 |
int c1, c2; |
980 |
while (k < n) { |
981 |
c1 = bbs1.value[k] & 0xFF; |
982 |
c2 = o2.charAt(k); |
983 |
if (c1 != c2) { |
984 |
return c1 - c2; |
985 |
} |
986 |
k++; |
987 |
} |
988 |
return len1 - len2; |
989 |
} |
990 |
|
991 |
private static int compareCharBasedWithOther(CharBasedSequence cbs1, CharSequence o2) { |
992 |
if (o2 instanceof CharBasedSequence) { |
993 |
return compareCharBased(cbs1, (CharBasedSequence) o2); |
994 |
} else if (o2 instanceof ByteBasedSequence) { |
995 |
return -compareByteBasedWithCharBased((ByteBasedSequence) o2, cbs1); |
996 |
} |
997 |
int len1 = cbs1.value.length; |
998 |
int len2 = o2.length(); |
999 |
int n = Math.min(len1, len2); |
1000 |
int k = 0; |
1001 |
int c1, c2; |
1002 |
while (k < n) { |
1003 |
c1 = cbs1.value[k]; |
1004 |
c2 = o2.charAt(k); |
1005 |
if (c1 != c2) { |
1006 |
return c1 - c2; |
1007 |
} |
1008 |
k++; |
1009 |
} |
1010 |
return len1 - len2; |
1011 |
} |
1012 |
//</editor-fold> |
1013 |
} |
1014 |
|
1015 |
private static class CharSequenceComparatorIgnoreCase implements Comparator<CharSequence> { |
1016 |
|
1017 |
@Override |
1018 |
public int compare(CharSequence o1, CharSequence o2) { |
1019 |
int n1 = o1.length(); |
1020 |
int n2 = o2.length(); |
1021 |
for (int i1 = 0, i2 = 0; i1 < n1 && i2 < n2; i1++, i2++) { |
1022 |
char c1 = o1.charAt(i1); |
1023 |
char c2 = o2.charAt(i2); |
1024 |
if (c1 != c2) { |
1025 |
c1 = Character.toUpperCase(c1); |
1026 |
c2 = Character.toUpperCase(c2); |
1027 |
if (c1 != c2) { |
1028 |
c1 = Character.toLowerCase(c1); |
1029 |
c2 = Character.toLowerCase(c2); |
1030 |
if (c1 != c2) { |
1031 |
return c1 - c2; |
1032 |
} |
1033 |
} |
1034 |
} |
1035 |
} |
1036 |
return n1 - n2; |
1037 |
} |
1038 |
} |
1039 |
|
1040 |
/** |
1041 |
* marker interface for compact char sequence implementations |
1042 |
*/ |
1043 |
private interface CompactCharSequence extends CharSequence { |
1044 |
} |
1045 |
|
1046 |
/** |
1047 |
* private constructor for utilities class |
1048 |
*/ |
1049 |
private CharSequences() { |
1050 |
} |
1051 |
|
1052 |
//</editor-fold> |
1053 |
} |