1 /* 2 * Copyright © 1998-2004 David Turner and Werner Lemberg 3 * Copyright © 2004,2007,2009,2010 Red Hat, Inc. 4 * Copyright © 2011,2012 Google, Inc. 5 * 6 * This is part of HarfBuzz, a text shaping library. 7 * 8 * Permission is hereby granted, without written agreement and without 9 * license or royalty fees, to use, copy, modify, and distribute this 10 * software and its documentation for any purpose, provided that the 11 * above copyright notice and the following two paragraphs appear in 12 * all copies of this software. 13 * 14 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 15 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 16 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 17 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 18 * DAMAGE. 19 * 20 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 21 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 22 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 23 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 24 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 25 * 26 * Red Hat Author(s): Owen Taylor, Behdad Esfahbod 27 * Google Author(s): Behdad Esfahbod 28 */ 29 30 #include "hb-buffer-private.hh" 31 #include "hb-utf-private.hh" 32 33 34 #ifndef HB_DEBUG_BUFFER 35 #define HB_DEBUG_BUFFER (HB_DEBUG+0) 36 #endif 37 38 39 /** 40 * Since: 0.9.7 41 **/ 42 hb_bool_t 43 hb_segment_properties_equal (const hb_segment_properties_t *a, 44 const hb_segment_properties_t *b) 45 { 46 return a->direction == b->direction && 47 a->script == b->script && 48 a->language == b->language && 49 a->reserved1 == b->reserved1 && 50 a->reserved2 == b->reserved2; 51 52 } 53 54 /** 55 * Since: 0.9.7 56 **/ 57 unsigned int 58 hb_segment_properties_hash (const hb_segment_properties_t *p) 59 { 60 return (unsigned int) p->direction ^ 61 (unsigned int) p->script ^ 62 (intptr_t) (p->language); 63 } 64 65 66 67 /* Here is how the buffer works internally: 68 * 69 * There are two info pointers: info and out_info. They always have 70 * the same allocated size, but different lengths. 71 * 72 * As an optimization, both info and out_info may point to the 73 * same piece of memory, which is owned by info. This remains the 74 * case as long as out_len doesn't exceed i at any time. 75 * In that case, swap_buffers() is no-op and the glyph operations operate 76 * mostly in-place. 77 * 78 * As soon as out_info gets longer than info, out_info is moved over 79 * to an alternate buffer (which we reuse the pos buffer for!), and its 80 * current contents (out_len entries) are copied to the new place. 81 * This should all remain transparent to the user. swap_buffers() then 82 * switches info and out_info. 83 */ 84 85 86 87 /* Internal API */ 88 89 bool 90 hb_buffer_t::enlarge (unsigned int size) 91 { 92 if (unlikely (in_error)) 93 return false; 94 95 unsigned int new_allocated = allocated; 96 hb_glyph_position_t *new_pos = NULL; 97 hb_glyph_info_t *new_info = NULL; 98 bool separate_out = out_info != info; 99 100 if (unlikely (_hb_unsigned_int_mul_overflows (size, sizeof (info[0])))) 101 goto done; 102 103 while (size >= new_allocated) 104 new_allocated += (new_allocated >> 1) + 32; 105 106 ASSERT_STATIC (sizeof (info[0]) == sizeof (pos[0])); 107 if (unlikely (_hb_unsigned_int_mul_overflows (new_allocated, sizeof (info[0])))) 108 goto done; 109 110 new_pos = (hb_glyph_position_t *) realloc (pos, new_allocated * sizeof (pos[0])); 111 new_info = (hb_glyph_info_t *) realloc (info, new_allocated * sizeof (info[0])); 112 113 done: 114 if (unlikely (!new_pos || !new_info)) 115 in_error = true; 116 117 if (likely (new_pos)) 118 pos = new_pos; 119 120 if (likely (new_info)) 121 info = new_info; 122 123 out_info = separate_out ? (hb_glyph_info_t *) pos : info; 124 if (likely (!in_error)) 125 allocated = new_allocated; 126 127 return likely (!in_error); 128 } 129 130 bool 131 hb_buffer_t::make_room_for (unsigned int num_in, 132 unsigned int num_out) 133 { 134 if (unlikely (!ensure (out_len + num_out))) return false; 135 136 if (out_info == info && 137 out_len + num_out > idx + num_in) 138 { 139 assert (have_output); 140 141 out_info = (hb_glyph_info_t *) pos; 142 memcpy (out_info, info, out_len * sizeof (out_info[0])); 143 } 144 145 return true; 146 } 147 148 bool 149 hb_buffer_t::shift_forward (unsigned int count) 150 { 151 assert (have_output); 152 if (unlikely (!ensure (len + count))) return false; 153 154 memmove (info + idx + count, info + idx, (len - idx) * sizeof (info[0])); 155 len += count; 156 idx += count; 157 158 return true; 159 } 160 161 hb_buffer_t::scratch_buffer_t * 162 hb_buffer_t::get_scratch_buffer (unsigned int *size) 163 { 164 have_output = false; 165 have_positions = false; 166 167 out_len = 0; 168 out_info = info; 169 170 assert ((uintptr_t) pos % sizeof (scratch_buffer_t) == 0); 171 *size = allocated * sizeof (pos[0]) / sizeof (scratch_buffer_t); 172 return (scratch_buffer_t *) (void *) pos; 173 } 174 175 176 177 /* HarfBuzz-Internal API */ 178 179 void 180 hb_buffer_t::reset (void) 181 { 182 if (unlikely (hb_object_is_inert (this))) 183 return; 184 185 hb_unicode_funcs_destroy (unicode); 186 unicode = hb_unicode_funcs_get_default (); 187 flags = HB_BUFFER_FLAG_DEFAULT; 188 replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT; 189 190 clear (); 191 } 192 193 void 194 hb_buffer_t::clear (void) 195 { 196 if (unlikely (hb_object_is_inert (this))) 197 return; 198 199 hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT; 200 props = default_props; 201 202 content_type = HB_BUFFER_CONTENT_TYPE_INVALID; 203 in_error = false; 204 have_output = false; 205 have_positions = false; 206 207 idx = 0; 208 len = 0; 209 out_len = 0; 210 out_info = info; 211 212 serial = 0; 213 memset (allocated_var_bytes, 0, sizeof allocated_var_bytes); 214 memset (allocated_var_owner, 0, sizeof allocated_var_owner); 215 216 memset (context, 0, sizeof context); 217 memset (context_len, 0, sizeof context_len); 218 } 219 220 void 221 hb_buffer_t::add (hb_codepoint_t codepoint, 222 unsigned int cluster) 223 { 224 hb_glyph_info_t *glyph; 225 226 if (unlikely (!ensure (len + 1))) return; 227 228 glyph = &info[len]; 229 230 memset (glyph, 0, sizeof (*glyph)); 231 glyph->codepoint = codepoint; 232 glyph->mask = 1; 233 glyph->cluster = cluster; 234 235 len++; 236 } 237 238 void 239 hb_buffer_t::add_info (const hb_glyph_info_t &glyph_info) 240 { 241 if (unlikely (!ensure (len + 1))) return; 242 243 info[len] = glyph_info; 244 245 len++; 246 } 247 248 249 void 250 hb_buffer_t::remove_output (void) 251 { 252 if (unlikely (hb_object_is_inert (this))) 253 return; 254 255 have_output = false; 256 have_positions = false; 257 258 out_len = 0; 259 out_info = info; 260 } 261 262 void 263 hb_buffer_t::clear_output (void) 264 { 265 if (unlikely (hb_object_is_inert (this))) 266 return; 267 268 have_output = true; 269 have_positions = false; 270 271 out_len = 0; 272 out_info = info; 273 } 274 275 void 276 hb_buffer_t::clear_positions (void) 277 { 278 if (unlikely (hb_object_is_inert (this))) 279 return; 280 281 have_output = false; 282 have_positions = true; 283 284 out_len = 0; 285 out_info = info; 286 287 memset (pos, 0, sizeof (pos[0]) * len); 288 } 289 290 void 291 hb_buffer_t::swap_buffers (void) 292 { 293 if (unlikely (in_error)) return; 294 295 assert (have_output); 296 have_output = false; 297 298 if (out_info != info) 299 { 300 hb_glyph_info_t *tmp_string; 301 tmp_string = info; 302 info = out_info; 303 out_info = tmp_string; 304 pos = (hb_glyph_position_t *) out_info; 305 } 306 307 unsigned int tmp; 308 tmp = len; 309 len = out_len; 310 out_len = tmp; 311 312 idx = 0; 313 } 314 315 316 void 317 hb_buffer_t::replace_glyphs (unsigned int num_in, 318 unsigned int num_out, 319 const uint32_t *glyph_data) 320 { 321 if (unlikely (!make_room_for (num_in, num_out))) return; 322 323 merge_clusters (idx, idx + num_in); 324 325 hb_glyph_info_t orig_info = info[idx]; 326 hb_glyph_info_t *pinfo = &out_info[out_len]; 327 for (unsigned int i = 0; i < num_out; i++) 328 { 329 *pinfo = orig_info; 330 pinfo->codepoint = glyph_data[i]; 331 pinfo++; 332 } 333 334 idx += num_in; 335 out_len += num_out; 336 } 337 338 void 339 hb_buffer_t::output_glyph (hb_codepoint_t glyph_index) 340 { 341 if (unlikely (!make_room_for (0, 1))) return; 342 343 out_info[out_len] = info[idx]; 344 out_info[out_len].codepoint = glyph_index; 345 346 out_len++; 347 } 348 349 void 350 hb_buffer_t::output_info (const hb_glyph_info_t &glyph_info) 351 { 352 if (unlikely (!make_room_for (0, 1))) return; 353 354 out_info[out_len] = glyph_info; 355 356 out_len++; 357 } 358 359 void 360 hb_buffer_t::copy_glyph (void) 361 { 362 if (unlikely (!make_room_for (0, 1))) return; 363 364 out_info[out_len] = info[idx]; 365 366 out_len++; 367 } 368 369 bool 370 hb_buffer_t::move_to (unsigned int i) 371 { 372 if (!have_output) 373 { 374 assert (i <= len); 375 idx = i; 376 return true; 377 } 378 379 assert (i <= out_len + (len - idx)); 380 381 if (out_len < i) 382 { 383 unsigned int count = i - out_len; 384 if (unlikely (!make_room_for (count, count))) return false; 385 386 memmove (out_info + out_len, info + idx, count * sizeof (out_info[0])); 387 idx += count; 388 out_len += count; 389 } 390 else if (out_len > i) 391 { 392 /* Tricky part: rewinding... */ 393 unsigned int count = out_len - i; 394 395 if (unlikely (idx < count && !shift_forward (count + 32))) return false; 396 397 assert (idx >= count); 398 399 idx -= count; 400 out_len -= count; 401 memmove (info + idx, out_info + out_len, count * sizeof (out_info[0])); 402 } 403 404 return true; 405 } 406 407 void 408 hb_buffer_t::replace_glyph (hb_codepoint_t glyph_index) 409 { 410 if (unlikely (out_info != info || out_len != idx)) { 411 if (unlikely (!make_room_for (1, 1))) return; 412 out_info[out_len] = info[idx]; 413 } 414 out_info[out_len].codepoint = glyph_index; 415 416 idx++; 417 out_len++; 418 } 419 420 421 void 422 hb_buffer_t::set_masks (hb_mask_t value, 423 hb_mask_t mask, 424 unsigned int cluster_start, 425 unsigned int cluster_end) 426 { 427 hb_mask_t not_mask = ~mask; 428 value &= mask; 429 430 if (!mask) 431 return; 432 433 if (cluster_start == 0 && cluster_end == (unsigned int)-1) { 434 unsigned int count = len; 435 for (unsigned int i = 0; i < count; i++) 436 info[i].mask = (info[i].mask & not_mask) | value; 437 return; 438 } 439 440 unsigned int count = len; 441 for (unsigned int i = 0; i < count; i++) 442 if (cluster_start <= info[i].cluster && info[i].cluster < cluster_end) 443 info[i].mask = (info[i].mask & not_mask) | value; 444 } 445 446 void 447 hb_buffer_t::reverse_range (unsigned int start, 448 unsigned int end) 449 { 450 unsigned int i, j; 451 452 if (end - start < 2) 453 return; 454 455 for (i = start, j = end - 1; i < j; i++, j--) { 456 hb_glyph_info_t t; 457 458 t = info[i]; 459 info[i] = info[j]; 460 info[j] = t; 461 } 462 463 if (have_positions) { 464 for (i = start, j = end - 1; i < j; i++, j--) { 465 hb_glyph_position_t t; 466 467 t = pos[i]; 468 pos[i] = pos[j]; 469 pos[j] = t; 470 } 471 } 472 } 473 474 void 475 hb_buffer_t::reverse (void) 476 { 477 if (unlikely (!len)) 478 return; 479 480 reverse_range (0, len); 481 } 482 483 void 484 hb_buffer_t::reverse_clusters (void) 485 { 486 unsigned int i, start, count, last_cluster; 487 488 if (unlikely (!len)) 489 return; 490 491 reverse (); 492 493 count = len; 494 start = 0; 495 last_cluster = info[0].cluster; 496 for (i = 1; i < count; i++) { 497 if (last_cluster != info[i].cluster) { 498 reverse_range (start, i); 499 start = i; 500 last_cluster = info[i].cluster; 501 } 502 } 503 reverse_range (start, i); 504 } 505 506 void 507 hb_buffer_t::merge_clusters_impl (unsigned int start, 508 unsigned int end) 509 { 510 if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) 511 return; 512 513 unsigned int cluster = info[start].cluster; 514 515 for (unsigned int i = start + 1; i < end; i++) 516 cluster = MIN (cluster, info[i].cluster); 517 518 /* Extend end */ 519 while (end < len && info[end - 1].cluster == info[end].cluster) 520 end++; 521 522 /* Extend start */ 523 while (idx < start && info[start - 1].cluster == info[start].cluster) 524 start--; 525 526 /* If we hit the start of buffer, continue in out-buffer. */ 527 if (idx == start) 528 for (unsigned int i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--) 529 out_info[i - 1].cluster = cluster; 530 531 for (unsigned int i = start; i < end; i++) 532 info[i].cluster = cluster; 533 } 534 void 535 hb_buffer_t::merge_out_clusters (unsigned int start, 536 unsigned int end) 537 { 538 if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) 539 return; 540 541 if (unlikely (end - start < 2)) 542 return; 543 544 unsigned int cluster = out_info[start].cluster; 545 546 for (unsigned int i = start + 1; i < end; i++) 547 cluster = MIN (cluster, out_info[i].cluster); 548 549 /* Extend start */ 550 while (start && out_info[start - 1].cluster == out_info[start].cluster) 551 start--; 552 553 /* Extend end */ 554 while (end < out_len && out_info[end - 1].cluster == out_info[end].cluster) 555 end++; 556 557 /* If we hit the end of out-buffer, continue in buffer. */ 558 if (end == out_len) 559 for (unsigned int i = idx; i < len && info[i].cluster == out_info[end - 1].cluster; i++) 560 info[i].cluster = cluster; 561 562 for (unsigned int i = start; i < end; i++) 563 out_info[i].cluster = cluster; 564 } 565 void 566 hb_buffer_t::delete_glyph () 567 { 568 unsigned int cluster = info[idx].cluster; 569 if (idx + 1 < len && cluster == info[idx + 1].cluster) 570 { 571 /* Cluster survives; do nothing. */ 572 goto done; 573 } 574 575 if (out_len) 576 { 577 /* Merge cluster backward. */ 578 if (cluster < out_info[out_len - 1].cluster) 579 { 580 unsigned int old_cluster = out_info[out_len - 1].cluster; 581 for (unsigned i = out_len; i && out_info[i - 1].cluster == old_cluster; i--) 582 out_info[i - 1].cluster = cluster; 583 } 584 goto done; 585 } 586 587 if (idx + 1 < len) 588 { 589 /* Merge cluster forward. */ 590 merge_clusters (idx, idx + 2); 591 goto done; 592 } 593 594 done: 595 skip_glyph (); 596 } 597 598 void 599 hb_buffer_t::guess_segment_properties (void) 600 { 601 assert (content_type == HB_BUFFER_CONTENT_TYPE_UNICODE || 602 (!len && content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); 603 604 /* If script is set to INVALID, guess from buffer contents */ 605 if (props.script == HB_SCRIPT_INVALID) { 606 for (unsigned int i = 0; i < len; i++) { 607 hb_script_t script = unicode->script (info[i].codepoint); 608 if (likely (script != HB_SCRIPT_COMMON && 609 script != HB_SCRIPT_INHERITED && 610 script != HB_SCRIPT_UNKNOWN)) { 611 props.script = script; 612 break; 613 } 614 } 615 } 616 617 /* If direction is set to INVALID, guess from script */ 618 if (props.direction == HB_DIRECTION_INVALID) { 619 props.direction = hb_script_get_horizontal_direction (props.script); 620 } 621 622 /* If language is not set, use default language from locale */ 623 if (props.language == HB_LANGUAGE_INVALID) { 624 /* TODO get_default_for_script? using $LANGUAGE */ 625 props.language = hb_language_get_default (); 626 } 627 } 628 629 630 static inline void 631 dump_var_allocation (const hb_buffer_t *buffer) 632 { 633 char buf[80]; 634 for (unsigned int i = 0; i < 8; i++) 635 buf[i] = '0' + buffer->allocated_var_bytes[7 - i]; 636 buf[8] = '\0'; 637 DEBUG_MSG (BUFFER, buffer, 638 "Current var allocation: %s", 639 buf); 640 } 641 642 void hb_buffer_t::allocate_var (unsigned int byte_i, unsigned int count, const char *owner) 643 { 644 assert (byte_i < 8 && byte_i + count <= 8); 645 646 if (DEBUG_ENABLED (BUFFER)) 647 dump_var_allocation (this); 648 DEBUG_MSG (BUFFER, this, 649 "Allocating var bytes %d..%d for %s", 650 byte_i, byte_i + count - 1, owner); 651 652 for (unsigned int i = byte_i; i < byte_i + count; i++) { 653 assert (!allocated_var_bytes[i]); 654 allocated_var_bytes[i]++; 655 allocated_var_owner[i] = owner; 656 } 657 } 658 659 void hb_buffer_t::deallocate_var (unsigned int byte_i, unsigned int count, const char *owner) 660 { 661 if (DEBUG_ENABLED (BUFFER)) 662 dump_var_allocation (this); 663 664 DEBUG_MSG (BUFFER, this, 665 "Deallocating var bytes %d..%d for %s", 666 byte_i, byte_i + count - 1, owner); 667 668 assert (byte_i < 8 && byte_i + count <= 8); 669 for (unsigned int i = byte_i; i < byte_i + count; i++) { 670 assert (allocated_var_bytes[i]); 671 assert (0 == strcmp (allocated_var_owner[i], owner)); 672 allocated_var_bytes[i]--; 673 } 674 } 675 676 void hb_buffer_t::assert_var (unsigned int byte_i, unsigned int count, const char *owner) 677 { 678 if (DEBUG_ENABLED (BUFFER)) 679 dump_var_allocation (this); 680 681 DEBUG_MSG (BUFFER, this, 682 "Asserting var bytes %d..%d for %s", 683 byte_i, byte_i + count - 1, owner); 684 685 assert (byte_i < 8 && byte_i + count <= 8); 686 for (unsigned int i = byte_i; i < byte_i + count; i++) { 687 assert (allocated_var_bytes[i]); 688 assert (0 == strcmp (allocated_var_owner[i], owner)); 689 } 690 } 691 692 void hb_buffer_t::deallocate_var_all (void) 693 { 694 memset (allocated_var_bytes, 0, sizeof (allocated_var_bytes)); 695 memset (allocated_var_owner, 0, sizeof (allocated_var_owner)); 696 } 697 698 /* Public API */ 699 700 /** 701 * hb_buffer_create: (Xconstructor) 702 * 703 * 704 * 705 * Return value: (transfer full) 706 * 707 * Since: 0.9.2 708 **/ 709 hb_buffer_t * 710 hb_buffer_create (void) 711 { 712 hb_buffer_t *buffer; 713 714 if (!(buffer = hb_object_create<hb_buffer_t> ())) 715 return hb_buffer_get_empty (); 716 717 buffer->reset (); 718 719 return buffer; 720 } 721 722 /** 723 * hb_buffer_get_empty: 724 * 725 * 726 * 727 * Return value: (transfer full): 728 * 729 * Since: 0.9.2 730 **/ 731 hb_buffer_t * 732 hb_buffer_get_empty (void) 733 { 734 static const hb_buffer_t _hb_buffer_nil = { 735 HB_OBJECT_HEADER_STATIC, 736 737 const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil), 738 HB_BUFFER_FLAG_DEFAULT, 739 HB_BUFFER_CLUSTER_LEVEL_DEFAULT, 740 HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT, 741 742 HB_BUFFER_CONTENT_TYPE_INVALID, 743 HB_SEGMENT_PROPERTIES_DEFAULT, 744 true, /* in_error */ 745 true, /* have_output */ 746 true /* have_positions */ 747 748 /* Zero is good enough for everything else. */ 749 }; 750 751 return const_cast<hb_buffer_t *> (&_hb_buffer_nil); 752 } 753 754 /** 755 * hb_buffer_reference: (skip) 756 * @buffer: a buffer. 757 * 758 * 759 * 760 * Return value: (transfer full): 761 * 762 * Since: 0.9.2 763 **/ 764 hb_buffer_t * 765 hb_buffer_reference (hb_buffer_t *buffer) 766 { 767 return hb_object_reference (buffer); 768 } 769 770 /** 771 * hb_buffer_destroy: (skip) 772 * @buffer: a buffer. 773 * 774 * 775 * 776 * Since: 0.9.2 777 **/ 778 void 779 hb_buffer_destroy (hb_buffer_t *buffer) 780 { 781 if (!hb_object_destroy (buffer)) return; 782 783 hb_unicode_funcs_destroy (buffer->unicode); 784 785 free (buffer->info); 786 free (buffer->pos); 787 788 free (buffer); 789 } 790 791 /** 792 * hb_buffer_set_user_data: (skip) 793 * @buffer: a buffer. 794 * @key: 795 * @data: 796 * @destroy: 797 * @replace: 798 * 799 * 800 * 801 * Return value: 802 * 803 * Since: 0.9.2 804 **/ 805 hb_bool_t 806 hb_buffer_set_user_data (hb_buffer_t *buffer, 807 hb_user_data_key_t *key, 808 void * data, 809 hb_destroy_func_t destroy, 810 hb_bool_t replace) 811 { 812 return hb_object_set_user_data (buffer, key, data, destroy, replace); 813 } 814 815 /** 816 * hb_buffer_get_user_data: (skip) 817 * @buffer: a buffer. 818 * @key: 819 * 820 * 821 * 822 * Return value: 823 * 824 * Since: 0.9.2 825 **/ 826 void * 827 hb_buffer_get_user_data (hb_buffer_t *buffer, 828 hb_user_data_key_t *key) 829 { 830 return hb_object_get_user_data (buffer, key); 831 } 832 833 834 /** 835 * hb_buffer_set_content_type: 836 * @buffer: a buffer. 837 * @content_type: 838 * 839 * 840 * 841 * Since: 0.9.5 842 **/ 843 void 844 hb_buffer_set_content_type (hb_buffer_t *buffer, 845 hb_buffer_content_type_t content_type) 846 { 847 buffer->content_type = content_type; 848 } 849 850 /** 851 * hb_buffer_get_content_type: 852 * @buffer: a buffer. 853 * 854 * 855 * 856 * Return value: 857 * 858 * Since: 0.9.5 859 **/ 860 hb_buffer_content_type_t 861 hb_buffer_get_content_type (hb_buffer_t *buffer) 862 { 863 return buffer->content_type; 864 } 865 866 867 /** 868 * hb_buffer_set_unicode_funcs: 869 * @buffer: a buffer. 870 * @unicode_funcs: 871 * 872 * 873 * 874 * Since: 0.9.2 875 **/ 876 void 877 hb_buffer_set_unicode_funcs (hb_buffer_t *buffer, 878 hb_unicode_funcs_t *unicode_funcs) 879 { 880 if (unlikely (hb_object_is_inert (buffer))) 881 return; 882 883 if (!unicode_funcs) 884 unicode_funcs = hb_unicode_funcs_get_default (); 885 886 887 hb_unicode_funcs_reference (unicode_funcs); 888 hb_unicode_funcs_destroy (buffer->unicode); 889 buffer->unicode = unicode_funcs; 890 } 891 892 /** 893 * hb_buffer_get_unicode_funcs: 894 * @buffer: a buffer. 895 * 896 * 897 * 898 * Return value: 899 * 900 * Since: 0.9.2 901 **/ 902 hb_unicode_funcs_t * 903 hb_buffer_get_unicode_funcs (hb_buffer_t *buffer) 904 { 905 return buffer->unicode; 906 } 907 908 /** 909 * hb_buffer_set_direction: 910 * @buffer: a buffer. 911 * @direction: 912 * 913 * 914 * 915 * Since: 0.9.2 916 **/ 917 void 918 hb_buffer_set_direction (hb_buffer_t *buffer, 919 hb_direction_t direction) 920 921 { 922 if (unlikely (hb_object_is_inert (buffer))) 923 return; 924 925 buffer->props.direction = direction; 926 } 927 928 /** 929 * hb_buffer_get_direction: 930 * @buffer: a buffer. 931 * 932 * 933 * 934 * Return value: 935 * 936 * Since: 0.9.2 937 **/ 938 hb_direction_t 939 hb_buffer_get_direction (hb_buffer_t *buffer) 940 { 941 return buffer->props.direction; 942 } 943 944 /** 945 * hb_buffer_set_script: 946 * @buffer: a buffer. 947 * @script: 948 * 949 * 950 * 951 * Since: 0.9.2 952 **/ 953 void 954 hb_buffer_set_script (hb_buffer_t *buffer, 955 hb_script_t script) 956 { 957 if (unlikely (hb_object_is_inert (buffer))) 958 return; 959 960 buffer->props.script = script; 961 } 962 963 /** 964 * hb_buffer_get_script: 965 * @buffer: a buffer. 966 * 967 * 968 * 969 * Return value: 970 * 971 * Since: 0.9.2 972 **/ 973 hb_script_t 974 hb_buffer_get_script (hb_buffer_t *buffer) 975 { 976 return buffer->props.script; 977 } 978 979 /** 980 * hb_buffer_set_language: 981 * @buffer: a buffer. 982 * @language: 983 * 984 * 985 * 986 * Since: 0.9.2 987 **/ 988 void 989 hb_buffer_set_language (hb_buffer_t *buffer, 990 hb_language_t language) 991 { 992 if (unlikely (hb_object_is_inert (buffer))) 993 return; 994 995 buffer->props.language = language; 996 } 997 998 /** 999 * hb_buffer_get_language: 1000 * @buffer: a buffer. 1001 * 1002 * 1003 * 1004 * Return value: (transfer none): 1005 * 1006 * Since: 0.9.2 1007 **/ 1008 hb_language_t 1009 hb_buffer_get_language (hb_buffer_t *buffer) 1010 { 1011 return buffer->props.language; 1012 } 1013 1014 /** 1015 * hb_buffer_set_segment_properties: 1016 * @buffer: a buffer. 1017 * @props: 1018 * 1019 * 1020 * 1021 * Since: 0.9.7 1022 **/ 1023 void 1024 hb_buffer_set_segment_properties (hb_buffer_t *buffer, 1025 const hb_segment_properties_t *props) 1026 { 1027 if (unlikely (hb_object_is_inert (buffer))) 1028 return; 1029 1030 buffer->props = *props; 1031 } 1032 1033 /** 1034 * hb_buffer_get_segment_properties: 1035 * @buffer: a buffer. 1036 * @props: (out): 1037 * 1038 * 1039 * 1040 * Since: 0.9.7 1041 **/ 1042 void 1043 hb_buffer_get_segment_properties (hb_buffer_t *buffer, 1044 hb_segment_properties_t *props) 1045 { 1046 *props = buffer->props; 1047 } 1048 1049 1050 /** 1051 * hb_buffer_set_flags: 1052 * @buffer: a buffer. 1053 * @flags: 1054 * 1055 * 1056 * 1057 * Since: 0.9.7 1058 **/ 1059 void 1060 hb_buffer_set_flags (hb_buffer_t *buffer, 1061 hb_buffer_flags_t flags) 1062 { 1063 if (unlikely (hb_object_is_inert (buffer))) 1064 return; 1065 1066 buffer->flags = flags; 1067 } 1068 1069 /** 1070 * hb_buffer_get_flags: 1071 * @buffer: a buffer. 1072 * 1073 * 1074 * 1075 * Return value: 1076 * 1077 * Since: 0.9.7 1078 **/ 1079 hb_buffer_flags_t 1080 hb_buffer_get_flags (hb_buffer_t *buffer) 1081 { 1082 return buffer->flags; 1083 } 1084 1085 /** 1086 * hb_buffer_set_cluster_level: 1087 * @buffer: a buffer. 1088 * @cluster_level: 1089 * 1090 * 1091 * 1092 * Since: 0.9.42 1093 **/ 1094 void 1095 hb_buffer_set_cluster_level (hb_buffer_t *buffer, 1096 hb_buffer_cluster_level_t cluster_level) 1097 { 1098 if (unlikely (hb_object_is_inert (buffer))) 1099 return; 1100 1101 buffer->cluster_level = cluster_level; 1102 } 1103 1104 /** 1105 * hb_buffer_get_cluster_level: 1106 * @buffer: a buffer. 1107 * 1108 * 1109 * 1110 * Return value: 1111 * 1112 * Since: 0.9.42 1113 **/ 1114 hb_buffer_cluster_level_t 1115 hb_buffer_get_cluster_level (hb_buffer_t *buffer) 1116 { 1117 return buffer->cluster_level; 1118 } 1119 1120 1121 /** 1122 * hb_buffer_set_replacement_codepoint: 1123 * @buffer: a buffer. 1124 * @replacement: 1125 * 1126 * 1127 * 1128 * Since: 0.9.31 1129 **/ 1130 void 1131 hb_buffer_set_replacement_codepoint (hb_buffer_t *buffer, 1132 hb_codepoint_t replacement) 1133 { 1134 if (unlikely (hb_object_is_inert (buffer))) 1135 return; 1136 1137 buffer->replacement = replacement; 1138 } 1139 1140 /** 1141 * hb_buffer_get_replacement_codepoint: 1142 * @buffer: a buffer. 1143 * 1144 * 1145 * 1146 * Return value: 1147 * 1148 * Since: 0.9.31 1149 **/ 1150 hb_codepoint_t 1151 hb_buffer_get_replacement_codepoint (hb_buffer_t *buffer) 1152 { 1153 return buffer->replacement; 1154 } 1155 1156 1157 /** 1158 * hb_buffer_reset: 1159 * @buffer: a buffer. 1160 * 1161 * 1162 * 1163 * Since: 0.9.2 1164 **/ 1165 void 1166 hb_buffer_reset (hb_buffer_t *buffer) 1167 { 1168 buffer->reset (); 1169 } 1170 1171 /** 1172 * hb_buffer_clear_contents: 1173 * @buffer: a buffer. 1174 * 1175 * 1176 * 1177 * Since: 0.9.11 1178 **/ 1179 void 1180 hb_buffer_clear_contents (hb_buffer_t *buffer) 1181 { 1182 buffer->clear (); 1183 } 1184 1185 /** 1186 * hb_buffer_pre_allocate: 1187 * @buffer: a buffer. 1188 * @size: 1189 * 1190 * 1191 * 1192 * Return value: 1193 * 1194 * Since: 0.9.2 1195 **/ 1196 hb_bool_t 1197 hb_buffer_pre_allocate (hb_buffer_t *buffer, unsigned int size) 1198 { 1199 return buffer->ensure (size); 1200 } 1201 1202 /** 1203 * hb_buffer_allocation_successful: 1204 * @buffer: a buffer. 1205 * 1206 * 1207 * 1208 * Return value: 1209 * 1210 * Since: 0.9.2 1211 **/ 1212 hb_bool_t 1213 hb_buffer_allocation_successful (hb_buffer_t *buffer) 1214 { 1215 return !buffer->in_error; 1216 } 1217 1218 /** 1219 * hb_buffer_add: 1220 * @buffer: a buffer. 1221 * @codepoint: 1222 * @cluster: 1223 * 1224 * 1225 * 1226 * Since: 0.9.7 1227 **/ 1228 void 1229 hb_buffer_add (hb_buffer_t *buffer, 1230 hb_codepoint_t codepoint, 1231 unsigned int cluster) 1232 { 1233 buffer->add (codepoint, cluster); 1234 buffer->clear_context (1); 1235 } 1236 1237 /** 1238 * hb_buffer_set_length: 1239 * @buffer: a buffer. 1240 * @length: 1241 * 1242 * 1243 * 1244 * Return value: 1245 * 1246 * Since: 0.9.2 1247 **/ 1248 hb_bool_t 1249 hb_buffer_set_length (hb_buffer_t *buffer, 1250 unsigned int length) 1251 { 1252 if (unlikely (hb_object_is_inert (buffer))) 1253 return length == 0; 1254 1255 if (!buffer->ensure (length)) 1256 return false; 1257 1258 /* Wipe the new space */ 1259 if (length > buffer->len) { 1260 memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len)); 1261 if (buffer->have_positions) 1262 memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len)); 1263 } 1264 1265 buffer->len = length; 1266 1267 if (!length) 1268 { 1269 buffer->content_type = HB_BUFFER_CONTENT_TYPE_INVALID; 1270 buffer->clear_context (0); 1271 } 1272 buffer->clear_context (1); 1273 1274 return true; 1275 } 1276 1277 /** 1278 * hb_buffer_get_length: 1279 * @buffer: a buffer. 1280 * 1281 * Returns the number of items in the buffer. 1282 * 1283 * Return value: buffer length. 1284 * 1285 * Since: 0.9.2 1286 **/ 1287 unsigned int 1288 hb_buffer_get_length (hb_buffer_t *buffer) 1289 { 1290 return buffer->len; 1291 } 1292 1293 /** 1294 * hb_buffer_get_glyph_infos: 1295 * @buffer: a buffer. 1296 * @length: (out): output array length. 1297 * 1298 * Returns buffer glyph information array. Returned pointer 1299 * is valid as long as buffer contents are not modified. 1300 * 1301 * Return value: (transfer none) (array length=length): buffer glyph information array. 1302 * 1303 * Since: 0.9.2 1304 **/ 1305 hb_glyph_info_t * 1306 hb_buffer_get_glyph_infos (hb_buffer_t *buffer, 1307 unsigned int *length) 1308 { 1309 if (length) 1310 *length = buffer->len; 1311 1312 return (hb_glyph_info_t *) buffer->info; 1313 } 1314 1315 /** 1316 * hb_buffer_get_glyph_positions: 1317 * @buffer: a buffer. 1318 * @length: (out): output length. 1319 * 1320 * Returns buffer glyph position array. Returned pointer 1321 * is valid as long as buffer contents are not modified. 1322 * 1323 * Return value: (transfer none) (array length=length): buffer glyph position array. 1324 * 1325 * Since: 0.9.2 1326 **/ 1327 hb_glyph_position_t * 1328 hb_buffer_get_glyph_positions (hb_buffer_t *buffer, 1329 unsigned int *length) 1330 { 1331 if (!buffer->have_positions) 1332 buffer->clear_positions (); 1333 1334 if (length) 1335 *length = buffer->len; 1336 1337 return (hb_glyph_position_t *) buffer->pos; 1338 } 1339 1340 /** 1341 * hb_buffer_reverse: 1342 * @buffer: a buffer. 1343 * 1344 * Reverses buffer contents. 1345 * 1346 * Since: 0.9.2 1347 **/ 1348 void 1349 hb_buffer_reverse (hb_buffer_t *buffer) 1350 { 1351 buffer->reverse (); 1352 } 1353 1354 /** 1355 * hb_buffer_reverse_range: 1356 * @buffer: a buffer. 1357 * @start: start index. 1358 * @end: end index. 1359 * 1360 * Reverses buffer contents between start to end. 1361 * 1362 * Since: 0.9.41 1363 **/ 1364 void 1365 hb_buffer_reverse_range (hb_buffer_t *buffer, 1366 unsigned int start, unsigned int end) 1367 { 1368 buffer->reverse_range (start, end); 1369 } 1370 1371 /** 1372 * hb_buffer_reverse_clusters: 1373 * @buffer: a buffer. 1374 * 1375 * Reverses buffer clusters. That is, the buffer contents are 1376 * reversed, then each cluster (consecutive items having the 1377 * same cluster number) are reversed again. 1378 * 1379 * Since: 0.9.2 1380 **/ 1381 void 1382 hb_buffer_reverse_clusters (hb_buffer_t *buffer) 1383 { 1384 buffer->reverse_clusters (); 1385 } 1386 1387 /** 1388 * hb_buffer_guess_segment_properties: 1389 * @buffer: a buffer. 1390 * 1391 * Sets unset buffer segment properties based on buffer Unicode 1392 * contents. If buffer is not empty, it must have content type 1393 * %HB_BUFFER_CONTENT_TYPE_UNICODE. 1394 * 1395 * If buffer script is not set (ie. is %HB_SCRIPT_INVALID), it 1396 * will be set to the Unicode script of the first character in 1397 * the buffer that has a script other than %HB_SCRIPT_COMMON, 1398 * %HB_SCRIPT_INHERITED, and %HB_SCRIPT_UNKNOWN. 1399 * 1400 * Next, if buffer direction is not set (ie. is %HB_DIRECTION_INVALID), 1401 * it will be set to the natural horizontal direction of the 1402 * buffer script as returned by hb_script_get_horizontal_direction(). 1403 * 1404 * Finally, if buffer language is not set (ie. is %HB_LANGUAGE_INVALID), 1405 * it will be set to the process's default language as returned by 1406 * hb_language_get_default(). This may change in the future by 1407 * taking buffer script into consideration when choosing a language. 1408 * 1409 * Since: 0.9.7 1410 **/ 1411 void 1412 hb_buffer_guess_segment_properties (hb_buffer_t *buffer) 1413 { 1414 buffer->guess_segment_properties (); 1415 } 1416 1417 template <typename utf_t> 1418 static inline void 1419 hb_buffer_add_utf (hb_buffer_t *buffer, 1420 const typename utf_t::codepoint_t *text, 1421 int text_length, 1422 unsigned int item_offset, 1423 int item_length) 1424 { 1425 typedef typename utf_t::codepoint_t T; 1426 const hb_codepoint_t replacement = buffer->replacement; 1427 1428 assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE || 1429 (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); 1430 1431 if (unlikely (hb_object_is_inert (buffer))) 1432 return; 1433 1434 if (text_length == -1) 1435 text_length = utf_t::strlen (text); 1436 1437 if (item_length == -1) 1438 item_length = text_length - item_offset; 1439 1440 buffer->ensure (buffer->len + item_length * sizeof (T) / 4); 1441 1442 /* If buffer is empty and pre-context provided, install it. 1443 * This check is written this way, to make sure people can 1444 * provide pre-context in one add_utf() call, then provide 1445 * text in a follow-up call. See: 1446 * 1447 * https://bugzilla.mozilla.org/show_bug.cgi?id=801410#c13 1448 */ 1449 if (!buffer->len && item_offset > 0) 1450 { 1451 /* Add pre-context */ 1452 buffer->clear_context (0); 1453 const T *prev = text + item_offset; 1454 const T *start = text; 1455 while (start < prev && buffer->context_len[0] < buffer->CONTEXT_LENGTH) 1456 { 1457 hb_codepoint_t u; 1458 prev = utf_t::prev (prev, start, &u, replacement); 1459 buffer->context[0][buffer->context_len[0]++] = u; 1460 } 1461 } 1462 1463 const T *next = text + item_offset; 1464 const T *end = next + item_length; 1465 while (next < end) 1466 { 1467 hb_codepoint_t u; 1468 const T *old_next = next; 1469 next = utf_t::next (next, end, &u, replacement); 1470 buffer->add (u, old_next - (const T *) text); 1471 } 1472 1473 /* Add post-context */ 1474 buffer->clear_context (1); 1475 end = text + text_length; 1476 while (next < end && buffer->context_len[1] < buffer->CONTEXT_LENGTH) 1477 { 1478 hb_codepoint_t u; 1479 next = utf_t::next (next, end, &u, replacement); 1480 buffer->context[1][buffer->context_len[1]++] = u; 1481 } 1482 1483 buffer->content_type = HB_BUFFER_CONTENT_TYPE_UNICODE; 1484 } 1485 1486 /** 1487 * hb_buffer_add_utf8: 1488 * @buffer: a buffer. 1489 * @text: (array length=text_length) (element-type uint8_t): 1490 * @text_length: 1491 * @item_offset: 1492 * @item_length: 1493 * 1494 * 1495 * 1496 * Since: 0.9.2 1497 **/ 1498 void 1499 hb_buffer_add_utf8 (hb_buffer_t *buffer, 1500 const char *text, 1501 int text_length, 1502 unsigned int item_offset, 1503 int item_length) 1504 { 1505 hb_buffer_add_utf<hb_utf8_t> (buffer, (const uint8_t *) text, text_length, item_offset, item_length); 1506 } 1507 1508 /** 1509 * hb_buffer_add_utf16: 1510 * @buffer: a buffer. 1511 * @text: (array length=text_length): 1512 * @text_length: 1513 * @item_offset: 1514 * @item_length: 1515 * 1516 * 1517 * 1518 * Since: 0.9.2 1519 **/ 1520 void 1521 hb_buffer_add_utf16 (hb_buffer_t *buffer, 1522 const uint16_t *text, 1523 int text_length, 1524 unsigned int item_offset, 1525 int item_length) 1526 { 1527 hb_buffer_add_utf<hb_utf16_t> (buffer, text, text_length, item_offset, item_length); 1528 } 1529 1530 /** 1531 * hb_buffer_add_utf32: 1532 * @buffer: a buffer. 1533 * @text: (array length=text_length): 1534 * @text_length: 1535 * @item_offset: 1536 * @item_length: 1537 * 1538 * 1539 * 1540 * Since: 0.9.2 1541 **/ 1542 void 1543 hb_buffer_add_utf32 (hb_buffer_t *buffer, 1544 const uint32_t *text, 1545 int text_length, 1546 unsigned int item_offset, 1547 int item_length) 1548 { 1549 hb_buffer_add_utf<hb_utf32_t<> > (buffer, text, text_length, item_offset, item_length); 1550 } 1551 1552 /** 1553 * hb_buffer_add_latin1: 1554 * @buffer: a buffer. 1555 * @text: (array length=text_length) (element-type uint8_t): 1556 * @text_length: 1557 * @item_offset: 1558 * @item_length: 1559 * 1560 * 1561 * 1562 * Since: 0.9.39 1563 **/ 1564 void 1565 hb_buffer_add_latin1 (hb_buffer_t *buffer, 1566 const uint8_t *text, 1567 int text_length, 1568 unsigned int item_offset, 1569 int item_length) 1570 { 1571 hb_buffer_add_utf<hb_latin1_t> (buffer, text, text_length, item_offset, item_length); 1572 } 1573 1574 /** 1575 * hb_buffer_add_codepoints: 1576 * @buffer: a buffer. 1577 * @text: (array length=text_length): 1578 * @text_length: 1579 * @item_offset: 1580 * @item_length: 1581 * 1582 * 1583 * 1584 * Since: 0.9.31 1585 **/ 1586 void 1587 hb_buffer_add_codepoints (hb_buffer_t *buffer, 1588 const hb_codepoint_t *text, 1589 int text_length, 1590 unsigned int item_offset, 1591 int item_length) 1592 { 1593 hb_buffer_add_utf<hb_utf32_t<false> > (buffer, text, text_length, item_offset, item_length); 1594 } 1595 1596 1597 static int 1598 compare_info_codepoint (const hb_glyph_info_t *pa, 1599 const hb_glyph_info_t *pb) 1600 { 1601 return (int) pb->codepoint - (int) pa->codepoint; 1602 } 1603 1604 static inline void 1605 normalize_glyphs_cluster (hb_buffer_t *buffer, 1606 unsigned int start, 1607 unsigned int end, 1608 bool backward) 1609 { 1610 hb_glyph_position_t *pos = buffer->pos; 1611 1612 /* Total cluster advance */ 1613 hb_position_t total_x_advance = 0, total_y_advance = 0; 1614 for (unsigned int i = start; i < end; i++) 1615 { 1616 total_x_advance += pos[i].x_advance; 1617 total_y_advance += pos[i].y_advance; 1618 } 1619 1620 hb_position_t x_advance = 0, y_advance = 0; 1621 for (unsigned int i = start; i < end; i++) 1622 { 1623 pos[i].x_offset += x_advance; 1624 pos[i].y_offset += y_advance; 1625 1626 x_advance += pos[i].x_advance; 1627 y_advance += pos[i].y_advance; 1628 1629 pos[i].x_advance = 0; 1630 pos[i].y_advance = 0; 1631 } 1632 1633 if (backward) 1634 { 1635 /* Transfer all cluster advance to the last glyph. */ 1636 pos[end - 1].x_advance = total_x_advance; 1637 pos[end - 1].y_advance = total_y_advance; 1638 1639 hb_stable_sort (buffer->info + start, end - start - 1, compare_info_codepoint, buffer->pos + start); 1640 } else { 1641 /* Transfer all cluster advance to the first glyph. */ 1642 pos[start].x_advance += total_x_advance; 1643 pos[start].y_advance += total_y_advance; 1644 for (unsigned int i = start + 1; i < end; i++) { 1645 pos[i].x_offset -= total_x_advance; 1646 pos[i].y_offset -= total_y_advance; 1647 } 1648 hb_stable_sort (buffer->info + start + 1, end - start - 1, compare_info_codepoint, buffer->pos + start + 1); 1649 } 1650 } 1651 1652 /** 1653 * hb_buffer_normalize_glyphs: 1654 * @buffer: a buffer. 1655 * 1656 * 1657 * 1658 * Since: 0.9.2 1659 **/ 1660 void 1661 hb_buffer_normalize_glyphs (hb_buffer_t *buffer) 1662 { 1663 assert (buffer->have_positions); 1664 assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS); 1665 1666 bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); 1667 1668 unsigned int count = buffer->len; 1669 if (unlikely (!count)) return; 1670 hb_glyph_info_t *info = buffer->info; 1671 1672 unsigned int start = 0; 1673 unsigned int end; 1674 for (end = start + 1; end < count; end++) 1675 if (info[start].cluster != info[end].cluster) { 1676 normalize_glyphs_cluster (buffer, start, end, backward); 1677 start = end; 1678 } 1679 normalize_glyphs_cluster (buffer, start, end, backward); 1680 } 1681 1682 void 1683 hb_buffer_t::sort (unsigned int start, unsigned int end, int(*compar)(const hb_glyph_info_t *, const hb_glyph_info_t *)) 1684 { 1685 assert (!have_positions); 1686 for (unsigned int i = start + 1; i < end; i++) 1687 { 1688 unsigned int j = i; 1689 while (j > start && compar (&info[j - 1], &info[i]) > 0) 1690 j--; 1691 if (i == j) 1692 continue; 1693 /* Move item i to occupy place for item j, shift what's in between. */ 1694 merge_clusters (j, i + 1); 1695 { 1696 hb_glyph_info_t t = info[i]; 1697 memmove (&info[j + 1], &info[j], (i - j) * sizeof (hb_glyph_info_t)); 1698 info[j] = t; 1699 } 1700 } 1701 }