| /* | |
| * Copyright (C) 2012 Grigori Goronzy <greg@kinoho.net> | |
| * | |
| * Permission to use, copy, modify, and/or distribute this software for any | |
| * purpose with or without fee is hereby granted, provided that the above | |
| * copyright notice and this permission notice appear in all copies. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
| */ | |
| #include "hb.hh" | |
| #include "hb-unicode.hh" | |
| #include "hb-machinery.hh" | |
| #include "hb-ucd-table.hh" | |
| static hb_unicode_combining_class_t | |
| hb_ucd_combining_class (hb_unicode_funcs_t *ufuncs HB_UNUSED, | |
| hb_codepoint_t unicode, | |
| void *user_data HB_UNUSED) | |
| { | |
| return (hb_unicode_combining_class_t) _hb_ucd_ccc (unicode); | |
| } | |
| static hb_unicode_general_category_t | |
| hb_ucd_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED, | |
| hb_codepoint_t unicode, | |
| void *user_data HB_UNUSED) | |
| { | |
| return (hb_unicode_general_category_t) _hb_ucd_gc (unicode); | |
| } | |
| static hb_codepoint_t | |
| hb_ucd_mirroring (hb_unicode_funcs_t *ufuncs HB_UNUSED, | |
| hb_codepoint_t unicode, | |
| void *user_data HB_UNUSED) | |
| { | |
| return unicode + _hb_ucd_bmg (unicode); | |
| } | |
| static hb_script_t | |
| hb_ucd_script (hb_unicode_funcs_t *ufuncs HB_UNUSED, | |
| hb_codepoint_t unicode, | |
| void *user_data HB_UNUSED) | |
| { | |
| return _hb_ucd_sc_map[_hb_ucd_sc (unicode)]; | |
| } | |
| #define SBASE 0xAC00u | |
| #define LBASE 0x1100u | |
| #define VBASE 0x1161u | |
| #define TBASE 0x11A7u | |
| #define SCOUNT 11172u | |
| #define LCOUNT 19u | |
| #define VCOUNT 21u | |
| #define TCOUNT 28u | |
| #define NCOUNT (VCOUNT * TCOUNT) | |
| static inline bool | |
| _hb_ucd_decompose_hangul (hb_codepoint_t ab, hb_codepoint_t *a, hb_codepoint_t *b) | |
| { | |
| unsigned si = ab - SBASE; | |
| if (si >= SCOUNT) | |
| return false; | |
| if (si % TCOUNT) | |
| { | |
| /* LV,T */ | |
| *a = SBASE + (si / TCOUNT) * TCOUNT; | |
| *b = TBASE + (si % TCOUNT); | |
| return true; | |
| } else { | |
| /* L,V */ | |
| *a = LBASE + (si / NCOUNT); | |
| *b = VBASE + (si % NCOUNT) / TCOUNT; | |
| return true; | |
| } | |
| } | |
| static inline bool | |
| _hb_ucd_compose_hangul (hb_codepoint_t a, hb_codepoint_t b, hb_codepoint_t *ab) | |
| { | |
| if (a >= SBASE && a < (SBASE + SCOUNT) && b > TBASE && b < (TBASE + TCOUNT) && | |
| !((a - SBASE) % TCOUNT)) | |
| { | |
| /* LV,T */ | |
| *ab = a + (b - TBASE); | |
| return true; | |
| } | |
| else if (a >= LBASE && a < (LBASE + LCOUNT) && b >= VBASE && b < (VBASE + VCOUNT)) | |
| { | |
| /* L,V */ | |
| int li = a - LBASE; | |
| int vi = b - VBASE; | |
| *ab = SBASE + li * NCOUNT + vi * TCOUNT; | |
| return true; | |
| } | |
| else | |
| return false; | |
| } | |
| static int | |
| _cmp_pair (const void *_key, const void *_item) | |
| { | |
| uint64_t& a = * (uint64_t*) _key; | |
| uint64_t b = (* (uint64_t*) _item) & HB_CODEPOINT_ENCODE3(0x1FFFFFu, 0x1FFFFFu, 0); | |
| return a < b ? -1 : a > b ? +1 : 0; | |
| } | |
| static int | |
| _cmp_pair_11_7_14 (const void *_key, const void *_item) | |
| { | |
| uint32_t& a = * (uint32_t*) _key; | |
| uint32_t b = (* (uint32_t*) _item) & HB_CODEPOINT_ENCODE3_11_7_14(0x1FFFFFu, 0x1FFFFFu, 0); | |
| return a < b ? -1 : a > b ? +1 : 0; | |
| } | |
| static hb_bool_t | |
| hb_ucd_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, | |
| hb_codepoint_t a, hb_codepoint_t b, hb_codepoint_t *ab, | |
| void *user_data HB_UNUSED) | |
| { | |
| // Hangul is handled algorithmically. | |
| if (_hb_ucd_compose_hangul (a, b, ab)) return true; | |
| hb_codepoint_t u = 0; | |
| if ((a & 0xFFFFF800u) == 0x0000u && (b & 0xFFFFFF80) == 0x0300u) | |
| { | |
| /* If "a" is small enough and "b" is in the U+0300 range, | |
| * the composition data is encoded in a 32bit array sorted | |
| * by "a,b" pair. */ | |
| uint32_t k = HB_CODEPOINT_ENCODE3_11_7_14 (a, b, 0); | |
| const uint32_t *v = hb_bsearch (k, | |
| _hb_ucd_dm2_u32_map, | |
| ARRAY_LENGTH (_hb_ucd_dm2_u32_map), | |
| sizeof (*_hb_ucd_dm2_u32_map), | |
| _cmp_pair_11_7_14); | |
| if (likely (!v)) return false; | |
| u = HB_CODEPOINT_DECODE3_11_7_14_3 (*v); | |
| } | |
| else | |
| { | |
| /* Otherwise it is stored in a 64bit array sorted by | |
| * "a,b" pair. */ | |
| uint64_t k = HB_CODEPOINT_ENCODE3 (a, b, 0); | |
| const uint64_t *v = hb_bsearch (k, | |
| _hb_ucd_dm2_u64_map, | |
| ARRAY_LENGTH (_hb_ucd_dm2_u64_map), | |
| sizeof (*_hb_ucd_dm2_u64_map), | |
| _cmp_pair); | |
| if (likely (!v)) return false; | |
| u = HB_CODEPOINT_DECODE3_3 (*v); | |
| } | |
| if (unlikely (!u)) return false; | |
| *ab = u; | |
| return true; | |
| } | |
| static hb_bool_t | |
| hb_ucd_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, | |
| hb_codepoint_t ab, hb_codepoint_t *a, hb_codepoint_t *b, | |
| void *user_data HB_UNUSED) | |
| { | |
| if (_hb_ucd_decompose_hangul (ab, a, b)) return true; | |
| unsigned i = _hb_ucd_dm (ab); | |
| /* If no data, there's no decomposition. */ | |
| if (likely (!i)) return false; | |
| i--; | |
| /* Check if it's a single-character decomposition. */ | |
| if (i < ARRAY_LENGTH (_hb_ucd_dm1_p0_map) + ARRAY_LENGTH (_hb_ucd_dm1_p2_map)) | |
| { | |
| /* Single-character decompositions currently are only in plane 0 or plane 2. */ | |
| if (i < ARRAY_LENGTH (_hb_ucd_dm1_p0_map)) | |
| { | |
| /* Plane 0. */ | |
| *a = _hb_ucd_dm1_p0_map[i]; | |
| } | |
| else | |
| { | |
| /* Plane 2. */ | |
| i -= ARRAY_LENGTH (_hb_ucd_dm1_p0_map); | |
| *a = 0x20000 | _hb_ucd_dm1_p2_map[i]; | |
| } | |
| *b = 0; | |
| return true; | |
| } | |
| i -= ARRAY_LENGTH (_hb_ucd_dm1_p0_map) + ARRAY_LENGTH (_hb_ucd_dm1_p2_map); | |
| /* Otherwise they are encoded either in a 32bit array or a 64bit array. */ | |
| if (i < ARRAY_LENGTH (_hb_ucd_dm2_u32_map)) | |
| { | |
| /* 32bit array. */ | |
| uint32_t v = _hb_ucd_dm2_u32_map[i]; | |
| *a = HB_CODEPOINT_DECODE3_11_7_14_1 (v); | |
| *b = HB_CODEPOINT_DECODE3_11_7_14_2 (v); | |
| return true; | |
| } | |
| i -= ARRAY_LENGTH (_hb_ucd_dm2_u32_map); | |
| /* 64bit array. */ | |
| uint64_t v = _hb_ucd_dm2_u64_map[i]; | |
| *a = HB_CODEPOINT_DECODE3_1 (v); | |
| *b = HB_CODEPOINT_DECODE3_2 (v); | |
| return true; | |
| } | |
| static void free_static_ucd_funcs (); | |
| static struct hb_ucd_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t<hb_ucd_unicode_funcs_lazy_loader_t> | |
| { | |
| static hb_unicode_funcs_t *create () | |
| { | |
| hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr); | |
| hb_unicode_funcs_set_combining_class_func (funcs, hb_ucd_combining_class, nullptr, nullptr); | |
| hb_unicode_funcs_set_general_category_func (funcs, hb_ucd_general_category, nullptr, nullptr); | |
| hb_unicode_funcs_set_mirroring_func (funcs, hb_ucd_mirroring, nullptr, nullptr); | |
| hb_unicode_funcs_set_script_func (funcs, hb_ucd_script, nullptr, nullptr); | |
| hb_unicode_funcs_set_compose_func (funcs, hb_ucd_compose, nullptr, nullptr); | |
| hb_unicode_funcs_set_decompose_func (funcs, hb_ucd_decompose, nullptr, nullptr); | |
| hb_unicode_funcs_make_immutable (funcs); | |
| hb_atexit (free_static_ucd_funcs); | |
| return funcs; | |
| } | |
| } static_ucd_funcs; | |
| static inline | |
| void free_static_ucd_funcs () | |
| { | |
| static_ucd_funcs.free_instance (); | |
| } | |
| hb_unicode_funcs_t * | |
| hb_ucd_get_unicode_funcs () | |
| { | |
| #ifdef HB_NO_UCD | |
| return hb_unicode_funcs_get_empty (); | |
| #endif | |
| return static_ucd_funcs.get_unconst (); | |
| } |