1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ****************************************************************************** 5 * 6 * Copyright (C) 2009-2015, International Business Machines 7 * Corporation and others. All Rights Reserved. 8 * 9 ****************************************************************************** 10 */ 11 12 package com.ibm.icu.impl; 13 14 import com.ibm.icu.text.UnicodeSet.SpanCondition; 15 import com.ibm.icu.util.OutputInt; 16 17 /** 18 * Helper class for frozen UnicodeSets, implements contains() and span() optimized for BMP code points. 19 * 20 * Latin-1: Look up bytes. 21 * 2-byte characters: Bits organized vertically. 22 * 3-byte characters: Use zero/one/mixed data per 64-block in U+0000..U+FFFF, with mixed for illegal ranges. 23 * Supplementary characters: Binary search over 24 * the supplementary part of the parent set's inversion list. 25 */ 26 public final class BMPSet { 27 public static int U16_SURROGATE_OFFSET = ((0xd800 << 10) + 0xdc00 - 0x10000); 28 29 /** 30 * One boolean ('true' or 'false') per Latin-1 character. 31 */ 32 private boolean[] latin1Contains; 33 34 /** 35 * One bit per code point from U+0000..U+07FF. The bits are organized vertically; consecutive code points 36 * correspond to the same bit positions in consecutive table words. With code point parts lead=c{10..6} 37 * trail=c{5..0} it is set.contains(c)==(table7FF[trail] bit lead) 38 * 39 * Bits for 0..FF are unused (0). 40 */ 41 private int[] table7FF; 42 43 /** 44 * One bit per 64 BMP code points. The bits are organized vertically; consecutive 64-code point blocks 45 * correspond to the same bit position in consecutive table words. With code point parts lead=c{15..12} 46 * t1=c{11..6} test bits (lead+16) and lead in bmpBlockBits[t1]. If the upper bit is 0, then the lower bit 47 * indicates if contains(c) for all code points in the 64-block. If the upper bit is 1, then the block is mixed 48 * and set.contains(c) must be called. 49 * 50 * Bits for 0..7FF are unused (0). 51 */ 52 private int[] bmpBlockBits; 53 54 /** 55 * Inversion list indexes for restricted binary searches in findCodePoint(), from findCodePoint(U+0800, U+1000, 56 * U+2000, .., U+F000, U+10000). U+0800 is the first 3-byte-UTF-8 code point. Code points below U+0800 are 57 * always looked up in the bit tables. The last pair of indexes is for finding supplementary code points. 58 */ 59 private int[] list4kStarts; 60 61 /** 62 * The inversion list of the parent set, for the slower contains() implementation for mixed BMP blocks and for 63 * supplementary code points. The list is terminated with list[listLength-1]=0x110000. 64 */ 65 private final int[] list; 66 private final int listLength; // length used; list may be longer to minimize reallocs 67 BMPSet(final int[] parentList, int parentListLength)68 public BMPSet(final int[] parentList, int parentListLength) { 69 list = parentList; 70 listLength = parentListLength; 71 latin1Contains = new boolean[0x100]; 72 table7FF = new int[64]; 73 bmpBlockBits = new int[64]; 74 list4kStarts = new int[18]; 75 76 /* 77 * Set the list indexes for binary searches for U+0800, U+1000, U+2000, .., U+F000, U+10000. U+0800 is the 78 * first 3-byte-UTF-8 code point. Lower code points are looked up in the bit tables. The last pair of 79 * indexes is for finding supplementary code points. 80 */ 81 list4kStarts[0] = findCodePoint(0x800, 0, listLength - 1); 82 int i; 83 for (i = 1; i <= 0x10; ++i) { 84 list4kStarts[i] = findCodePoint(i << 12, list4kStarts[i - 1], listLength - 1); 85 } 86 list4kStarts[0x11] = listLength - 1; 87 88 initBits(); 89 } 90 BMPSet(final BMPSet otherBMPSet, final int[] newParentList, int newParentListLength)91 public BMPSet(final BMPSet otherBMPSet, final int[] newParentList, int newParentListLength) { 92 list = newParentList; 93 listLength = newParentListLength; 94 latin1Contains = otherBMPSet.latin1Contains.clone(); 95 table7FF = otherBMPSet.table7FF.clone(); 96 bmpBlockBits = otherBMPSet.bmpBlockBits.clone(); 97 list4kStarts = otherBMPSet.list4kStarts.clone(); 98 } 99 contains(int c)100 public boolean contains(int c) { 101 if (c <= 0xff) { 102 return (latin1Contains[c]); 103 } else if (c <= 0x7ff) { 104 return ((table7FF[c & 0x3f] & (1 << (c >> 6))) != 0); 105 } else if (c < 0xd800 || (c >= 0xe000 && c <= 0xffff)) { 106 int lead = c >> 12; 107 int twoBits = (bmpBlockBits[(c >> 6) & 0x3f] >> lead) & 0x10001; 108 if (twoBits <= 1) { 109 // All 64 code points with the same bits 15..6 110 // are either in the set or not. 111 return (0 != twoBits); 112 } else { 113 // Look up the code point in its 4k block of code points. 114 return containsSlow(c, list4kStarts[lead], list4kStarts[lead + 1]); 115 } 116 } else if (c <= 0x10ffff) { 117 // surrogate or supplementary code point 118 return containsSlow(c, list4kStarts[0xd], list4kStarts[0x11]); 119 } else { 120 // Out-of-range code points get false, consistent with long-standing 121 // behavior of UnicodeSet.contains(c). 122 return false; 123 } 124 } 125 126 /** 127 * Span the initial substring for which each character c has spanCondition==contains(c). It must be 128 * spanCondition==0 or 1. 129 * 130 * @param start The start index 131 * @param outCount If not null: Receives the number of code points in the span. 132 * @return the limit (exclusive end) of the span 133 * 134 * NOTE: to reduce the overhead of function call to contains(c), it is manually inlined here. Check for 135 * sufficient length for trail unit for each surrogate pair. Handle single surrogates as surrogate code points 136 * as usual in ICU. 137 */ span(CharSequence s, int start, SpanCondition spanCondition, OutputInt outCount)138 public final int span(CharSequence s, int start, SpanCondition spanCondition, 139 OutputInt outCount) { 140 char c, c2; 141 int i = start; 142 int limit = s.length(); 143 int numSupplementary = 0; 144 if (SpanCondition.NOT_CONTAINED != spanCondition) { 145 // span 146 while (i < limit) { 147 c = s.charAt(i); 148 if (c <= 0xff) { 149 if (!latin1Contains[c]) { 150 break; 151 } 152 } else if (c <= 0x7ff) { 153 if ((table7FF[c & 0x3f] & (1 << (c >> 6))) == 0) { 154 break; 155 } 156 } else if (c < 0xd800 || 157 c >= 0xdc00 || (i + 1) == limit || (c2 = s.charAt(i + 1)) < 0xdc00 || c2 >= 0xe000) { 158 int lead = c >> 12; 159 int twoBits = (bmpBlockBits[(c >> 6) & 0x3f] >> lead) & 0x10001; 160 if (twoBits <= 1) { 161 // All 64 code points with the same bits 15..6 162 // are either in the set or not. 163 if (twoBits == 0) { 164 break; 165 } 166 } else { 167 // Look up the code point in its 4k block of code points. 168 if (!containsSlow(c, list4kStarts[lead], list4kStarts[lead + 1])) { 169 break; 170 } 171 } 172 } else { 173 // surrogate pair 174 int supplementary = Character.toCodePoint(c, c2); 175 if (!containsSlow(supplementary, list4kStarts[0x10], list4kStarts[0x11])) { 176 break; 177 } 178 ++numSupplementary; 179 ++i; 180 } 181 ++i; 182 } 183 } else { 184 // span not 185 while (i < limit) { 186 c = s.charAt(i); 187 if (c <= 0xff) { 188 if (latin1Contains[c]) { 189 break; 190 } 191 } else if (c <= 0x7ff) { 192 if ((table7FF[c & 0x3f] & (1 << (c >> 6))) != 0) { 193 break; 194 } 195 } else if (c < 0xd800 || 196 c >= 0xdc00 || (i + 1) == limit || (c2 = s.charAt(i + 1)) < 0xdc00 || c2 >= 0xe000) { 197 int lead = c >> 12; 198 int twoBits = (bmpBlockBits[(c >> 6) & 0x3f] >> lead) & 0x10001; 199 if (twoBits <= 1) { 200 // All 64 code points with the same bits 15..6 201 // are either in the set or not. 202 if (twoBits != 0) { 203 break; 204 } 205 } else { 206 // Look up the code point in its 4k block of code points. 207 if (containsSlow(c, list4kStarts[lead], list4kStarts[lead + 1])) { 208 break; 209 } 210 } 211 } else { 212 // surrogate pair 213 int supplementary = Character.toCodePoint(c, c2); 214 if (containsSlow(supplementary, list4kStarts[0x10], list4kStarts[0x11])) { 215 break; 216 } 217 ++numSupplementary; 218 ++i; 219 } 220 ++i; 221 } 222 } 223 if (outCount != null) { 224 int spanLength = i - start; 225 outCount.value = spanLength - numSupplementary; // number of code points 226 } 227 return i; 228 } 229 230 /** 231 * Symmetrical with span(). 232 * Span the trailing substring for which each character c has spanCondition==contains(c). It must be s.length >= 233 * limit and spanCondition==0 or 1. 234 * 235 * @return The string index which starts the span (i.e. inclusive). 236 */ spanBack(CharSequence s, int limit, SpanCondition spanCondition)237 public final int spanBack(CharSequence s, int limit, SpanCondition spanCondition) { 238 char c, c2; 239 240 if (SpanCondition.NOT_CONTAINED != spanCondition) { 241 // span 242 for (;;) { 243 c = s.charAt(--limit); 244 if (c <= 0xff) { 245 if (!latin1Contains[c]) { 246 break; 247 } 248 } else if (c <= 0x7ff) { 249 if ((table7FF[c & 0x3f] & (1 << (c >> 6))) == 0) { 250 break; 251 } 252 } else if (c < 0xd800 || 253 c < 0xdc00 || 0 == limit || (c2 = s.charAt(limit - 1)) < 0xd800 || c2 >= 0xdc00) { 254 int lead = c >> 12; 255 int twoBits = (bmpBlockBits[(c >> 6) & 0x3f] >> lead) & 0x10001; 256 if (twoBits <= 1) { 257 // All 64 code points with the same bits 15..6 258 // are either in the set or not. 259 if (twoBits == 0) { 260 break; 261 } 262 } else { 263 // Look up the code point in its 4k block of code points. 264 if (!containsSlow(c, list4kStarts[lead], list4kStarts[lead + 1])) { 265 break; 266 } 267 } 268 } else { 269 // surrogate pair 270 int supplementary = Character.toCodePoint(c2, c); 271 if (!containsSlow(supplementary, list4kStarts[0x10], list4kStarts[0x11])) { 272 break; 273 } 274 --limit; 275 } 276 if (0 == limit) { 277 return 0; 278 } 279 } 280 } else { 281 // span not 282 for (;;) { 283 c = s.charAt(--limit); 284 if (c <= 0xff) { 285 if (latin1Contains[c]) { 286 break; 287 } 288 } else if (c <= 0x7ff) { 289 if ((table7FF[c & 0x3f] & (1 << (c >> 6))) != 0) { 290 break; 291 } 292 } else if (c < 0xd800 || 293 c < 0xdc00 || 0 == limit || (c2 = s.charAt(limit - 1)) < 0xd800 || c2 >= 0xdc00) { 294 int lead = c >> 12; 295 int twoBits = (bmpBlockBits[(c >> 6) & 0x3f] >> lead) & 0x10001; 296 if (twoBits <= 1) { 297 // All 64 code points with the same bits 15..6 298 // are either in the set or not. 299 if (twoBits != 0) { 300 break; 301 } 302 } else { 303 // Look up the code point in its 4k block of code points. 304 if (containsSlow(c, list4kStarts[lead], list4kStarts[lead + 1])) { 305 break; 306 } 307 } 308 } else { 309 // surrogate pair 310 int supplementary = Character.toCodePoint(c2, c); 311 if (containsSlow(supplementary, list4kStarts[0x10], list4kStarts[0x11])) { 312 break; 313 } 314 --limit; 315 } 316 if (0 == limit) { 317 return 0; 318 } 319 } 320 } 321 return limit + 1; 322 } 323 324 /** 325 * Set bits in a bit rectangle in "vertical" bit organization. start<limit<=0x800 326 */ set32x64Bits(int[] table, int start, int limit)327 private static void set32x64Bits(int[] table, int start, int limit) { 328 assert (64 == table.length); 329 int lead = start >> 6; // Named for UTF-8 2-byte lead byte with upper 5 bits. 330 int trail = start & 0x3f; // Named for UTF-8 2-byte trail byte with lower 6 bits. 331 332 // Set one bit indicating an all-one block. 333 int bits = 1 << lead; 334 if ((start + 1) == limit) { // Single-character shortcut. 335 table[trail] |= bits; 336 return; 337 } 338 339 int limitLead = limit >> 6; 340 int limitTrail = limit & 0x3f; 341 342 if (lead == limitLead) { 343 // Partial vertical bit column. 344 while (trail < limitTrail) { 345 table[trail++] |= bits; 346 } 347 } else { 348 // Partial vertical bit column, 349 // followed by a bit rectangle, 350 // followed by another partial vertical bit column. 351 if (trail > 0) { 352 do { 353 table[trail++] |= bits; 354 } while (trail < 64); 355 ++lead; 356 } 357 if (lead < limitLead) { 358 bits = ~((1 << lead) - 1); 359 if (limitLead < 0x20) { 360 bits &= (1 << limitLead) - 1; 361 } 362 for (trail = 0; trail < 64; ++trail) { 363 table[trail] |= bits; 364 } 365 } 366 // limit<=0x800. If limit==0x800 then limitLead=32 and limitTrail=0. 367 // In that case, bits=1<<limitLead == 1<<0 == 1 368 // (because Java << uses only the lower 5 bits of the shift operand) 369 // but the bits value is not used because trail<limitTrail is already false. 370 bits = 1 << limitLead; 371 for (trail = 0; trail < limitTrail; ++trail) { 372 table[trail] |= bits; 373 } 374 } 375 } 376 initBits()377 private void initBits() { 378 int start, limit; 379 int listIndex = 0; 380 381 // Set latin1Contains[]. 382 do { 383 start = list[listIndex++]; 384 if (listIndex < listLength) { 385 limit = list[listIndex++]; 386 } else { 387 limit = 0x110000; 388 } 389 if (start >= 0x100) { 390 break; 391 } 392 do { 393 latin1Contains[start++] = true; 394 } while (start < limit && start < 0x100); 395 } while (limit <= 0x100); 396 397 // Set table7FF[]. 398 while (start < 0x800) { 399 set32x64Bits(table7FF, start, limit <= 0x800 ? limit : 0x800); 400 if (limit > 0x800) { 401 start = 0x800; 402 break; 403 } 404 405 start = list[listIndex++]; 406 if (listIndex < listLength) { 407 limit = list[listIndex++]; 408 } else { 409 limit = 0x110000; 410 } 411 } 412 413 // Set bmpBlockBits[]. 414 int minStart = 0x800; 415 while (start < 0x10000) { 416 if (limit > 0x10000) { 417 limit = 0x10000; 418 } 419 420 if (start < minStart) { 421 start = minStart; 422 } 423 if (start < limit) { // Else: Another range entirely in a known mixed-value block. 424 if (0 != (start & 0x3f)) { 425 // Mixed-value block of 64 code points. 426 start >>= 6; 427 bmpBlockBits[start & 0x3f] |= 0x10001 << (start >> 6); 428 start = (start + 1) << 6; // Round up to the next block boundary. 429 minStart = start; // Ignore further ranges in this block. 430 } 431 if (start < limit) { 432 if (start < (limit & ~0x3f)) { 433 // Multiple all-ones blocks of 64 code points each. 434 set32x64Bits(bmpBlockBits, start >> 6, limit >> 6); 435 } 436 437 if (0 != (limit & 0x3f)) { 438 // Mixed-value block of 64 code points. 439 limit >>= 6; 440 bmpBlockBits[limit & 0x3f] |= 0x10001 << (limit >> 6); 441 limit = (limit + 1) << 6; // Round up to the next block boundary. 442 minStart = limit; // Ignore further ranges in this block. 443 } 444 } 445 } 446 447 if (limit == 0x10000) { 448 break; 449 } 450 451 start = list[listIndex++]; 452 if (listIndex < listLength) { 453 limit = list[listIndex++]; 454 } else { 455 limit = 0x110000; 456 } 457 } 458 } 459 460 461 /** 462 * Same as UnicodeSet.findCodePoint(int c) except that the binary search is restricted for finding code 463 * points in a certain range. 464 * 465 * For restricting the search for finding in the range start..end, pass in lo=findCodePoint(start) and 466 * hi=findCodePoint(end) with 0<=lo<=hi<len. findCodePoint(c) defaults to lo=0 and hi=len-1. 467 * 468 * @param c 469 * a character in a subrange of MIN_VALUE..MAX_VALUE 470 * @param lo 471 * The lowest index to be returned. 472 * @param hi 473 * The highest index to be returned. 474 * @return the smallest integer i in the range lo..hi, inclusive, such that c < list[i] 475 */ findCodePoint(int c, int lo, int hi)476 private int findCodePoint(int c, int lo, int hi) { 477 /* Examples: 478 findCodePoint(c) 479 set list[] c=0 1 3 4 7 8 480 === ============== =========== 481 [] [110000] 0 0 0 0 0 0 482 [\u0000-\u0003] [0, 4, 110000] 1 1 1 2 2 2 483 [\u0004-\u0007] [4, 8, 110000] 0 0 0 1 1 2 484 [:Any:] [0, 110000] 1 1 1 1 1 1 485 */ 486 487 // Return the smallest i such that c < list[i]. Assume 488 // list[len - 1] == HIGH and that c is legal (0..HIGH-1). 489 if (c < list[lo]) 490 return lo; 491 // High runner test. c is often after the last range, so an 492 // initial check for this condition pays off. 493 if (lo >= hi || c >= list[hi - 1]) 494 return hi; 495 // invariant: c >= list[lo] 496 // invariant: c < list[hi] 497 for (;;) { 498 int i = (lo + hi) >>> 1; 499 if (i == lo) { 500 break; // Found! 501 } else if (c < list[i]) { 502 hi = i; 503 } else { 504 lo = i; 505 } 506 } 507 return hi; 508 } 509 containsSlow(int c, int lo, int hi)510 private final boolean containsSlow(int c, int lo, int hi) { 511 return (0 != (findCodePoint(c, lo, hi) & 1)); 512 } 513 } 514