| /* ------------------------------------------------------------------------ | |
| unicodedata -- Provides access to the Unicode database. | |
| The current version number is reported in the unidata_version constant. | |
| Written by Marc-Andre Lemburg (mal@lemburg.com). | |
| Modified for Python 2.0 by Fredrik Lundh (fredrik@pythonware.com) | |
| Modified by Martin v. Löwis (martin@v.loewis.de) | |
| Copyright (c) Corporation for National Research Initiatives. | |
| ------------------------------------------------------------------------ */ | |
| #ifndef Py_BUILD_CORE_BUILTIN | |
| # define Py_BUILD_CORE_MODULE 1 | |
| #endif | |
| #include "Python.h" | |
| #include "pycore_object.h" // _PyObject_VisitType() | |
| #include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI | |
| #include "pycore_unicodectype.h" // _PyUnicode_IsXidStart() | |
| #include <stdbool.h> | |
| #include <stddef.h> // offsetof() | |
| /* helper macro to fixup start/end slice values */ | |
| #define ADJUST_INDICES(start, end, len) \ | |
| do { \ | |
| if (end > len) { \ | |
| end = len; \ | |
| } \ | |
| else if (end < 0) { \ | |
| end += len; \ | |
| if (end < 0) { \ | |
| end = 0; \ | |
| } \ | |
| } \ | |
| if (start < 0) { \ | |
| start += len; \ | |
| if (start < 0) { \ | |
| start = 0; \ | |
| } \ | |
| } \ | |
| } while (0) | |
| /*[clinic input] | |
| module unicodedata | |
| class unicodedata.UCD 'PreviousDBVersion *' '<not used>' | |
| [clinic start generated code]*/ | |
| /*[clinic end generated code: output=da39a3ee5e6b4b0d input=e47113e05924be43]*/ | |
| /* character properties */ | |
| typedef struct { | |
| const unsigned char category; /* index into | |
| _PyUnicode_CategoryNames */ | |
| const unsigned char combining; /* combining class value 0 - 255 */ | |
| const unsigned char bidirectional; /* index into | |
| _PyUnicode_BidirectionalNames */ | |
| const unsigned char mirrored; /* true if mirrored in bidir mode */ | |
| const unsigned char east_asian_width; /* index into | |
| _PyUnicode_EastAsianWidth */ | |
| const unsigned char normalization_quick_check; /* see is_normalized() */ | |
| const unsigned char grapheme_cluster_break; /* index into | |
| _PyUnicode_GraphemeBreakNames */ | |
| const unsigned char incb; /* index into | |
| _PyUnicode_IndicConjunctBreakNames */ | |
| const unsigned char ext_pict; /* true if Extended_Pictographic */ | |
| } _PyUnicode_DatabaseRecord; | |
| typedef struct change_record { | |
| /* sequence of fields should be the same as in merge_old_version */ | |
| const unsigned char bidir_changed; | |
| const unsigned char category_changed; | |
| const unsigned char decimal_changed; | |
| const unsigned char mirrored_changed; | |
| const unsigned char east_asian_width_changed; | |
| const double numeric_changed; | |
| } change_record; | |
| /* data file generated by Tools/unicode/makeunicodedata.py */ | |
| #include "unicodedata_db.h" | |
| static const _PyUnicode_DatabaseRecord* | |
| _getrecord_ex(Py_UCS4 code) | |
| { | |
| int index; | |
| if (code >= 0x110000) | |
| index = 0; | |
| else { | |
| index = index1[(code>>SHIFT)]; | |
| index = index2[(index<<SHIFT)+(code&((1<<SHIFT)-1))]; | |
| } | |
| return &_PyUnicode_Database_Records[index]; | |
| } | |
| typedef struct { | |
| PyObject *SegmentType; | |
| PyObject *GraphemeBreakIteratorType; | |
| } unicodedatastate; | |
| static inline unicodedatastate * | |
| get_unicodedata_state(PyObject *module) | |
| { | |
| void *state = _PyModule_GetState(module); | |
| assert(state != NULL); | |
| return (unicodedatastate *)state; | |
| } | |
| /* ------------- Previous-version API ------------------------------------- */ | |
| typedef struct previous_version { | |
| PyObject_HEAD | |
| const char *name; | |
| const change_record* (*getrecord)(Py_UCS4); | |
| Py_UCS4 (*normalization)(Py_UCS4); | |
| } PreviousDBVersion; | |
| #define PreviousDBVersion_CAST(op) ((PreviousDBVersion *)(op)) | |
| #include "clinic/unicodedata.c.h" | |
| #define get_old_record(self, v) (PreviousDBVersion_CAST(self)->getrecord(v)) | |
| static PyMemberDef DB_members[] = { | |
| {"unidata_version", Py_T_STRING, offsetof(PreviousDBVersion, name), Py_READONLY}, | |
| {NULL} | |
| }; | |
| // Check if self is an unicodedata.UCD instance. | |
| // If self is NULL (when the PyCapsule C API is used), return 0. | |
| // PyModule_Check() is used to avoid having to retrieve the ucd_type. | |
| // See unicodedata_functions comment to the rationale of this macro. | |
| #define UCD_Check(self) (self != NULL && !PyModule_Check(self)) | |
| static PyObject* | |
| new_previous_version(PyTypeObject *ucd_type, | |
| const char*name, const change_record* (*getrecord)(Py_UCS4), | |
| Py_UCS4 (*normalization)(Py_UCS4)) | |
| { | |
| PreviousDBVersion *self; | |
| self = PyObject_GC_New(PreviousDBVersion, ucd_type); | |
| if (self == NULL) | |
| return NULL; | |
| self->name = name; | |
| self->getrecord = getrecord; | |
| self->normalization = normalization; | |
| PyObject_GC_Track(self); | |
| return (PyObject*)self; | |
| } | |
| /* --- Module API --------------------------------------------------------- */ | |
| /*[clinic input] | |
| unicodedata.UCD.decimal | |
| self: self | |
| chr: int(accept={str}) | |
| default: object=NULL | |
| / | |
| Converts a Unicode character into its equivalent decimal value. | |
| Returns the decimal value assigned to the character chr as integer. | |
| If no such value is defined, default is returned, or, if not given, | |
| ValueError is raised. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| unicodedata_UCD_decimal_impl(PyObject *self, int chr, | |
| PyObject *default_value) | |
| /*[clinic end generated code: output=be23376e1a185231 input=933f8107993f23d0]*/ | |
| { | |
| int have_old = 0; | |
| long rc; | |
| Py_UCS4 c = (Py_UCS4)chr; | |
| if (UCD_Check(self)) { | |
| const change_record *old = get_old_record(self, c); | |
| if (old->category_changed == 0) { | |
| /* unassigned */ | |
| have_old = 1; | |
| rc = -1; | |
| } | |
| else if (old->decimal_changed != 0xFF) { | |
| have_old = 1; | |
| rc = old->decimal_changed; | |
| } | |
| } | |
| if (!have_old) | |
| rc = Py_UNICODE_TODECIMAL(c); | |
| if (rc < 0) { | |
| if (default_value == NULL) { | |
| PyErr_SetString(PyExc_ValueError, | |
| "not a decimal"); | |
| return NULL; | |
| } | |
| else { | |
| return Py_NewRef(default_value); | |
| } | |
| } | |
| return PyLong_FromLong(rc); | |
| } | |
| /*[clinic input] | |
| unicodedata.UCD.digit | |
| self: self | |
| chr: int(accept={str}) | |
| default: object=NULL | |
| / | |
| Converts a Unicode character into its equivalent digit value. | |
| Returns the digit value assigned to the character chr as integer. | |
| If no such value is defined, default is returned, or, if not given, | |
| ValueError is raised. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| unicodedata_UCD_digit_impl(PyObject *self, int chr, PyObject *default_value) | |
| /*[clinic end generated code: output=96e18c950171fd2f input=e27d6e4565cd29f2]*/ | |
| { | |
| long rc; | |
| Py_UCS4 c = (Py_UCS4)chr; | |
| rc = Py_UNICODE_TODIGIT(c); | |
| if (rc < 0) { | |
| if (default_value == NULL) { | |
| PyErr_SetString(PyExc_ValueError, "not a digit"); | |
| return NULL; | |
| } | |
| else { | |
| return Py_NewRef(default_value); | |
| } | |
| } | |
| return PyLong_FromLong(rc); | |
| } | |
| /*[clinic input] | |
| unicodedata.UCD.numeric | |
| self: self | |
| chr: int(accept={str}) | |
| default: object=NULL | |
| / | |
| Converts a Unicode character into its equivalent numeric value. | |
| Returns the numeric value assigned to the character chr as float. | |
| If no such value is defined, default is returned, or, if not given, | |
| ValueError is raised. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| unicodedata_UCD_numeric_impl(PyObject *self, int chr, | |
| PyObject *default_value) | |
| /*[clinic end generated code: output=53ce281fe85b10c4 input=fdf5871a5542893c]*/ | |
| { | |
| int have_old = 0; | |
| double rc; | |
| Py_UCS4 c = (Py_UCS4)chr; | |
| if (UCD_Check(self)) { | |
| const change_record *old = get_old_record(self, c); | |
| if (old->category_changed == 0) { | |
| /* unassigned */ | |
| have_old = 1; | |
| rc = -1.0; | |
| } | |
| else if (old->numeric_changed != 0.0) { | |
| have_old = 1; | |
| rc = old->numeric_changed; | |
| } | |
| } | |
| if (!have_old) | |
| rc = Py_UNICODE_TONUMERIC(c); | |
| if (rc == -1.0) { | |
| if (default_value == NULL) { | |
| PyErr_SetString(PyExc_ValueError, "not a numeric character"); | |
| return NULL; | |
| } | |
| else { | |
| return Py_NewRef(default_value); | |
| } | |
| } | |
| return PyFloat_FromDouble(rc); | |
| } | |
| /*[clinic input] | |
| @permit_long_summary | |
| unicodedata.UCD.category | |
| self: self | |
| chr: int(accept={str}) | |
| / | |
| Returns the general category assigned to the character chr as string. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| unicodedata_UCD_category_impl(PyObject *self, int chr) | |
| /*[clinic end generated code: output=8571539ee2e6783a input=1d729c67299e8a31]*/ | |
| { | |
| int index; | |
| Py_UCS4 c = (Py_UCS4)chr; | |
| index = (int) _getrecord_ex(c)->category; | |
| if (UCD_Check(self)) { | |
| const change_record *old = get_old_record(self, c); | |
| if (old->category_changed != 0xFF) | |
| index = old->category_changed; | |
| } | |
| return PyUnicode_FromString(_PyUnicode_CategoryNames[index]); | |
| } | |
| /*[clinic input] | |
| @permit_long_summary | |
| unicodedata.UCD.bidirectional | |
| self: self | |
| chr: int(accept={str}) | |
| / | |
| Returns the bidirectional class assigned to the character chr as string. | |
| If no such value is defined, an empty string is returned. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| unicodedata_UCD_bidirectional_impl(PyObject *self, int chr) | |
| /*[clinic end generated code: output=d36310ce2039bb92 input=838f8a2203bd2990]*/ | |
| { | |
| int index; | |
| Py_UCS4 c = (Py_UCS4)chr; | |
| index = (int) _getrecord_ex(c)->bidirectional; | |
| if (UCD_Check(self)) { | |
| const change_record *old = get_old_record(self, c); | |
| if (old->category_changed == 0) | |
| index = 0; /* unassigned */ | |
| else if (old->bidir_changed != 0xFF) | |
| index = old->bidir_changed; | |
| } | |
| return PyUnicode_FromString(_PyUnicode_BidirectionalNames[index]); | |
| } | |
| /*[clinic input] | |
| @permit_long_summary | |
| unicodedata.UCD.combining -> int | |
| self: self | |
| chr: int(accept={str}) | |
| / | |
| Returns the canonical combining class assigned to the character chr as integer. | |
| Returns 0 if no combining class is defined. | |
| [clinic start generated code]*/ | |
| static int | |
| unicodedata_UCD_combining_impl(PyObject *self, int chr) | |
| /*[clinic end generated code: output=cad056d0cb6a5920 input=e05edfbb882ebfed]*/ | |
| { | |
| int index; | |
| Py_UCS4 c = (Py_UCS4)chr; | |
| index = (int) _getrecord_ex(c)->combining; | |
| if (UCD_Check(self)) { | |
| const change_record *old = get_old_record(self, c); | |
| if (old->category_changed == 0) | |
| index = 0; /* unassigned */ | |
| } | |
| return index; | |
| } | |
| /*[clinic input] | |
| @permit_long_summary | |
| unicodedata.UCD.mirrored -> int | |
| self: self | |
| chr: int(accept={str}) | |
| / | |
| Returns the mirrored property assigned to the character chr as integer. | |
| Returns 1 if the character has been identified as a "mirrored" | |
| character in bidirectional text, 0 otherwise. | |
| [clinic start generated code]*/ | |
| static int | |
| unicodedata_UCD_mirrored_impl(PyObject *self, int chr) | |
| /*[clinic end generated code: output=2532dbf8121b50e6 input=6db28989e49cd9c8]*/ | |
| { | |
| int index; | |
| Py_UCS4 c = (Py_UCS4)chr; | |
| index = (int) _getrecord_ex(c)->mirrored; | |
| if (UCD_Check(self)) { | |
| const change_record *old = get_old_record(self, c); | |
| if (old->category_changed == 0) | |
| index = 0; /* unassigned */ | |
| else if (old->mirrored_changed != 0xFF) | |
| index = old->mirrored_changed; | |
| } | |
| return index; | |
| } | |
| /*[clinic input] | |
| @permit_long_summary | |
| unicodedata.UCD.east_asian_width | |
| self: self | |
| chr: int(accept={str}) | |
| / | |
| Returns the east asian width assigned to the character chr as string. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| unicodedata_UCD_east_asian_width_impl(PyObject *self, int chr) | |
| /*[clinic end generated code: output=484e8537d9ee8197 input=207c5f68fa475516]*/ | |
| { | |
| int index; | |
| Py_UCS4 c = (Py_UCS4)chr; | |
| index = (int) _getrecord_ex(c)->east_asian_width; | |
| if (UCD_Check(self)) { | |
| const change_record *old = get_old_record(self, c); | |
| if (old->category_changed == 0) | |
| index = 0; /* unassigned */ | |
| else if (old->east_asian_width_changed != 0xFF) | |
| index = old->east_asian_width_changed; | |
| } | |
| return PyUnicode_FromString(_PyUnicode_EastAsianWidthNames[index]); | |
| } | |
| // For Hangul decomposition | |
| #define SBase 0xAC00 | |
| #define LBase 0x1100 | |
| #define VBase 0x1161 | |
| #define TBase 0x11A7 | |
| #define LCount 19 | |
| #define VCount 21 | |
| #define TCount 28 | |
| #define NCount (VCount*TCount) | |
| #define SCount (LCount*NCount) | |
| /*[clinic input] | |
| @permit_long_summary | |
| unicodedata.UCD.decomposition | |
| self: self | |
| chr: int(accept={str}) | |
| / | |
| Returns the character decomposition mapping assigned to the character chr as string. | |
| An empty string is returned in case no such mapping is defined. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| unicodedata_UCD_decomposition_impl(PyObject *self, int chr) | |
| /*[clinic end generated code: output=7d699f3ec7565d27 input=84d628d1abfd01ec]*/ | |
| { | |
| char decomp[256]; | |
| int code, index, count; | |
| size_t i; | |
| unsigned int prefix_index; | |
| Py_UCS4 c = (Py_UCS4)chr; | |
| code = (int)c; | |
| if (UCD_Check(self)) { | |
| const change_record *old = get_old_record(self, c); | |
| if (old->category_changed == 0) | |
| return Py_GetConstant(Py_CONSTANT_EMPTY_STR); /* unassigned */ | |
| } | |
| // Hangul Decomposition. | |
| // See section 3.12.2, "Hangul Syllable Decomposition" | |
| // https://www.unicode.org/versions/latest/core-spec/chapter-3/#G56669 | |
| if (SBase <= code && code < (SBase + SCount)) { | |
| int SIndex = code - SBase; | |
| int L = LBase + SIndex / NCount; | |
| int V = VBase + (SIndex % NCount) / TCount; | |
| int T = TBase + SIndex % TCount; | |
| if (T != TBase) { | |
| PyOS_snprintf(decomp, sizeof(decomp), | |
| "%04X %04X %04X", L, V, T); | |
| } | |
| else { | |
| PyOS_snprintf(decomp, sizeof(decomp), | |
| "%04X %04X", L, V); | |
| } | |
| return PyUnicode_FromString(decomp); | |
| } | |
| if (code < 0 || code >= 0x110000) | |
| index = 0; | |
| else { | |
| index = decomp_index1[(code>>DECOMP_SHIFT)]; | |
| index = decomp_index2[(index<<DECOMP_SHIFT)+ | |
| (code&((1<<DECOMP_SHIFT)-1))]; | |
| } | |
| /* high byte is number of hex bytes (usually one or two), low byte | |
| is prefix code (from*/ | |
| count = decomp_data[index] >> 8; | |
| /* XXX: could allocate the PyString up front instead | |
| (strlen(prefix) + 5 * count + 1 bytes) */ | |
| /* Based on how index is calculated above and decomp_data is generated | |
| from Tools/unicode/makeunicodedata.py, it should not be possible | |
| to overflow decomp_prefix. */ | |
| prefix_index = decomp_data[index] & 255; | |
| assert(prefix_index < Py_ARRAY_LENGTH(decomp_prefix)); | |
| /* copy prefix */ | |
| i = strlen(decomp_prefix[prefix_index]); | |
| memcpy(decomp, decomp_prefix[prefix_index], i); | |
| while (count-- > 0) { | |
| if (i) | |
| decomp[i++] = ' '; | |
| assert(i < sizeof(decomp)); | |
| PyOS_snprintf(decomp + i, sizeof(decomp) - i, "%04X", | |
| decomp_data[++index]); | |
| i += strlen(decomp + i); | |
| } | |
| return PyUnicode_FromStringAndSize(decomp, i); | |
| } | |
| static void | |
| get_decomp_record(PyObject *self, Py_UCS4 code, | |
| int *index, int *prefix, int *count) | |
| { | |
| if (code >= 0x110000) { | |
| *index = 0; | |
| } | |
| else if (UCD_Check(self) | |
| && get_old_record(self, code)->category_changed==0) { | |
| /* unassigned in old version */ | |
| *index = 0; | |
| } | |
| else { | |
| *index = decomp_index1[(code>>DECOMP_SHIFT)]; | |
| *index = decomp_index2[(*index<<DECOMP_SHIFT)+ | |
| (code&((1<<DECOMP_SHIFT)-1))]; | |
| } | |
| /* high byte is number of hex bytes (usually one or two), low byte | |
| is prefix code (from*/ | |
| *count = decomp_data[*index] >> 8; | |
| *prefix = decomp_data[*index] & 255; | |
| (*index)++; | |
| } | |
| /* Small combining runs are usually cheaper with insertion sort. */ | |
| #define CANONICAL_ORDERING_COUNTING_SORT_THRESHOLD 20 | |
| static void | |
| canonical_ordering_sort_insertion(Py_UCS4 *data, Py_ssize_t length) | |
| { | |
| for (Py_ssize_t i = 1; i < length; i++) { | |
| Py_UCS4 code = data[i]; | |
| unsigned char combining = _getrecord_ex(code)->combining; | |
| Py_ssize_t j = i; | |
| while (j > 0) { | |
| Py_UCS4 previous = data[j - 1]; | |
| if (_getrecord_ex(previous)->combining <= combining) { | |
| break; | |
| } | |
| data[j] = previous; | |
| j--; | |
| } | |
| if (j != i) { | |
| data[j] = code; | |
| } | |
| } | |
| } | |
| static void | |
| canonical_ordering_sort_counting(Py_UCS4 *data, Py_ssize_t length, | |
| Py_UCS4 *sortbuf) | |
| { | |
| Py_ssize_t counts[256] = {0}; | |
| Py_ssize_t total = 0; | |
| for (Py_ssize_t i = 0; i < length; i++) { | |
| Py_UCS4 code = data[i]; | |
| unsigned char combining = _getrecord_ex(code)->combining; | |
| counts[combining]++; | |
| } | |
| for (size_t i = 0; i < Py_ARRAY_LENGTH(counts); i++) { | |
| Py_ssize_t count = counts[i]; | |
| counts[i] = total; | |
| total += count; | |
| } | |
| /* Reuse counts[] as the next output slot for each CCC. */ | |
| for (Py_ssize_t i = 0; i < length; i++) { | |
| Py_UCS4 code = data[i]; | |
| unsigned char combining = _getrecord_ex(code)->combining; | |
| sortbuf[counts[combining]++] = code; | |
| } | |
| memcpy(data, sortbuf, length * sizeof(Py_UCS4)); | |
| } | |
| static PyObject* | |
| nfd_nfkd(PyObject *self, PyObject *input, int k) | |
| { | |
| PyObject *result; | |
| Py_UCS4 *output; | |
| Py_ssize_t i, o, osize; | |
| int input_kind; | |
| const void *input_data; | |
| /* Longest decomposition in Unicode 3.2: U+FDFA */ | |
| Py_UCS4 stack[20]; | |
| Py_ssize_t space, isize; | |
| int index, prefix, count, stackptr; | |
| unsigned char prev, cur; | |
| Py_UCS4 *sortbuf = NULL; | |
| Py_ssize_t sortbuflen = 0; | |
| stackptr = 0; | |
| isize = PyUnicode_GET_LENGTH(input); | |
| space = isize; | |
| /* Overallocate at most 10 characters. */ | |
| if (space > 10) { | |
| if (space <= PY_SSIZE_T_MAX - 10) | |
| space += 10; | |
| } | |
| else { | |
| space *= 2; | |
| } | |
| osize = space; | |
| output = PyMem_NEW(Py_UCS4, space); | |
| if (!output) { | |
| PyErr_NoMemory(); | |
| return NULL; | |
| } | |
| i = o = 0; | |
| input_kind = PyUnicode_KIND(input); | |
| input_data = PyUnicode_DATA(input); | |
| while (i < isize) { | |
| stack[stackptr++] = PyUnicode_READ(input_kind, input_data, i++); | |
| while(stackptr) { | |
| Py_UCS4 code = stack[--stackptr]; | |
| /* Hangul Decomposition adds three characters in | |
| a single step, so we need at least that much room. */ | |
| if (space < 3) { | |
| Py_UCS4 *new_output; | |
| osize += 10; | |
| space += 10; | |
| new_output = PyMem_Realloc(output, osize*sizeof(Py_UCS4)); | |
| if (new_output == NULL) { | |
| PyMem_Free(output); | |
| PyErr_NoMemory(); | |
| return NULL; | |
| } | |
| output = new_output; | |
| } | |
| // Hangul Decomposition. | |
| // See section 3.12.2, "Hangul Syllable Decomposition" | |
| // https://www.unicode.org/versions/latest/core-spec/chapter-3/#G56669 | |
| if (SBase <= code && code < (SBase+SCount)) { | |
| int SIndex = code - SBase; | |
| int L = LBase + SIndex / NCount; | |
| int V = VBase + (SIndex % NCount) / TCount; | |
| int T = TBase + SIndex % TCount; | |
| output[o++] = L; | |
| output[o++] = V; | |
| space -= 2; | |
| if (T != TBase) { | |
| output[o++] = T; | |
| space --; | |
| } | |
| continue; | |
| } | |
| /* normalization changes */ | |
| if (UCD_Check(self)) { | |
| Py_UCS4 value = ((PreviousDBVersion*)self)->normalization(code); | |
| if (value != 0) { | |
| stack[stackptr++] = value; | |
| continue; | |
| } | |
| } | |
| /* Other decompositions. */ | |
| get_decomp_record(self, code, &index, &prefix, &count); | |
| /* Copy character if it is not decomposable, or has a | |
| compatibility decomposition, but we do NFD. */ | |
| if (!count || (prefix && !k)) { | |
| output[o++] = code; | |
| space--; | |
| continue; | |
| } | |
| /* Copy decomposition onto the stack, in reverse | |
| order. */ | |
| while(count) { | |
| code = decomp_data[index + (--count)]; | |
| stack[stackptr++] = code; | |
| } | |
| } | |
| } | |
| /* Sort each consecutive combining-character run canonically. */ | |
| i = 0; | |
| while (i < o) { | |
| Py_ssize_t run_length, run_start; | |
| int needs_sort = 0; | |
| Py_UCS4 ch = output[i]; | |
| prev = _getrecord_ex(ch)->combining; | |
| if (prev == 0) { | |
| i++; | |
| continue; | |
| } | |
| run_start = i++; | |
| while (i < o) { | |
| Py_UCS4 ch = output[i]; | |
| cur = _getrecord_ex(ch)->combining; | |
| if (cur == 0) { | |
| break; | |
| } | |
| if (prev > cur) { | |
| needs_sort = 1; | |
| } | |
| prev = cur; | |
| i++; | |
| } | |
| if (!needs_sort) { | |
| continue; | |
| } | |
| run_length = i - run_start; | |
| if (run_length < CANONICAL_ORDERING_COUNTING_SORT_THRESHOLD) { | |
| canonical_ordering_sort_insertion(output + run_start, run_length); | |
| continue; | |
| } | |
| if (run_length > sortbuflen) { | |
| Py_UCS4 *new_sortbuf = PyMem_Resize(sortbuf, Py_UCS4, run_length); | |
| if (new_sortbuf == NULL) { | |
| PyErr_NoMemory(); | |
| PyMem_Free(sortbuf); | |
| PyMem_Free(output); | |
| return NULL; | |
| } | |
| sortbuf = new_sortbuf; | |
| sortbuflen = run_length; | |
| } | |
| canonical_ordering_sort_counting(output + run_start, run_length, | |
| sortbuf); | |
| } | |
| PyMem_Free(sortbuf); | |
| result = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, output, o); | |
| PyMem_Free(output); | |
| return result; | |
| } | |
| static int | |
| find_nfc_index(const struct reindex* nfc, Py_UCS4 code) | |
| { | |
| /* The table is sorted by .start ascending with disjoint [start, start+count] | |
| ranges and ends with a sentinel whose .start exceeds every codepoint, so | |
| a single .start <= code test per entry also stops at the sentinel. Find | |
| the first entry past code, then range-check the candidate (entry i - 1). */ | |
| unsigned int i; | |
| for (i = 0; (Py_UCS4)nfc[i].start <= code; i++) { | |
| } | |
| if (i == 0) { | |
| return -1; | |
| } | |
| unsigned int start = nfc[i - 1].start; | |
| if (code <= start + nfc[i - 1].count) { | |
| return nfc[i - 1].index + (code - start); | |
| } | |
| return -1; | |
| } | |
| static PyObject* | |
| nfc_nfkc(PyObject *self, PyObject *input, int k) | |
| { | |
| PyObject *result; | |
| int kind; | |
| const void *data; | |
| Py_UCS4 *output; | |
| Py_ssize_t i, i1, o, len; | |
| int f,l,index,index1,comb; | |
| Py_UCS4 code; | |
| Py_ssize_t skipped[20]; | |
| int cskipped = 0; | |
| result = nfd_nfkd(self, input, k); | |
| if (!result) | |
| return NULL; | |
| kind = PyUnicode_KIND(result); | |
| data = PyUnicode_DATA(result); | |
| len = PyUnicode_GET_LENGTH(result); | |
| /* We allocate a buffer for the output. | |
| If we find that we made no changes, we still return | |
| the NFD result. */ | |
| output = PyMem_NEW(Py_UCS4, len); | |
| if (!output) { | |
| PyErr_NoMemory(); | |
| Py_DECREF(result); | |
| return 0; | |
| } | |
| i = o = 0; | |
| again: | |
| while (i < len) { | |
| for (index = 0; index < cskipped; index++) { | |
| if (skipped[index] == i) { | |
| /* *i character is skipped. | |
| Remove from list. */ | |
| skipped[index] = skipped[cskipped-1]; | |
| cskipped--; | |
| i++; | |
| goto again; /* continue while */ | |
| } | |
| } | |
| /* Hangul Composition. We don't need to check for <LV,T> | |
| pairs, since we always have decomposed data. */ | |
| code = PyUnicode_READ(kind, data, i); | |
| if (LBase <= code && code < (LBase+LCount) && | |
| i + 1 < len && | |
| VBase <= PyUnicode_READ(kind, data, i+1) && | |
| PyUnicode_READ(kind, data, i+1) < (VBase+VCount)) { | |
| /* check L character is a modern leading consonant (0x1100 ~ 0x1112) | |
| and V character is a modern vowel (0x1161 ~ 0x1175). */ | |
| int LIndex, VIndex; | |
| LIndex = code - LBase; | |
| VIndex = PyUnicode_READ(kind, data, i+1) - VBase; | |
| code = SBase + (LIndex*VCount+VIndex)*TCount; | |
| i+=2; | |
| if (i < len && | |
| TBase < PyUnicode_READ(kind, data, i) && | |
| PyUnicode_READ(kind, data, i) < (TBase+TCount)) { | |
| /* check T character is a modern trailing consonant | |
| (0x11A8 ~ 0x11C2). */ | |
| code += PyUnicode_READ(kind, data, i)-TBase; | |
| i++; | |
| } | |
| output[o++] = code; | |
| continue; | |
| } | |
| /* code is still input[i] here */ | |
| f = find_nfc_index(nfc_first, code); | |
| if (f == -1) { | |
| output[o++] = code; | |
| i++; | |
| continue; | |
| } | |
| /* Find next unblocked character. */ | |
| i1 = i+1; | |
| comb = 0; | |
| /* output base character for now; might be updated later. */ | |
| output[o] = PyUnicode_READ(kind, data, i); | |
| while (i1 < len) { | |
| Py_UCS4 code1 = PyUnicode_READ(kind, data, i1); | |
| int comb1 = _getrecord_ex(code1)->combining; | |
| if (comb) { | |
| if (comb1 == 0) | |
| break; | |
| if (comb >= comb1) { | |
| /* Character is blocked. */ | |
| i1++; | |
| continue; | |
| } | |
| } | |
| l = find_nfc_index(nfc_last, code1); | |
| /* i1 cannot be combined with i. If i1 | |
| is a starter, we don't need to look further. | |
| Otherwise, record the combining class. */ | |
| if (l == -1) { | |
| not_combinable: | |
| if (comb1 == 0) | |
| break; | |
| comb = comb1; | |
| i1++; | |
| continue; | |
| } | |
| index = f*TOTAL_LAST + l; | |
| index1 = comp_index[index >> COMP_SHIFT]; | |
| code = comp_data[(index1<<COMP_SHIFT)+ | |
| (index&((1<<COMP_SHIFT)-1))]; | |
| if (code == 0) | |
| goto not_combinable; | |
| /* Replace the original character. */ | |
| output[o] = code; | |
| /* Mark the second character unused. */ | |
| assert(cskipped < 20); | |
| skipped[cskipped++] = i1; | |
| i1++; | |
| f = find_nfc_index(nfc_first, output[o]); | |
| if (f == -1) | |
| break; | |
| } | |
| /* Output character was already written. | |
| Just advance the indices. */ | |
| o++; i++; | |
| } | |
| if (o == len) { | |
| /* No changes. Return original string. */ | |
| PyMem_Free(output); | |
| return result; | |
| } | |
| Py_DECREF(result); | |
| result = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, | |
| output, o); | |
| PyMem_Free(output); | |
| return result; | |
| } | |
| // This needs to match the logic in makeunicodedata.py | |
| // which constructs the quickcheck data. | |
| typedef enum {YES = 0, MAYBE = 1, NO = 2} QuickcheckResult; | |
| /* Run the Unicode normalization "quickcheck" algorithm. | |
| * | |
| * Return YES or NO if quickcheck determines the input is certainly | |
| * normalized or certainly not, and MAYBE if quickcheck is unable to | |
| * tell. | |
| * | |
| * If `yes_only` is true, then return MAYBE as soon as we determine | |
| * the answer is not YES. | |
| * | |
| * For background and details on the algorithm, see UAX #15: | |
| * https://www.unicode.org/reports/tr15/#Detecting_Normalization_Forms | |
| */ | |
| static QuickcheckResult | |
| is_normalized_quickcheck(PyObject *self, PyObject *input, bool nfc, bool k, | |
| bool yes_only) | |
| { | |
| /* UCD 3.2.0 is requested, quickchecks must be disabled. */ | |
| if (UCD_Check(self)) { | |
| return MAYBE; | |
| } | |
| if (PyUnicode_IS_ASCII(input)) { | |
| return YES; | |
| } | |
| Py_ssize_t i, len; | |
| int kind; | |
| const void *data; | |
| unsigned char prev_combining = 0; | |
| /* The two quickcheck bits at this shift have type QuickcheckResult. */ | |
| int quickcheck_shift = (nfc ? 4 : 0) + (k ? 2 : 0); | |
| QuickcheckResult result = YES; /* certainly normalized, unless we find something */ | |
| i = 0; | |
| kind = PyUnicode_KIND(input); | |
| data = PyUnicode_DATA(input); | |
| len = PyUnicode_GET_LENGTH(input); | |
| while (i < len) { | |
| Py_UCS4 ch = PyUnicode_READ(kind, data, i++); | |
| const _PyUnicode_DatabaseRecord *record = _getrecord_ex(ch); | |
| unsigned char combining = record->combining; | |
| if (combining && prev_combining > combining) | |
| return NO; /* non-canonical sort order, not normalized */ | |
| prev_combining = combining; | |
| unsigned char quickcheck_whole = record->normalization_quick_check; | |
| if (yes_only) { | |
| if (quickcheck_whole & (3 << quickcheck_shift)) | |
| return MAYBE; | |
| } else { | |
| switch ((quickcheck_whole >> quickcheck_shift) & 3) { | |
| case NO: | |
| return NO; | |
| case MAYBE: | |
| result = MAYBE; /* this string might need normalization */ | |
| } | |
| } | |
| } | |
| return result; | |
| } | |
| /*[clinic input] | |
| @permit_long_summary | |
| unicodedata.UCD.is_normalized | |
| self: self | |
| form: unicode | |
| unistr as input: unicode | |
| / | |
| Return whether the Unicode string unistr is in the normal form 'form'. | |
| Valid values for form are 'NFC', 'NFKC', 'NFD', and 'NFKD'. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| unicodedata_UCD_is_normalized_impl(PyObject *self, PyObject *form, | |
| PyObject *input) | |
| /*[clinic end generated code: output=11e5a3694e723ca5 input=de66aa679265300b]*/ | |
| { | |
| if (PyUnicode_GET_LENGTH(input) == 0) { | |
| /* special case empty input strings. */ | |
| Py_RETURN_TRUE; | |
| } | |
| PyObject *result; | |
| bool nfc = false; | |
| bool k = false; | |
| QuickcheckResult m; | |
| PyObject *cmp; | |
| int match = 0; | |
| if (PyUnicode_CompareWithASCIIString(form, "NFC") == 0) { | |
| nfc = true; | |
| } | |
| else if (PyUnicode_CompareWithASCIIString(form, "NFKC") == 0) { | |
| nfc = true; | |
| k = true; | |
| } | |
| else if (PyUnicode_CompareWithASCIIString(form, "NFD") == 0) { | |
| /* matches default values for `nfc` and `k` */ | |
| } | |
| else if (PyUnicode_CompareWithASCIIString(form, "NFKD") == 0) { | |
| k = true; | |
| } | |
| else { | |
| PyErr_SetString(PyExc_ValueError, "invalid normalization form"); | |
| return NULL; | |
| } | |
| m = is_normalized_quickcheck(self, input, nfc, k, false); | |
| if (m == MAYBE) { | |
| cmp = (nfc ? nfc_nfkc : nfd_nfkd)(self, input, k); | |
| if (cmp == NULL) { | |
| return NULL; | |
| } | |
| match = PyUnicode_Compare(input, cmp); | |
| Py_DECREF(cmp); | |
| result = (match == 0) ? Py_True : Py_False; | |
| } | |
| else { | |
| result = (m == YES) ? Py_True : Py_False; | |
| } | |
| return Py_NewRef(result); | |
| } | |
| /*[clinic input] | |
| unicodedata.UCD.normalize | |
| self: self | |
| form: unicode | |
| unistr as input: unicode | |
| / | |
| Return the normal form 'form' for the Unicode string unistr. | |
| Valid values for form are 'NFC', 'NFKC', 'NFD', and 'NFKD'. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| unicodedata_UCD_normalize_impl(PyObject *self, PyObject *form, | |
| PyObject *input) | |
| /*[clinic end generated code: output=05ca4385a2ad6983 input=3a5206c0ad2833fb]*/ | |
| { | |
| if (PyUnicode_GET_LENGTH(input) == 0) { | |
| /* Special case empty input strings, since resizing | |
| them later would cause internal errors. */ | |
| return PyUnicode_FromObject(input); | |
| } | |
| if (PyUnicode_CompareWithASCIIString(form, "NFC") == 0) { | |
| if (is_normalized_quickcheck(self, input, | |
| true, false, true) == YES) { | |
| return PyUnicode_FromObject(input); | |
| } | |
| return nfc_nfkc(self, input, 0); | |
| } | |
| if (PyUnicode_CompareWithASCIIString(form, "NFKC") == 0) { | |
| if (is_normalized_quickcheck(self, input, | |
| true, true, true) == YES) { | |
| return PyUnicode_FromObject(input); | |
| } | |
| return nfc_nfkc(self, input, 1); | |
| } | |
| if (PyUnicode_CompareWithASCIIString(form, "NFD") == 0) { | |
| if (is_normalized_quickcheck(self, input, | |
| false, false, true) == YES) { | |
| return PyUnicode_FromObject(input); | |
| } | |
| return nfd_nfkd(self, input, 0); | |
| } | |
| if (PyUnicode_CompareWithASCIIString(form, "NFKD") == 0) { | |
| if (is_normalized_quickcheck(self, input, | |
| false, true, true) == YES) { | |
| return PyUnicode_FromObject(input); | |
| } | |
| return nfd_nfkd(self, input, 1); | |
| } | |
| PyErr_SetString(PyExc_ValueError, "invalid normalization form"); | |
| return NULL; | |
| } | |
| /* -------------------------------------------------------------------- */ | |
| /* unicode character name tables */ | |
| /* data file generated by Tools/unicode/makeunicodedata.py */ | |
| #include "unicodename_db.h" | |
| /* -------------------------------------------------------------------- */ | |
| /* database code (cut and pasted from the unidb package) */ | |
| static const char * const hangul_syllables[][3] = { | |
| { "G", "A", "" }, | |
| { "GG", "AE", "G" }, | |
| { "N", "YA", "GG" }, | |
| { "D", "YAE", "GS" }, | |
| { "DD", "EO", "N", }, | |
| { "R", "E", "NJ" }, | |
| { "M", "YEO", "NH" }, | |
| { "B", "YE", "D" }, | |
| { "BB", "O", "L" }, | |
| { "S", "WA", "LG" }, | |
| { "SS", "WAE", "LM" }, | |
| { "", "OE", "LB" }, | |
| { "J", "YO", "LS" }, | |
| { "JJ", "U", "LT" }, | |
| { "C", "WEO", "LP" }, | |
| { "K", "WE", "LH" }, | |
| { "T", "WI", "M" }, | |
| { "P", "YU", "B" }, | |
| { "H", "EU", "BS" }, | |
| { 0, "YI", "S" }, | |
| { 0, "I", "SS" }, | |
| { 0, 0, "NG" }, | |
| { 0, 0, "J" }, | |
| { 0, 0, "C" }, | |
| { 0, 0, "K" }, | |
| { 0, 0, "T" }, | |
| { 0, 0, "P" }, | |
| { 0, 0, "H" } | |
| }; | |
| static int | |
| find_prefix_id(Py_UCS4 code) | |
| { | |
| for (int i = 0; i < (int)Py_ARRAY_LENGTH(derived_name_ranges); i++) { | |
| if (code < derived_name_ranges[i].first) { | |
| return -1; | |
| } | |
| if (code <= derived_name_ranges[i].last) { | |
| return derived_name_ranges[i].prefixid; | |
| } | |
| } | |
| return -1; | |
| } | |
| /* macros used to determine if the given code point is in the PUA range that | |
| * we are using to store aliases and named sequences */ | |
| #define IS_ALIAS(cp) ((cp >= aliases_start) && (cp < aliases_end)) | |
| #define IS_NAMED_SEQ(cp) ((cp >= named_sequences_start) && \ | |
| (cp < named_sequences_end)) | |
| // DAWG decoding functions | |
| static unsigned int | |
| _dawg_decode_varint_unsigned(unsigned int index, unsigned int* result) | |
| { | |
| unsigned int res = 0; | |
| unsigned int shift = 0; | |
| for (;;) { | |
| unsigned char byte = packed_name_dawg[index]; | |
| res |= (byte & 0x7f) << shift; | |
| index++; | |
| shift += 7; | |
| if (!(byte & 0x80)) { | |
| *result = res; | |
| return index; | |
| } | |
| } | |
| } | |
| static int | |
| _dawg_match_edge(const char* name, unsigned int namelen, unsigned int size, | |
| unsigned int label_offset, unsigned int namepos) | |
| { | |
| // This returns 1 if the edge matched, 0 if it didn't (but further edges | |
| // could match) and -1 if the name cannot match at all. | |
| if (size > 1 && namepos + size > namelen) { | |
| return 0; | |
| } | |
| for (unsigned int i = 0; i < size; i++) { | |
| if (packed_name_dawg[label_offset + i] != Py_TOUPPER(name[namepos + i])) { | |
| if (i > 0) { | |
| return -1; // cannot match at all | |
| } | |
| return 0; | |
| } | |
| } | |
| return 1; | |
| } | |
| // reading DAWG node information: | |
| // a node is encoded by a varint. The lowest bit of that int is set if the node | |
| // is a final, accepting state. The higher bits of that int represent the | |
| // number of names that are encoded by the sub-DAWG started by this node. It's | |
| // used to compute the position of a name. | |
| // | |
| // the starting node of the DAWG is at position 0. | |
| // | |
| // the varint representing a node is followed by the node's edges, the encoding | |
| // is described below | |
| static unsigned int | |
| _dawg_decode_node(unsigned int node_offset, bool* final) | |
| { | |
| unsigned int num; | |
| node_offset = _dawg_decode_varint_unsigned(node_offset, &num); | |
| *final = num & 1; | |
| return node_offset; | |
| } | |
| static bool | |
| _dawg_node_is_final(unsigned int node_offset) | |
| { | |
| unsigned int num; | |
| _dawg_decode_varint_unsigned(node_offset, &num); | |
| return num & 1; | |
| } | |
| static unsigned int | |
| _dawg_node_descendant_count(unsigned int node_offset) | |
| { | |
| unsigned int num; | |
| _dawg_decode_varint_unsigned(node_offset, &num); | |
| return num >> 1; | |
| } | |
| // reading DAWG edge information: | |
| // a DAWG edge is comprised of the following information: | |
| // (1) the size of the label of the string attached to the edge | |
| // (2) the characters of that edge | |
| // (3) the target node | |
| // (4) whether the edge is the last edge in the list of edges following a node | |
| // | |
| // this information is encoded in a compact form as follows: | |
| // | |
| // +---------+-----------------+--------------+-------------------- | |
| // | varint | size (if != 1) | label chars | ... next edge ... | |
| // +---------+-----------------+--------------+-------------------- | |
| // | |
| // - first comes a varint | |
| // - the lowest bit of that varint is whether the edge is final (4) | |
| // - the second lowest bit of that varint is true if the size of | |
| // the length of the label is 1 (1) | |
| // - the rest of the varint is an offset that can be used to compute | |
| // the offset of the target node of that edge (3) | |
| // - if the size is not 1, the first varint is followed by a | |
| // character encoding the number of characters of the label (1) | |
| // (unicode character names aren't larger than 256 bytes, therefore each | |
| // edge label can be at most 256 chars, but is usually smaller) | |
| // - the next size bytes are the characters of the label (2) | |
| // | |
| // the offset of the target node is computed as follows: the number in the | |
| // upper bits of the varint needs to be added to the offset of the target node | |
| // of the previous edge. For the first edge, where there is no previous target | |
| // node, the offset of the first edge is used. | |
| // The intuition here is that edges going out from a node often lead to nodes | |
| // that are close by, leading to small offsets from the current node and thus | |
| // fewer bytes. | |
| // | |
| // There is a special case: if a final node has no outgoing edges, it has to be | |
| // followed by a 0 byte to indicate that there are no edges (because the end of | |
| // the edge list is normally indicated in a bit in the edge encoding). This is | |
| // indicated by _dawg_decode_edge returning -1 | |
| static int | |
| _dawg_decode_edge(bool is_first_edge, unsigned int prev_target_node_offset, | |
| unsigned int edge_offset, unsigned int* size, | |
| unsigned int* label_offset, unsigned int* target_node_offset) | |
| { | |
| unsigned int num; | |
| edge_offset = _dawg_decode_varint_unsigned(edge_offset, &num); | |
| if (num == 0 && is_first_edge) { | |
| return -1; // trying to decode past a final node without outgoing edges | |
| } | |
| bool last_edge = num & 1; | |
| num >>= 1; | |
| bool len_is_one = num & 1; | |
| num >>= 1; | |
| *target_node_offset = prev_target_node_offset + num; | |
| if (len_is_one) { | |
| *size = 1; | |
| } else { | |
| *size = packed_name_dawg[edge_offset++]; | |
| } | |
| *label_offset = edge_offset; | |
| return last_edge; | |
| } | |
| static int | |
| _lookup_dawg_packed(const char* name, unsigned int namelen) | |
| { | |
| unsigned int stringpos = 0; | |
| unsigned int node_offset = 0; | |
| unsigned int result = 0; // this is the number of final nodes that we skipped to match name | |
| while (stringpos < namelen) { | |
| bool final; | |
| unsigned int edge_offset = _dawg_decode_node(node_offset, &final); | |
| unsigned int prev_target_node_offset = edge_offset; | |
| bool is_first_edge = true; | |
| for (;;) { | |
| unsigned int size; | |
| unsigned int label_offset, target_node_offset; | |
| int last_edge = _dawg_decode_edge( | |
| is_first_edge, prev_target_node_offset, edge_offset, | |
| &size, &label_offset, &target_node_offset); | |
| if (last_edge == -1) { | |
| return -1; | |
| } | |
| is_first_edge = false; | |
| prev_target_node_offset = target_node_offset; | |
| int matched = _dawg_match_edge(name, namelen, size, label_offset, stringpos); | |
| if (matched == -1) { | |
| return -1; | |
| } | |
| if (matched) { | |
| if (final) | |
| result += 1; | |
| stringpos += size; | |
| node_offset = target_node_offset; | |
| break; | |
| } | |
| if (last_edge) { | |
| return -1; | |
| } | |
| result += _dawg_node_descendant_count(target_node_offset); | |
| edge_offset = label_offset + size; | |
| } | |
| } | |
| if (_dawg_node_is_final(node_offset)) { | |
| return result; | |
| } | |
| return -1; | |
| } | |
| static int | |
| _inverse_dawg_lookup(char* buffer, unsigned int buflen, unsigned int pos) | |
| { | |
| unsigned int node_offset = 0; | |
| unsigned int bufpos = 0; | |
| for (;;) { | |
| bool final; | |
| unsigned int edge_offset = _dawg_decode_node(node_offset, &final); | |
| if (final) { | |
| if (pos == 0) { | |
| if (bufpos + 1 == buflen) { | |
| return 0; | |
| } | |
| buffer[bufpos] = '\0'; | |
| return 1; | |
| } | |
| pos--; | |
| } | |
| unsigned int prev_target_node_offset = edge_offset; | |
| bool is_first_edge = true; | |
| for (;;) { | |
| unsigned int size; | |
| unsigned int label_offset, target_node_offset; | |
| int last_edge = _dawg_decode_edge( | |
| is_first_edge, prev_target_node_offset, edge_offset, | |
| &size, &label_offset, &target_node_offset); | |
| if (last_edge == -1) { | |
| return 0; | |
| } | |
| is_first_edge = false; | |
| prev_target_node_offset = target_node_offset; | |
| unsigned int descendant_count = _dawg_node_descendant_count(target_node_offset); | |
| if (pos < descendant_count) { | |
| if (bufpos + size >= buflen) { | |
| return 0; // buffer overflow | |
| } | |
| for (unsigned int i = 0; i < size; i++) { | |
| buffer[bufpos++] = packed_name_dawg[label_offset++]; | |
| } | |
| node_offset = target_node_offset; | |
| break; | |
| } else if (!last_edge) { | |
| pos -= descendant_count; | |
| edge_offset = label_offset + size; | |
| } else { | |
| return 0; | |
| } | |
| } | |
| } | |
| } | |
| static int | |
| _getucname(PyObject *self, | |
| Py_UCS4 code, char* buffer, int buflen, int with_alias_and_seq) | |
| { | |
| /* Find the name associated with the given code point. | |
| * If with_alias_and_seq is 1, check for names in the Private Use Area 15 | |
| * that we are using for aliases and named sequences. */ | |
| int offset; | |
| if (code >= 0x110000) | |
| return 0; | |
| /* XXX should we just skip all the code points in the PUAs here? */ | |
| if (!with_alias_and_seq && (IS_ALIAS(code) || IS_NAMED_SEQ(code))) | |
| return 0; | |
| if (UCD_Check(self)) { | |
| /* in 3.2.0 there are no aliases and named sequences */ | |
| const change_record *old; | |
| if (IS_ALIAS(code) || IS_NAMED_SEQ(code)) | |
| return 0; | |
| old = get_old_record(self, code); | |
| if (old->category_changed == 0) { | |
| /* unassigned */ | |
| return 0; | |
| } | |
| } | |
| int prefixid = find_prefix_id(code); | |
| if (prefixid == 0) { | |
| assert(SBase <= code && code < SBase+SCount); | |
| /* Hangul syllable. */ | |
| int SIndex = code - SBase; | |
| int L = SIndex / NCount; | |
| int V = (SIndex % NCount) / TCount; | |
| int T = SIndex % TCount; | |
| if (buflen < 27) | |
| /* Worst case: HANGUL SYLLABLE <10chars>. */ | |
| return 0; | |
| strcpy(buffer, "HANGUL SYLLABLE "); | |
| buffer += 16; | |
| strcpy(buffer, hangul_syllables[L][0]); | |
| buffer += strlen(hangul_syllables[L][0]); | |
| strcpy(buffer, hangul_syllables[V][1]); | |
| buffer += strlen(hangul_syllables[V][1]); | |
| strcpy(buffer, hangul_syllables[T][2]); | |
| buffer += strlen(hangul_syllables[T][2]); | |
| *buffer = '\0'; | |
| return 1; | |
| } | |
| if (prefixid > 0) { | |
| const char *prefix = derived_name_prefixes[prefixid]; | |
| if (snprintf(buffer, buflen, "%s%04X", prefix, code) >= buflen) { | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| /* get position of codepoint in order of names in the dawg */ | |
| offset = dawg_codepoint_to_pos_index1[(code>>DAWG_CODEPOINT_TO_POS_SHIFT)]; | |
| offset = dawg_codepoint_to_pos_index2[(offset<<DAWG_CODEPOINT_TO_POS_SHIFT) + | |
| (code&((1<<DAWG_CODEPOINT_TO_POS_SHIFT)-1))]; | |
| if (offset == DAWG_CODEPOINT_TO_POS_NOTFOUND) | |
| return 0; | |
| assert(buflen >= 0); | |
| return _inverse_dawg_lookup(buffer, Py_SAFE_DOWNCAST(buflen, int, unsigned int), offset); | |
| } | |
| static int | |
| capi_getucname(Py_UCS4 code, | |
| char* buffer, int buflen, | |
| int with_alias_and_seq) | |
| { | |
| return _getucname(NULL, code, buffer, buflen, with_alias_and_seq); | |
| } | |
| static void | |
| find_syllable(const char *str, int *len, int *pos, int count, int column) | |
| { | |
| int i, len1; | |
| *len = -1; | |
| for (i = 0; i < count; i++) { | |
| const char *s = hangul_syllables[i][column]; | |
| len1 = Py_SAFE_DOWNCAST(strlen(s), size_t, int); | |
| if (len1 <= *len) | |
| continue; | |
| if (PyOS_strnicmp(str, s, len1) == 0) { | |
| *len = len1; | |
| *pos = i; | |
| } | |
| } | |
| if (*len == -1) { | |
| *len = 0; | |
| } | |
| } | |
| static int | |
| _check_alias_and_seq(Py_UCS4* code, int with_named_seq) | |
| { | |
| /* check if named sequences are allowed */ | |
| if (!with_named_seq && IS_NAMED_SEQ(*code)) | |
| return 0; | |
| /* if the code point is in the PUA range that we use for aliases, | |
| * convert it to obtain the right code point */ | |
| if (IS_ALIAS(*code)) | |
| *code = name_aliases[*code-aliases_start]; | |
| return 1; | |
| } | |
| static Py_UCS4 | |
| parse_hex_code(const char *name, int namelen) | |
| { | |
| if (namelen < 4 || namelen > 6) { | |
| return (Py_UCS4)-1; | |
| } | |
| if (*name == '0') { | |
| return (Py_UCS4)-1; | |
| } | |
| int v = 0; | |
| while (namelen--) { | |
| v *= 16; | |
| Py_UCS1 c = Py_TOUPPER(*name); | |
| if (c >= '0' && c <= '9') { | |
| v += c - '0'; | |
| } | |
| else if (c >= 'A' && c <= 'F') { | |
| v += c - 'A' + 10; | |
| } | |
| else { | |
| return (Py_UCS4)-1; | |
| } | |
| name++; | |
| } | |
| if (v > 0x10ffff) { | |
| return (Py_UCS4)-1; | |
| } | |
| return v; | |
| } | |
| static int | |
| _getcode(const char* name, int namelen, Py_UCS4* code) | |
| { | |
| /* Return the code point associated with the given name. | |
| * Named aliases are not resolved, they are returned as a code point in the | |
| * PUA */ | |
| int i = 0; | |
| size_t prefixlen; | |
| for (; i < (int)Py_ARRAY_LENGTH(derived_name_prefixes); i++) { | |
| const char *prefix = derived_name_prefixes[i]; | |
| prefixlen = strlen(derived_name_prefixes[i]); | |
| if (PyOS_strnicmp(name, prefix, prefixlen) == 0) { | |
| break; | |
| } | |
| } | |
| if (i == 0) { | |
| /* Hangul syllables. */ | |
| assert(PyOS_strnicmp(name, "HANGUL SYLLABLE ", 16) == 0); | |
| int len, L = -1, V = -1, T = -1; | |
| const char *pos = name + 16; | |
| find_syllable(pos, &len, &L, LCount, 0); | |
| pos += len; | |
| find_syllable(pos, &len, &V, VCount, 1); | |
| pos += len; | |
| find_syllable(pos, &len, &T, TCount, 2); | |
| pos += len; | |
| if (L != -1 && V != -1 && T != -1 && pos-name == namelen) { | |
| *code = SBase + (L*VCount+V)*TCount + T; | |
| return 1; | |
| } | |
| /* Otherwise, it's an illegal syllable name. */ | |
| return 0; | |
| } | |
| if (i < (int)Py_ARRAY_LENGTH(derived_name_prefixes)) { | |
| Py_UCS4 v = parse_hex_code(name + prefixlen, namelen - (int)prefixlen); | |
| if (find_prefix_id(v) != i) { | |
| return 0; | |
| } | |
| *code = v; | |
| return 1; | |
| } | |
| assert(namelen >= 0); | |
| int position = _lookup_dawg_packed(name, Py_SAFE_DOWNCAST(namelen, int, unsigned int)); | |
| if (position < 0) { | |
| return 0; | |
| } | |
| *code = dawg_pos_to_codepoint[position]; | |
| return 1; | |
| } | |
| static int | |
| capi_getcode(const char* name, int namelen, Py_UCS4* code, | |
| int with_named_seq) | |
| { | |
| if (!_getcode(name, namelen, code)) { | |
| return 0; | |
| } | |
| return _check_alias_and_seq(code, with_named_seq); | |
| } | |
| static PyObject * | |
| unicodedata_create_capi(void) | |
| { | |
| // Statically allocated so that any cached pointers stay valid after unicodedata | |
| // is removed from sys.modules and the capsule is gc'd (gh-149449). | |
| static _PyUnicode_Name_CAPI capi = { | |
| .getname = capi_getucname, | |
| .getcode = capi_getcode, | |
| }; | |
| return PyCapsule_New(&capi, PyUnicodeData_CAPSULE_NAME, NULL); | |
| } | |
| /* -------------------------------------------------------------------- */ | |
| /* Python bindings */ | |
| /*[clinic input] | |
| unicodedata.UCD.name | |
| self: self | |
| chr: int(accept={str}) | |
| default: object=NULL | |
| / | |
| Returns the name assigned to the character chr as a string. | |
| If no name is defined, default is returned, or, if not given, | |
| ValueError is raised. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| unicodedata_UCD_name_impl(PyObject *self, int chr, PyObject *default_value) | |
| /*[clinic end generated code: output=6bbb37a326407707 input=3e0367f534de56d9]*/ | |
| { | |
| char name[NAME_MAXLEN+1]; | |
| Py_UCS4 c = (Py_UCS4)chr; | |
| if (!_getucname(self, c, name, NAME_MAXLEN, 0)) { | |
| if (default_value == NULL) { | |
| PyErr_SetString(PyExc_ValueError, "no such name"); | |
| return NULL; | |
| } | |
| else { | |
| return Py_NewRef(default_value); | |
| } | |
| } | |
| return PyUnicode_FromString(name); | |
| } | |
| /*[clinic input] | |
| unicodedata.isxidstart | |
| chr: int(accept={str}) | |
| / | |
| Return True if the character has the XID_Start property, else False. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| unicodedata_isxidstart_impl(PyObject *module, int chr) | |
| /*[clinic end generated code: output=7ae0e1a3915aa031 input=3812717f3a6bfc56]*/ | |
| { | |
| return PyBool_FromLong(_PyUnicode_IsXidStart(chr)); | |
| } | |
| /*[clinic input] | |
| unicodedata.isxidcontinue | |
| chr: int(accept={str}) | |
| / | |
| Return True if the character has the XID_Continue property, else False. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| unicodedata_isxidcontinue_impl(PyObject *module, int chr) | |
| /*[clinic end generated code: output=517caa8b38c73aed input=a971ed6e57cac374]*/ | |
| { | |
| return PyBool_FromLong(_PyUnicode_IsXidContinue(chr)); | |
| } | |
| /*[clinic input] | |
| unicodedata.UCD.lookup | |
| self: self | |
| name: str(accept={str, robuffer}, zeroes=True) | |
| / | |
| Look up character by name. | |
| If a character with the given name is found, return the | |
| corresponding character. If not found, KeyError is raised. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| unicodedata_UCD_lookup_impl(PyObject *self, const char *name, | |
| Py_ssize_t name_length) | |
| /*[clinic end generated code: output=7f03fc4959b242f6 input=a557be0f8607a0d6]*/ | |
| { | |
| Py_UCS4 code; | |
| unsigned int index; | |
| if (name_length > NAME_MAXLEN) { | |
| PyErr_SetString(PyExc_KeyError, "name too long"); | |
| return NULL; | |
| } | |
| if (!_getcode(name, (int)name_length, &code)) { | |
| PyErr_Format(PyExc_KeyError, "undefined character name '%s'", name); | |
| return NULL; | |
| } | |
| if (UCD_Check(self)) { | |
| /* in 3.2.0 there are no aliases and named sequences */ | |
| if (IS_ALIAS(code) || IS_NAMED_SEQ(code)) { | |
| PyErr_Format(PyExc_KeyError, "undefined character name '%s'", name); | |
| return 0; | |
| } | |
| } | |
| /* check if code is in the PUA range that we use for named sequences | |
| and convert it */ | |
| if (IS_NAMED_SEQ(code)) { | |
| index = code-named_sequences_start; | |
| return PyUnicode_FromKindAndData(PyUnicode_2BYTE_KIND, | |
| named_sequences[index].seq, | |
| named_sequences[index].seqlen); | |
| } | |
| if (IS_ALIAS(code)) { | |
| code = name_aliases[code-aliases_start]; | |
| } | |
| return PyUnicode_FromOrdinal(code); | |
| } | |
| /* Grapheme Cluster Break algorithm */ | |
| enum ExtPictState { | |
| ExtPictState_Init, | |
| // \p{Extended_Pictographic} Extend* | |
| ExtPictState_Started, | |
| // ... ZWJ | |
| ExtPictState_ZWJ, | |
| // ... \p{Extended_Pictographic} | |
| ExtPictState_Matched, | |
| }; | |
| enum InCBState { | |
| InCBState_Init, | |
| // \p{InCB=Linker} \p{InCB=Extend}* | |
| InCBState_Linker, | |
| // ... \p{InCB=Consonant} | |
| InCBState_Matched, | |
| }; | |
| typedef struct { | |
| PyObject *str; | |
| Py_ssize_t start; | |
| Py_ssize_t pos; | |
| Py_ssize_t end; | |
| int gcb; | |
| enum ExtPictState ep_state; | |
| enum InCBState incb_state; | |
| bool ri_flag; | |
| } _PyGraphemeBreak; | |
| static inline enum ExtPictState | |
| update_ext_pict_state(enum ExtPictState state, int gcb, bool ext_pict) | |
| { | |
| if (ext_pict) { | |
| return (state == ExtPictState_ZWJ) ? ExtPictState_Matched : ExtPictState_Started; | |
| } | |
| if (state == ExtPictState_Started || state == ExtPictState_Matched) { | |
| if (gcb == GCB_Extend) { | |
| return ExtPictState_Started; | |
| } | |
| if (gcb == GCB_ZWJ) { | |
| return ExtPictState_ZWJ; | |
| } | |
| } | |
| return ExtPictState_Init; | |
| } | |
| static inline enum InCBState | |
| update_incb_state(enum InCBState state, int incb) | |
| { | |
| if (incb == InCB_Linker) { | |
| return InCBState_Linker; | |
| } | |
| if (state == InCBState_Linker) { | |
| if (incb == InCB_Extend) { | |
| return InCBState_Linker; | |
| } | |
| if (incb == InCB_Consonant) { | |
| return InCBState_Matched; | |
| } | |
| } | |
| return InCBState_Init; | |
| } | |
| static inline bool | |
| update_ri_flag(bool flag, int gcb) | |
| { | |
| if (gcb == GCB_Regional_Indicator) { | |
| return !flag; | |
| } | |
| else { | |
| return false; | |
| } | |
| } | |
| static inline bool | |
| grapheme_break(int prev_gcb, int curr_gcb, enum ExtPictState ep_state, | |
| bool ri_flag, enum InCBState incb_state) | |
| { | |
| /* GB3 */ | |
| if (prev_gcb == GCB_CR && curr_gcb == GCB_LF) { | |
| return false; | |
| } | |
| /* GB4 */ | |
| if (prev_gcb == GCB_CR || | |
| prev_gcb == GCB_LF || | |
| prev_gcb == GCB_Control) | |
| { | |
| return true; | |
| } | |
| /* GB5 */ | |
| if (curr_gcb == GCB_CR || | |
| curr_gcb == GCB_LF || | |
| curr_gcb == GCB_Control) | |
| { | |
| return true; | |
| } | |
| /* GB6 */ | |
| if (prev_gcb == GCB_L && | |
| (curr_gcb == GCB_L || | |
| curr_gcb == GCB_V || | |
| curr_gcb == GCB_LV || | |
| curr_gcb == GCB_LVT)) | |
| { | |
| return false; | |
| } | |
| /* GB7 */ | |
| if ((prev_gcb == GCB_LV || prev_gcb == GCB_V) && | |
| (curr_gcb == GCB_V || curr_gcb == GCB_T)) | |
| { | |
| return false; | |
| } | |
| /* GB8 */ | |
| if ((prev_gcb == GCB_LVT || prev_gcb == GCB_T) && | |
| curr_gcb == GCB_T) | |
| { | |
| return false; | |
| } | |
| /* GB9 */ | |
| if (curr_gcb == GCB_Extend || curr_gcb == GCB_ZWJ) { | |
| return false; | |
| } | |
| /* GB9a */ | |
| if (curr_gcb == GCB_SpacingMark) { | |
| return false; | |
| } | |
| /* GB9b */ | |
| if (prev_gcb == GCB_Prepend) { | |
| return false; | |
| } | |
| /* GB9c */ | |
| if (incb_state == InCBState_Matched) { | |
| return false; | |
| } | |
| /* GB11 */ | |
| if (ep_state == ExtPictState_Matched) { | |
| return false; | |
| } | |
| /* GB12 and GB13 */ | |
| if (prev_gcb == GCB_Regional_Indicator && curr_gcb == prev_gcb) { | |
| return ri_flag; | |
| } | |
| /* GB999 */ | |
| return true; | |
| } | |
| static void | |
| _Py_InitGraphemeBreak(_PyGraphemeBreak *iter, PyObject *str, | |
| Py_ssize_t start, Py_ssize_t end) | |
| { | |
| iter->str = str; | |
| iter->start = iter->pos = start; | |
| iter->end = end; | |
| iter->gcb = 0; | |
| iter->ep_state = ExtPictState_Init; | |
| iter->ri_flag = false; | |
| iter->incb_state = InCBState_Init; | |
| } | |
| static Py_ssize_t | |
| _Py_NextGraphemeBreak(_PyGraphemeBreak *iter) | |
| { | |
| if (iter->start >= iter->end) { | |
| return -1; | |
| } | |
| int kind = PyUnicode_KIND(iter->str); | |
| void *pstr = PyUnicode_DATA(iter->str); | |
| while (iter->pos < iter->end) { | |
| Py_UCS4 chr = PyUnicode_READ(kind, pstr, iter->pos); | |
| const _PyUnicode_DatabaseRecord *record = _getrecord_ex(chr); | |
| int gcb = record->grapheme_cluster_break; | |
| iter->ep_state = update_ext_pict_state(iter->ep_state, gcb, record->ext_pict); | |
| iter->ri_flag = update_ri_flag(iter->ri_flag, gcb); | |
| iter->incb_state = update_incb_state(iter->incb_state, record->incb); | |
| int prev_gcb = iter->gcb; | |
| iter->gcb = gcb; | |
| if (iter->pos != iter->start && | |
| grapheme_break(prev_gcb, gcb, iter->ep_state, iter->ri_flag, | |
| iter->incb_state)) | |
| { | |
| iter->start = iter->pos; | |
| return iter->pos++; | |
| } | |
| ++iter->pos; | |
| } | |
| iter->start = iter->pos; | |
| return iter->pos; | |
| } | |
| /* Text Segment object */ | |
| typedef struct { | |
| PyObject_HEAD | |
| PyObject *string; | |
| Py_ssize_t start; | |
| Py_ssize_t end; | |
| } SegmentObject; | |
| static void | |
| Segment_dealloc(PyObject *self) | |
| { | |
| PyTypeObject *tp = Py_TYPE(self); | |
| PyObject_GC_UnTrack(self); | |
| Py_DECREF(((SegmentObject *)self)->string); | |
| tp->tp_free(self); | |
| Py_DECREF(tp); | |
| } | |
| static int | |
| Segment_traverse(PyObject *self, visitproc visit, void *arg) | |
| { | |
| Py_VISIT(((SegmentObject *)self)->string); | |
| return 0; | |
| } | |
| static PyObject * | |
| Segment_str(PyObject *self) | |
| { | |
| SegmentObject *s = (SegmentObject *)self; | |
| return PyUnicode_Substring(s->string, s->start, s->end); | |
| } | |
| static PyObject * | |
| Segment_repr(PyObject *self) | |
| { | |
| SegmentObject *s = (SegmentObject *)self; | |
| return PyUnicode_FromFormat("<Segment %zd:%zd>", s->start, s->end); | |
| } | |
| static PyMemberDef Segment_members[] = { | |
| {"start", Py_T_PYSSIZET, offsetof(SegmentObject, start), Py_READONLY, | |
| PyDoc_STR("grapheme start")}, | |
| {"end", Py_T_PYSSIZET, offsetof(SegmentObject, end), Py_READONLY, | |
| PyDoc_STR("grapheme end")}, | |
| {NULL} /* Sentinel */ | |
| }; | |
| static PyType_Slot Segment_slots[] = { | |
| {Py_tp_dealloc, Segment_dealloc}, | |
| {Py_tp_traverse, Segment_traverse}, | |
| {Py_tp_str, Segment_str}, | |
| {Py_tp_repr, Segment_repr}, | |
| {Py_tp_members, Segment_members}, | |
| {0, 0}, | |
| }; | |
| static PyType_Spec Segment_spec = { | |
| .name = "unicodedata.Segment", | |
| .basicsize = sizeof(SegmentObject), | |
| .flags = ( | |
| Py_TPFLAGS_DEFAULT | |
| | Py_TPFLAGS_HAVE_GC | |
| | Py_TPFLAGS_DISALLOW_INSTANTIATION | |
| | Py_TPFLAGS_IMMUTABLETYPE | |
| ), | |
| .slots = Segment_slots | |
| }; | |
| /* Grapheme Cluster iterator */ | |
| typedef struct { | |
| PyObject_HEAD | |
| _PyGraphemeBreak iter; | |
| } GraphemeBreakIterator; | |
| static void | |
| GBI_dealloc(PyObject *self) | |
| { | |
| PyTypeObject *tp = Py_TYPE(self); | |
| PyObject_GC_UnTrack(self); | |
| Py_DECREF(((GraphemeBreakIterator *)self)->iter.str); | |
| tp->tp_free(self); | |
| Py_DECREF(tp); | |
| } | |
| static int | |
| GBI_traverse(PyObject *self, visitproc visit, void *arg) | |
| { | |
| Py_VISIT(((GraphemeBreakIterator *)self)->iter.str); | |
| return 0; | |
| } | |
| static PyObject * | |
| GBI_iternext(PyObject *self) | |
| { | |
| GraphemeBreakIterator *it = (GraphemeBreakIterator *)self; | |
| Py_ssize_t start = it->iter.start; | |
| Py_ssize_t pos = _Py_NextGraphemeBreak(&it->iter); | |
| if (pos < 0) { | |
| return NULL; | |
| } | |
| PyObject *module = PyType_GetModule(Py_TYPE(it)); | |
| PyObject *SegmentType = get_unicodedata_state(module)->SegmentType; | |
| SegmentObject *s = PyObject_GC_New(SegmentObject, | |
| (PyTypeObject *)SegmentType); | |
| if (!s) { | |
| return NULL; | |
| } | |
| s->string = Py_NewRef(it->iter.str); | |
| s->start = start; | |
| s->end = pos; | |
| PyObject_GC_Track(s); | |
| return (PyObject *)s; | |
| } | |
| static PyType_Slot GraphemeBreakIterator_slots[] = { | |
| {Py_tp_dealloc, GBI_dealloc}, | |
| {Py_tp_iter, PyObject_SelfIter}, | |
| {Py_tp_iternext, GBI_iternext}, | |
| {Py_tp_traverse, GBI_traverse}, | |
| {0, 0}, | |
| }; | |
| static PyType_Spec GraphemeBreakIterator_spec = { | |
| .name = "unicodedata.GraphemeBreakIterator", | |
| .basicsize = sizeof(GraphemeBreakIterator), | |
| .flags = ( | |
| Py_TPFLAGS_DEFAULT | |
| | Py_TPFLAGS_HAVE_GC | |
| | Py_TPFLAGS_DISALLOW_INSTANTIATION | |
| | Py_TPFLAGS_IMMUTABLETYPE | |
| ), | |
| .slots = GraphemeBreakIterator_slots | |
| }; | |
| /*[clinic input] | |
| unicodedata.iter_graphemes | |
| unistr: unicode | |
| start: Py_ssize_t = 0 | |
| end: Py_ssize_t(c_default="PY_SSIZE_T_MAX") = sys.maxsize | |
| / | |
| Returns an iterator to iterate over grapheme clusters. | |
| It uses extended grapheme cluster rules from TR29. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| unicodedata_iter_graphemes_impl(PyObject *module, PyObject *unistr, | |
| Py_ssize_t start, Py_ssize_t end) | |
| /*[clinic end generated code: output=b0b831944265d36f input=a1454d9e8135951f]*/ | |
| { | |
| PyObject *GraphemeBreakIteratorType = get_unicodedata_state(module)->GraphemeBreakIteratorType; | |
| GraphemeBreakIterator *gbi = PyObject_GC_New(GraphemeBreakIterator, | |
| (PyTypeObject *)GraphemeBreakIteratorType); | |
| if (!gbi) { | |
| return NULL; | |
| } | |
| Py_ssize_t len = PyUnicode_GET_LENGTH(unistr); | |
| ADJUST_INDICES(start, end, len); | |
| Py_INCREF(unistr); | |
| _Py_InitGraphemeBreak(&gbi->iter, unistr, start, end); | |
| PyObject_GC_Track(gbi); | |
| return (PyObject*)gbi; | |
| } | |
| /*[clinic input] | |
| unicodedata.block | |
| chr: int(accept={str}) | |
| / | |
| Return block assigned to the character chr. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| unicodedata_block_impl(PyObject *module, int chr) | |
| /*[clinic end generated code: output=5f8b40c49eaec75a input=0834cf2642d6eaae]*/ | |
| { | |
| Py_UCS4 c = (Py_UCS4)chr; | |
| int lo = 0, hi = BLOCK_COUNT - 1; | |
| while (lo <= hi) { | |
| int mid = (lo + hi) / 2; | |
| if (c < _PyUnicode_Blocks[mid].start) { | |
| hi = mid - 1; | |
| } | |
| else if (c > _PyUnicode_Blocks[mid].end) { | |
| lo = mid + 1; | |
| } | |
| else { | |
| size_t name = _PyUnicode_Blocks[mid].name; | |
| return PyUnicode_FromString(_PyUnicode_BlockNames[name]); | |
| } | |
| } | |
| // Otherwise, return the default value per | |
| // https://www.unicode.org/versions/latest/core-spec/chapter-3/#G64189 | |
| return PyUnicode_FromString("No_Block"); | |
| } | |
| /*[clinic input] | |
| unicodedata.grapheme_cluster_break | |
| chr: int(accept={str}) | |
| / | |
| Returns the Grapheme_Cluster_Break property assigned to the character. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| unicodedata_grapheme_cluster_break_impl(PyObject *module, int chr) | |
| /*[clinic end generated code: output=39542e0f63bba36f input=5da75e86435576fd]*/ | |
| { | |
| Py_UCS4 c = (Py_UCS4)chr; | |
| int index = (int) _getrecord_ex(c)->grapheme_cluster_break; | |
| return PyUnicode_FromString(_PyUnicode_GraphemeBreakNames[index]); | |
| } | |
| /*[clinic input] | |
| unicodedata.indic_conjunct_break | |
| chr: int(accept={str}) | |
| / | |
| Returns the Indic_Conjunct_Break property assigned to the character. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| unicodedata_indic_conjunct_break_impl(PyObject *module, int chr) | |
| /*[clinic end generated code: output=673eff2caf797f08 input=5c730f78e469f2e8]*/ | |
| { | |
| Py_UCS4 c = (Py_UCS4)chr; | |
| int index = (int) _getrecord_ex(c)->incb; | |
| return PyUnicode_FromString(_PyUnicode_IndicConjunctBreakNames[index]); | |
| } | |
| /*[clinic input] | |
| @permit_long_summary | |
| unicodedata.extended_pictographic | |
| chr: int(accept={str}) | |
| / | |
| Returns the Extended_Pictographic property assigned to the character, as boolean. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| unicodedata_extended_pictographic_impl(PyObject *module, int chr) | |
| /*[clinic end generated code: output=b6bbb349427370b1 input=250d7bd988997eb3]*/ | |
| { | |
| Py_UCS4 c = (Py_UCS4)chr; | |
| int index = (int) _getrecord_ex(c)->ext_pict; | |
| return PyBool_FromLong(index); | |
| } | |
| // List of functions used to define module functions *AND* unicodedata.UCD | |
| // methods. For module functions, self is the module. For UCD methods, self | |
| // is an UCD instance. The UCD_Check() macro is used to check if self is | |
| // an UCD instance. | |
| static PyMethodDef unicodedata_functions[] = { | |
| // Module only functions. | |
| UNICODEDATA_BLOCK_METHODDEF | |
| UNICODEDATA_GRAPHEME_CLUSTER_BREAK_METHODDEF | |
| UNICODEDATA_INDIC_CONJUNCT_BREAK_METHODDEF | |
| UNICODEDATA_EXTENDED_PICTOGRAPHIC_METHODDEF | |
| UNICODEDATA_ITER_GRAPHEMES_METHODDEF | |
| UNICODEDATA_ISXIDSTART_METHODDEF | |
| UNICODEDATA_ISXIDCONTINUE_METHODDEF | |
| // The following definitions are shared between the module | |
| // and the UCD class. | |
| #define DB_methods (unicodedata_functions + 7) | |
| UNICODEDATA_UCD_DECIMAL_METHODDEF | |
| UNICODEDATA_UCD_DIGIT_METHODDEF | |
| UNICODEDATA_UCD_NUMERIC_METHODDEF | |
| UNICODEDATA_UCD_CATEGORY_METHODDEF | |
| UNICODEDATA_UCD_BIDIRECTIONAL_METHODDEF | |
| UNICODEDATA_UCD_COMBINING_METHODDEF | |
| UNICODEDATA_UCD_MIRRORED_METHODDEF | |
| UNICODEDATA_UCD_EAST_ASIAN_WIDTH_METHODDEF | |
| UNICODEDATA_UCD_DECOMPOSITION_METHODDEF | |
| UNICODEDATA_UCD_NAME_METHODDEF | |
| UNICODEDATA_UCD_LOOKUP_METHODDEF | |
| UNICODEDATA_UCD_IS_NORMALIZED_METHODDEF | |
| UNICODEDATA_UCD_NORMALIZE_METHODDEF | |
| {NULL, NULL} /* sentinel */ | |
| }; | |
| static void | |
| ucd_dealloc(PyObject *self) | |
| { | |
| PyTypeObject *tp = Py_TYPE(self); | |
| PyObject_GC_UnTrack(self); | |
| PyObject_GC_Del(self); | |
| Py_DECREF(tp); | |
| } | |
| static PyType_Slot ucd_type_slots[] = { | |
| {Py_tp_dealloc, ucd_dealloc}, | |
| {Py_tp_traverse, _PyObject_VisitType}, | |
| {Py_tp_getattro, PyObject_GenericGetAttr}, | |
| {Py_tp_methods, DB_methods}, | |
| {Py_tp_members, DB_members}, | |
| {0, 0} | |
| }; | |
| static PyType_Spec ucd_type_spec = { | |
| .name = "unicodedata.UCD", | |
| .basicsize = sizeof(PreviousDBVersion), | |
| .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | | |
| Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE), | |
| .slots = ucd_type_slots | |
| }; | |
| PyDoc_STRVAR(unicodedata_docstring, | |
| "This module provides access to the Unicode Character Database which\n\ | |
| defines character properties for all Unicode characters. The data in\n\ | |
| this database is based on the UnicodeData.txt file version\n\ | |
| " UNIDATA_VERSION " which is publicly available from ftp://ftp.unicode.org/.\n\ | |
| \n\ | |
| The module uses the same names and symbols as defined by the\n\ | |
| UnicodeData File Format " UNIDATA_VERSION "."); | |
| static int | |
| unicodedata_traverse(PyObject *module, visitproc visit, void *arg) | |
| { | |
| unicodedatastate *state = get_unicodedata_state(module); | |
| Py_VISIT(state->SegmentType); | |
| Py_VISIT(state->GraphemeBreakIteratorType); | |
| return 0; | |
| } | |
| static int | |
| unicodedata_clear(PyObject *module) | |
| { | |
| unicodedatastate *state = get_unicodedata_state(module); | |
| Py_CLEAR(state->SegmentType); | |
| Py_CLEAR(state->GraphemeBreakIteratorType); | |
| return 0; | |
| } | |
| static void | |
| unicodedata_free(void *module) | |
| { | |
| unicodedata_clear((PyObject *)module); | |
| } | |
| static int | |
| unicodedata_exec(PyObject *module) | |
| { | |
| unicodedatastate *state = get_unicodedata_state(module); | |
| PyObject *SegmentType = PyType_FromModuleAndSpec(module, &Segment_spec, NULL); | |
| if (SegmentType == NULL) { | |
| return -1; | |
| } | |
| state->SegmentType = SegmentType; | |
| PyObject *GraphemeBreakIteratorType = PyType_FromModuleAndSpec(module, &GraphemeBreakIterator_spec, NULL); | |
| if (GraphemeBreakIteratorType == NULL) { | |
| return -1; | |
| } | |
| state->GraphemeBreakIteratorType = GraphemeBreakIteratorType; | |
| if (PyModule_AddStringConstant(module, "unidata_version", UNIDATA_VERSION) < 0) { | |
| return -1; | |
| } | |
| PyTypeObject *ucd_type = (PyTypeObject *)PyType_FromSpec(&ucd_type_spec); | |
| if (ucd_type == NULL) { | |
| return -1; | |
| } | |
| if (PyModule_AddType(module, ucd_type) < 0) { | |
| Py_DECREF(ucd_type); | |
| return -1; | |
| } | |
| // Unicode database version 3.2.0 used by the IDNA encoding | |
| PyObject *v; | |
| v = new_previous_version(ucd_type, "3.2.0", | |
| get_change_3_2_0, normalization_3_2_0); | |
| Py_DECREF(ucd_type); | |
| if (PyModule_Add(module, "ucd_3_2_0", v) < 0) { | |
| return -1; | |
| } | |
| /* Export C API */ | |
| if (PyModule_Add(module, "_ucnhash_CAPI", unicodedata_create_capi()) < 0) { | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| static PyModuleDef_Slot unicodedata_slots[] = { | |
| _Py_ABI_SLOT, | |
| {Py_mod_exec, unicodedata_exec}, | |
| {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, | |
| {Py_mod_gil, Py_MOD_GIL_NOT_USED}, | |
| {0, NULL} | |
| }; | |
| static struct PyModuleDef unicodedata_module = { | |
| PyModuleDef_HEAD_INIT, | |
| .m_name = "unicodedata", | |
| .m_doc = unicodedata_docstring, | |
| .m_size = sizeof(unicodedatastate), | |
| .m_methods = unicodedata_functions, | |
| .m_slots = unicodedata_slots, | |
| .m_traverse = unicodedata_traverse, | |
| .m_clear = unicodedata_clear, | |
| .m_free = unicodedata_free, | |
| }; | |
| PyMODINIT_FUNC | |
| PyInit_unicodedata(void) | |
| { | |
| return PyModuleDef_Init(&unicodedata_module); | |
| } | |
| /* | |
| Local variables: | |
| c-basic-offset: 4 | |
| indent-tabs-mode: nil | |
| End: | |
| */ |