Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
/mkutf8data
/utf8data.c
+16
View File
@@ -0,0 +1,16 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# UTF-8 normalization
#
config UNICODE
tristate "UTF-8 normalization and casefolding support"
help
Say Y here to enable UTF-8 NFD normalization and NFD+CF casefolding
support. If you say M here the large table of case foldings will
be a separate loadable module that gets requested only when a file
system actually use it.
config UNICODE_NORMALIZATION_KUNIT_TEST
tristate "Test UTF-8 normalization support"
depends on UNICODE && KUNIT
default KUNIT_ALL_TESTS
+41
View File
@@ -0,0 +1,41 @@
# SPDX-License-Identifier: GPL-2.0
ifneq ($(CONFIG_UNICODE),)
obj-y += unicode.o
endif
obj-$(CONFIG_UNICODE) += utf8data.o
obj-$(CONFIG_UNICODE_NORMALIZATION_KUNIT_TEST) += tests/utf8_kunit.o
unicode-y := utf8-norm.o utf8-core.o
$(obj)/utf8-data.o: $(obj)/utf8data.c
# In the normal build, the checked-in utf8data.c is just shipped.
#
# To generate utf8data.c from UCD, put *.txt files in this directory
# and pass REGENERATE_UTF8DATA=1 from the command line.
ifdef REGENERATE_UTF8DATA
quiet_cmd_utf8data = GEN $@
cmd_utf8data = $< \
-a $(src)/DerivedAge.txt \
-c $(src)/DerivedCombiningClass.txt \
-p $(src)/DerivedCoreProperties.txt \
-d $(src)/UnicodeData.txt \
-f $(src)/CaseFolding.txt \
-n $(src)/NormalizationCorrections.txt \
-t $(src)/NormalizationTest.txt \
-o $@
$(obj)/utf8data.c: $(obj)/mkutf8data $(filter %.txt, $(cmd_utf8data)) FORCE
$(call if_changed,utf8data)
else
$(obj)/utf8data.c: $(src)/utf8data.c_shipped FORCE
$(call if_changed,copy)
endif
targets += utf8data.c
hostprogs += mkutf8data
+57
View File
@@ -0,0 +1,57 @@
The utf8data.c file in this directory is generated from the Unicode
Character Database for version 12.1.0 of the Unicode standard.
The full set of files can be found here:
http://www.unicode.org/Public/12.1.0/ucd/
Individual source links:
https://www.unicode.org/Public/12.1.0/ucd/CaseFolding.txt
https://www.unicode.org/Public/12.1.0/ucd/DerivedAge.txt
https://www.unicode.org/Public/12.1.0/ucd/extracted/DerivedCombiningClass.txt
https://www.unicode.org/Public/12.1.0/ucd/DerivedCoreProperties.txt
https://www.unicode.org/Public/12.1.0/ucd/NormalizationCorrections.txt
https://www.unicode.org/Public/12.1.0/ucd/NormalizationTest.txt
https://www.unicode.org/Public/12.1.0/ucd/UnicodeData.txt
md5sums (verify by running "md5sum -c README.utf8data"):
900e76da1d822a160fd6b8c0b1d70094 CaseFolding.txt
131256380bff4fea8ad4a851616f2f10 DerivedAge.txt
e731a4089b30002144e107e3d6f8d1fa DerivedCombiningClass.txt
a47c9fbd7ff92a9b261ba9831e68778a DerivedCoreProperties.txt
fcab6dad15e440879d92f315978f93d3 NormalizationCorrections.txt
f9ff1c55a60decf436100f791b44aa98 NormalizationTest.txt
755f6af699f8c8d2d958da411f78f6c6 UnicodeData.txt
sha1sums (verify by running "sha1sum -c README.utf8data"):
dc9245f6803c4ac99555c361f5052e0b13eb779b CaseFolding.txt
3281104f237184cdb5d869e86eb8573678ada7da DerivedAge.txt
2f5f995ccb96e0fa84b15151b35d5e2681535175 DerivedCombiningClass.txt
5b8698a3fcd5018e1987f296b02e2c17e696415e DerivedCoreProperties.txt
cd83935fbc012345d8792d2c704f69497e753835 NormalizationCorrections.txt
ea419aae505b337b0d99a83fa83fe58ddff7c19f NormalizationTest.txt
dc973c0fc93d6f09d9ab9f70d1c9f89c447f0526 UnicodeData.txt
To update to the newer version of the Unicode standard, the latest
released version of the UCD can be found here:
http://www.unicode.org/Public/UCD/latest/
Then, build under fs/unicode/ with REGENERATE_UTF8DATA=1:
make REGENERATE_UTF8DATA=1 fs/unicode/
After sanity checking the newly generated utf8data.c file (the
version generated from the 12.1.0 UCD should be 4,109 lines long, and
have a total size of 324k) and/or comparing it with the older version
of utf8data.c_shipped, rename it to utf8data.c_shipped.
If you are a kernel developer updating to a newer version of the
Unicode Character Database, please update this README.utf8data file
with the version of the UCD that was used, the md5sum and sha1sums of
the *.txt files, before checking in the new versions of the utf8data.c
and README.utf8data files.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,3 @@
CONFIG_KUNIT=y
CONFIG_UNICODE=y
CONFIG_UNICODE_NORMALIZATION_KUNIT_TEST=y
+297
View File
@@ -0,0 +1,297 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* KUnit tests for utf-8 support.
*
* Copyright 2017 Collabora Ltd.
*/
#include <linux/unicode.h>
#include <kunit/test.h>
#include "../utf8n.h"
static const struct {
/* UTF-8 strings in this vector _must_ be NULL-terminated. */
unsigned char str[10];
unsigned char dec[10];
} nfdi_test_data[] = {
/* Trivial sequence */
{
/* "ABba" decomposes to itself */
.str = "aBba",
.dec = "aBba",
},
/* Simple equivalent sequences */
{
/* 'VULGAR FRACTION ONE QUARTER' cannot decompose to
'NUMBER 1' + 'FRACTION SLASH' + 'NUMBER 4' on
canonical decomposition */
.str = {0xc2, 0xbc, 0x00},
.dec = {0xc2, 0xbc, 0x00},
},
{
/* 'LATIN SMALL LETTER A WITH DIAERESIS' decomposes to
'LETTER A' + 'COMBINING DIAERESIS' */
.str = {0xc3, 0xa4, 0x00},
.dec = {0x61, 0xcc, 0x88, 0x00},
},
{
/* 'LATIN SMALL LETTER LJ' can't decompose to
'LETTER L' + 'LETTER J' on canonical decomposition */
.str = {0xC7, 0x89, 0x00},
.dec = {0xC7, 0x89, 0x00},
},
{
/* GREEK ANO TELEIA decomposes to MIDDLE DOT */
.str = {0xCE, 0x87, 0x00},
.dec = {0xC2, 0xB7, 0x00}
},
/* Canonical ordering */
{
/* A + 'COMBINING ACUTE ACCENT' + 'COMBINING OGONEK' decomposes
to A + 'COMBINING OGONEK' + 'COMBINING ACUTE ACCENT' */
.str = {0x41, 0xcc, 0x81, 0xcc, 0xa8, 0x0},
.dec = {0x41, 0xcc, 0xa8, 0xcc, 0x81, 0x0},
},
{
/* 'LATIN SMALL LETTER A WITH DIAERESIS' + 'COMBINING OGONEK'
decomposes to
'LETTER A' + 'COMBINING OGONEK' + 'COMBINING DIAERESIS' */
.str = {0xc3, 0xa4, 0xCC, 0xA8, 0x00},
.dec = {0x61, 0xCC, 0xA8, 0xcc, 0x88, 0x00},
},
};
static const struct {
/* UTF-8 strings in this vector _must_ be NULL-terminated. */
unsigned char str[30];
unsigned char ncf[30];
} nfdicf_test_data[] = {
/* Trivial sequences */
{
/* "ABba" folds to lowercase */
.str = {0x41, 0x42, 0x62, 0x61, 0x00},
.ncf = {0x61, 0x62, 0x62, 0x61, 0x00},
},
{
/* All ASCII folds to lower-case */
.str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0.1",
.ncf = "abcdefghijklmnopqrstuvwxyz0.1",
},
{
/* LATIN SMALL LETTER SHARP S folds to
LATIN SMALL LETTER S + LATIN SMALL LETTER S */
.str = {0xc3, 0x9f, 0x00},
.ncf = {0x73, 0x73, 0x00},
},
{
/* LATIN CAPITAL LETTER A WITH RING ABOVE folds to
LATIN SMALL LETTER A + COMBINING RING ABOVE */
.str = {0xC3, 0x85, 0x00},
.ncf = {0x61, 0xcc, 0x8a, 0x00},
},
/* Introduced by UTF-8.0.0. */
/* Cherokee letters are interesting test-cases because they fold
to upper-case. Before 8.0.0, Cherokee lowercase were
undefined, thus, the folding from LC is not stable between
7.0.0 -> 8.0.0, but it is from UC. */
{
/* CHEROKEE SMALL LETTER A folds to CHEROKEE LETTER A */
.str = {0xea, 0xad, 0xb0, 0x00},
.ncf = {0xe1, 0x8e, 0xa0, 0x00},
},
{
/* CHEROKEE SMALL LETTER YE folds to CHEROKEE LETTER YE */
.str = {0xe1, 0x8f, 0xb8, 0x00},
.ncf = {0xe1, 0x8f, 0xb0, 0x00},
},
{
/* OLD HUNGARIAN CAPITAL LETTER AMB folds to
OLD HUNGARIAN SMALL LETTER AMB */
.str = {0xf0, 0x90, 0xb2, 0x83, 0x00},
.ncf = {0xf0, 0x90, 0xb3, 0x83, 0x00},
},
/* Introduced by UTF-9.0.0. */
{
/* OSAGE CAPITAL LETTER CHA folds to
OSAGE SMALL LETTER CHA */
.str = {0xf0, 0x90, 0x92, 0xb5, 0x00},
.ncf = {0xf0, 0x90, 0x93, 0x9d, 0x00},
},
{
/* LATIN CAPITAL LETTER SMALL CAPITAL I folds to
LATIN LETTER SMALL CAPITAL I */
.str = {0xea, 0x9e, 0xae, 0x00},
.ncf = {0xc9, 0xaa, 0x00},
},
/* Introduced by UTF-11.0.0. */
{
/* GEORGIAN SMALL LETTER AN folds to GEORGIAN MTAVRULI
CAPITAL LETTER AN */
.str = {0xe1, 0xb2, 0x90, 0x00},
.ncf = {0xe1, 0x83, 0x90, 0x00},
}
};
static ssize_t utf8len(const struct unicode_map *um, enum utf8_normalization n,
const char *s)
{
return utf8nlen(um, n, s, (size_t)-1);
}
static int utf8cursor(struct utf8cursor *u8c, const struct unicode_map *um,
enum utf8_normalization n, const char *s)
{
return utf8ncursor(u8c, um, n, s, (unsigned int)-1);
}
static void check_utf8_nfdi(struct kunit *test)
{
int i;
struct utf8cursor u8c;
struct unicode_map *um = test->priv;
for (i = 0; i < ARRAY_SIZE(nfdi_test_data); i++) {
int len = strlen(nfdi_test_data[i].str);
int nlen = strlen(nfdi_test_data[i].dec);
int j = 0;
unsigned char c;
int ret;
KUNIT_EXPECT_EQ(test, utf8len(um, UTF8_NFDI, nfdi_test_data[i].str), nlen);
KUNIT_EXPECT_EQ(test, utf8nlen(um, UTF8_NFDI, nfdi_test_data[i].str, len),
nlen);
ret = utf8cursor(&u8c, um, UTF8_NFDI, nfdi_test_data[i].str);
KUNIT_EXPECT_TRUE_MSG(test, ret >= 0, "Can't create cursor\n");
while ((c = utf8byte(&u8c)) > 0) {
KUNIT_EXPECT_EQ_MSG(test, c, nfdi_test_data[i].dec[j],
"Unexpected byte 0x%x should be 0x%x\n",
c, nfdi_test_data[i].dec[j]);
j++;
}
KUNIT_EXPECT_EQ(test, j, nlen);
}
}
static void check_utf8_nfdicf(struct kunit *test)
{
int i;
struct utf8cursor u8c;
struct unicode_map *um = test->priv;
for (i = 0; i < ARRAY_SIZE(nfdicf_test_data); i++) {
int len = strlen(nfdicf_test_data[i].str);
int nlen = strlen(nfdicf_test_data[i].ncf);
int j = 0;
int ret;
unsigned char c;
KUNIT_EXPECT_EQ(test, utf8len(um, UTF8_NFDICF, nfdicf_test_data[i].str),
nlen);
KUNIT_EXPECT_EQ(test, utf8nlen(um, UTF8_NFDICF, nfdicf_test_data[i].str, len),
nlen);
ret = utf8cursor(&u8c, um, UTF8_NFDICF, nfdicf_test_data[i].str);
KUNIT_EXPECT_TRUE_MSG(test, ret >= 0, "Can't create cursor\n");
while ((c = utf8byte(&u8c)) > 0) {
KUNIT_EXPECT_EQ_MSG(test, c, nfdicf_test_data[i].ncf[j],
"Unexpected byte 0x%x should be 0x%x\n",
c, nfdicf_test_data[i].ncf[j]);
j++;
}
KUNIT_EXPECT_EQ(test, j, nlen);
}
}
static void check_utf8_comparisons(struct kunit *test)
{
int i;
struct unicode_map *um = test->priv;
for (i = 0; i < ARRAY_SIZE(nfdi_test_data); i++) {
const struct qstr s1 = {.name = nfdi_test_data[i].str,
.len = sizeof(nfdi_test_data[i].str)};
const struct qstr s2 = {.name = nfdi_test_data[i].dec,
.len = sizeof(nfdi_test_data[i].dec)};
/* strncmp returns 0 when strings are equal */
KUNIT_EXPECT_TRUE_MSG(test, utf8_strncmp(um, &s1, &s2) == 0,
"%s %s comparison mismatch\n", s1.name, s2.name);
}
for (i = 0; i < ARRAY_SIZE(nfdicf_test_data); i++) {
const struct qstr s1 = {.name = nfdicf_test_data[i].str,
.len = sizeof(nfdicf_test_data[i].str)};
const struct qstr s2 = {.name = nfdicf_test_data[i].ncf,
.len = sizeof(nfdicf_test_data[i].ncf)};
/* strncasecmp returns 0 when strings are equal */
KUNIT_EXPECT_TRUE_MSG(test, utf8_strncasecmp(um, &s1, &s2) == 0,
"%s %s comparison mismatch\n", s1.name, s2.name);
}
}
static void check_supported_versions(struct kunit *test)
{
struct unicode_map *um = test->priv;
/* Unicode 7.0.0 should be supported. */
KUNIT_EXPECT_TRUE(test, utf8version_is_supported(um, UNICODE_AGE(7, 0, 0)));
/* Unicode 9.0.0 should be supported. */
KUNIT_EXPECT_TRUE(test, utf8version_is_supported(um, UNICODE_AGE(9, 0, 0)));
/* Unicode 1x.0.0 (the latest version) should be supported. */
KUNIT_EXPECT_TRUE(test, utf8version_is_supported(um, UTF8_LATEST));
/* Next versions don't exist. */
KUNIT_EXPECT_FALSE(test, utf8version_is_supported(um, UNICODE_AGE(13, 0, 0)));
KUNIT_EXPECT_FALSE(test, utf8version_is_supported(um, UNICODE_AGE(0, 0, 0)));
KUNIT_EXPECT_FALSE(test, utf8version_is_supported(um, UNICODE_AGE(-1, -1, -1)));
}
static struct kunit_case unicode_normalization_test_cases[] = {
KUNIT_CASE(check_supported_versions),
KUNIT_CASE(check_utf8_comparisons),
KUNIT_CASE(check_utf8_nfdicf),
KUNIT_CASE(check_utf8_nfdi),
{}
};
static int init_test_ucd(struct kunit *test)
{
struct unicode_map *um = utf8_load(UTF8_LATEST);
test->priv = um;
KUNIT_EXPECT_EQ_MSG(test, IS_ERR(um), 0,
"%s: Unable to load utf8 table.\n", __func__);
return 0;
}
static void exit_test_ucd(struct kunit *test)
{
utf8_unload(test->priv);
}
static struct kunit_suite unicode_normalization_test_suite = {
.name = "unicode_normalization",
.test_cases = unicode_normalization_test_cases,
.init = init_test_ucd,
.exit = exit_test_ucd,
};
kunit_test_suite(unicode_normalization_test_suite);
MODULE_AUTHOR("Gabriel Krisman Bertazi <krisman@collabora.co.uk>");
MODULE_DESCRIPTION("KUnit tests for utf-8 support.");
MODULE_LICENSE("GPL");
+242
View File
@@ -0,0 +1,242 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/parser.h>
#include <linux/errno.h>
#include <linux/stringhash.h>
#include "utf8n.h"
int utf8_validate(const struct unicode_map *um, const struct qstr *str)
{
if (utf8nlen(um, UTF8_NFDI, str->name, str->len) < 0)
return -1;
return 0;
}
EXPORT_SYMBOL(utf8_validate);
int utf8_strncmp(const struct unicode_map *um,
const struct qstr *s1, const struct qstr *s2)
{
struct utf8cursor cur1, cur2;
int c1, c2;
if (utf8ncursor(&cur1, um, UTF8_NFDI, s1->name, s1->len) < 0)
return -EINVAL;
if (utf8ncursor(&cur2, um, UTF8_NFDI, s2->name, s2->len) < 0)
return -EINVAL;
do {
c1 = utf8byte(&cur1);
c2 = utf8byte(&cur2);
if (c1 < 0 || c2 < 0)
return -EINVAL;
if (c1 != c2)
return 1;
} while (c1);
return 0;
}
EXPORT_SYMBOL(utf8_strncmp);
int utf8_strncasecmp(const struct unicode_map *um,
const struct qstr *s1, const struct qstr *s2)
{
struct utf8cursor cur1, cur2;
int c1, c2;
if (utf8ncursor(&cur1, um, UTF8_NFDICF, s1->name, s1->len) < 0)
return -EINVAL;
if (utf8ncursor(&cur2, um, UTF8_NFDICF, s2->name, s2->len) < 0)
return -EINVAL;
do {
c1 = utf8byte(&cur1);
c2 = utf8byte(&cur2);
if (c1 < 0 || c2 < 0)
return -EINVAL;
if (c1 != c2)
return 1;
} while (c1);
return 0;
}
EXPORT_SYMBOL(utf8_strncasecmp);
/* String cf is expected to be a valid UTF-8 casefolded
* string.
*/
int utf8_strncasecmp_folded(const struct unicode_map *um,
const struct qstr *cf,
const struct qstr *s1)
{
struct utf8cursor cur1;
int c1, c2;
int i = 0;
if (utf8ncursor(&cur1, um, UTF8_NFDICF, s1->name, s1->len) < 0)
return -EINVAL;
do {
c1 = utf8byte(&cur1);
c2 = cf->name[i++];
if (c1 < 0)
return -EINVAL;
if (c1 != c2)
return 1;
} while (c1);
return 0;
}
EXPORT_SYMBOL(utf8_strncasecmp_folded);
int utf8_casefold(const struct unicode_map *um, const struct qstr *str,
unsigned char *dest, size_t dlen)
{
struct utf8cursor cur;
size_t nlen = 0;
if (utf8ncursor(&cur, um, UTF8_NFDICF, str->name, str->len) < 0)
return -EINVAL;
for (nlen = 0; nlen < dlen; nlen++) {
int c = utf8byte(&cur);
dest[nlen] = c;
if (!c)
return nlen;
if (c == -1)
break;
}
return -EINVAL;
}
EXPORT_SYMBOL(utf8_casefold);
int utf8_casefold_hash(const struct unicode_map *um, const void *salt,
struct qstr *str)
{
struct utf8cursor cur;
int c;
unsigned long hash = init_name_hash(salt);
if (utf8ncursor(&cur, um, UTF8_NFDICF, str->name, str->len) < 0)
return -EINVAL;
while ((c = utf8byte(&cur))) {
if (c < 0)
return -EINVAL;
hash = partial_name_hash((unsigned char)c, hash);
}
str->hash = end_name_hash(hash);
return 0;
}
EXPORT_SYMBOL(utf8_casefold_hash);
int utf8_normalize(const struct unicode_map *um, const struct qstr *str,
unsigned char *dest, size_t dlen)
{
struct utf8cursor cur;
ssize_t nlen = 0;
if (utf8ncursor(&cur, um, UTF8_NFDI, str->name, str->len) < 0)
return -EINVAL;
for (nlen = 0; nlen < dlen; nlen++) {
int c = utf8byte(&cur);
dest[nlen] = c;
if (!c)
return nlen;
if (c == -1)
break;
}
return -EINVAL;
}
EXPORT_SYMBOL(utf8_normalize);
static const struct utf8data *find_table_version(const struct utf8data *table,
size_t nr_entries, unsigned int version)
{
size_t i = nr_entries - 1;
while (version < table[i].maxage)
i--;
if (version > table[i].maxage)
return NULL;
return &table[i];
}
struct unicode_map *utf8_load(unsigned int version)
{
struct unicode_map *um;
um = kzalloc_obj(struct unicode_map);
if (!um)
return ERR_PTR(-ENOMEM);
um->version = version;
um->tables = symbol_request(utf8_data_table);
if (!um->tables)
goto out_free_um;
if (!utf8version_is_supported(um, version))
goto out_symbol_put;
um->ntab[UTF8_NFDI] = find_table_version(um->tables->utf8nfdidata,
um->tables->utf8nfdidata_size, um->version);
if (!um->ntab[UTF8_NFDI])
goto out_symbol_put;
um->ntab[UTF8_NFDICF] = find_table_version(um->tables->utf8nfdicfdata,
um->tables->utf8nfdicfdata_size, um->version);
if (!um->ntab[UTF8_NFDICF])
goto out_symbol_put;
return um;
out_symbol_put:
symbol_put(utf8_data_table);
out_free_um:
kfree(um);
return ERR_PTR(-EINVAL);
}
EXPORT_SYMBOL(utf8_load);
void utf8_unload(struct unicode_map *um)
{
if (um) {
symbol_put(utf8_data_table);
kfree(um);
}
}
EXPORT_SYMBOL(utf8_unload);
/**
* utf8_parse_version - Parse a UTF-8 version number from a string
*
* @version: input string
*
* Returns the parsed version on success, negative code on error
*/
int utf8_parse_version(char *version)
{
substring_t args[3];
unsigned int maj, min, rev;
static const struct match_token token[] = {
{1, "%d.%d.%d"},
{0, NULL}
};
if (match_token(version, token, args) != 1)
return -EINVAL;
if (match_int(&args[0], &maj) || match_int(&args[1], &min) ||
match_int(&args[2], &rev))
return -EINVAL;
return UNICODE_AGE(maj, min, rev);
}
EXPORT_SYMBOL(utf8_parse_version);
+594
View File
@@ -0,0 +1,594 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014 SGI.
* All rights reserved.
*/
#include "utf8n.h"
int utf8version_is_supported(const struct unicode_map *um, unsigned int version)
{
int i = um->tables->utf8agetab_size - 1;
while (i >= 0 && um->tables->utf8agetab[i] != 0) {
if (version == um->tables->utf8agetab[i])
return 1;
i--;
}
return 0;
}
/*
* UTF-8 valid ranges.
*
* The UTF-8 encoding spreads the bits of a 32bit word over several
* bytes. This table gives the ranges that can be held and how they'd
* be represented.
*
* 0x00000000 0x0000007F: 0xxxxxxx
* 0x00000000 0x000007FF: 110xxxxx 10xxxxxx
* 0x00000000 0x0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
* 0x00000000 0x001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
* 0x00000000 0x03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
* 0x00000000 0x7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
*
* There is an additional requirement on UTF-8, in that only the
* shortest representation of a 32bit value is to be used. A decoder
* must not decode sequences that do not satisfy this requirement.
* Thus the allowed ranges have a lower bound.
*
* 0x00000000 0x0000007F: 0xxxxxxx
* 0x00000080 0x000007FF: 110xxxxx 10xxxxxx
* 0x00000800 0x0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
* 0x00010000 0x001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
* 0x00200000 0x03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
* 0x04000000 0x7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
*
* Actual unicode characters are limited to the range 0x0 - 0x10FFFF,
* 17 planes of 65536 values. This limits the sequences actually seen
* even more, to just the following.
*
* 0 - 0x7F: 0 - 0x7F
* 0x80 - 0x7FF: 0xC2 0x80 - 0xDF 0xBF
* 0x800 - 0xFFFF: 0xE0 0xA0 0x80 - 0xEF 0xBF 0xBF
* 0x10000 - 0x10FFFF: 0xF0 0x90 0x80 0x80 - 0xF4 0x8F 0xBF 0xBF
*
* Within those ranges the surrogates 0xD800 - 0xDFFF are not allowed.
*
* Note that the longest sequence seen with valid usage is 4 bytes,
* the same a single UTF-32 character. This makes the UTF-8
* representation of Unicode strictly smaller than UTF-32.
*
* The shortest sequence requirement was introduced by:
* Corrigendum #1: UTF-8 Shortest Form
* It can be found here:
* http://www.unicode.org/versions/corrigendum1.html
*
*/
/*
* Return the number of bytes used by the current UTF-8 sequence.
* Assumes the input points to the first byte of a valid UTF-8
* sequence.
*/
static inline int utf8clen(const char *s)
{
unsigned char c = *s;
return 1 + (c >= 0xC0) + (c >= 0xE0) + (c >= 0xF0);
}
/*
* Decode a 3-byte UTF-8 sequence.
*/
static unsigned int
utf8decode3(const char *str)
{
unsigned int uc;
uc = *str++ & 0x0F;
uc <<= 6;
uc |= *str++ & 0x3F;
uc <<= 6;
uc |= *str++ & 0x3F;
return uc;
}
/*
* Encode a 3-byte UTF-8 sequence.
*/
static int
utf8encode3(char *str, unsigned int val)
{
str[2] = (val & 0x3F) | 0x80;
val >>= 6;
str[1] = (val & 0x3F) | 0x80;
val >>= 6;
str[0] = val | 0xE0;
return 3;
}
/*
* utf8trie_t
*
* A compact binary tree, used to decode UTF-8 characters.
*
* Internal nodes are one byte for the node itself, and up to three
* bytes for an offset into the tree. The first byte contains the
* following information:
* NEXTBYTE - flag - advance to next byte if set
* BITNUM - 3 bit field - the bit number to tested
* OFFLEN - 2 bit field - number of bytes in the offset
* if offlen == 0 (non-branching node)
* RIGHTPATH - 1 bit field - set if the following node is for the
* right-hand path (tested bit is set)
* TRIENODE - 1 bit field - set if the following node is an internal
* node, otherwise it is a leaf node
* if offlen != 0 (branching node)
* LEFTNODE - 1 bit field - set if the left-hand node is internal
* RIGHTNODE - 1 bit field - set if the right-hand node is internal
*
* Due to the way utf8 works, there cannot be branching nodes with
* NEXTBYTE set, and moreover those nodes always have a righthand
* descendant.
*/
typedef const unsigned char utf8trie_t;
#define BITNUM 0x07
#define NEXTBYTE 0x08
#define OFFLEN 0x30
#define OFFLEN_SHIFT 4
#define RIGHTPATH 0x40
#define TRIENODE 0x80
#define RIGHTNODE 0x40
#define LEFTNODE 0x80
/*
* utf8leaf_t
*
* The leaves of the trie are embedded in the trie, and so the same
* underlying datatype: unsigned char.
*
* leaf[0]: The unicode version, stored as a generation number that is
* an index into ->utf8agetab[]. With this we can filter code
* points based on the unicode version in which they were
* defined. The CCC of a non-defined code point is 0.
* leaf[1]: Canonical Combining Class. During normalization, we need
* to do a stable sort into ascending order of all characters
* with a non-zero CCC that occur between two characters with
* a CCC of 0, or at the begin or end of a string.
* The unicode standard guarantees that all CCC values are
* between 0 and 254 inclusive, which leaves 255 available as
* a special value.
* Code points with CCC 0 are known as stoppers.
* leaf[2]: Decomposition. If leaf[1] == 255, then leaf[2] is the
* start of a NUL-terminated string that is the decomposition
* of the character.
* The CCC of a decomposable character is the same as the CCC
* of the first character of its decomposition.
* Some characters decompose as the empty string: these are
* characters with the Default_Ignorable_Code_Point property.
* These do affect normalization, as they all have CCC 0.
*
* The decompositions in the trie have been fully expanded, with the
* exception of Hangul syllables, which are decomposed algorithmically.
*
* Casefolding, if applicable, is also done using decompositions.
*
* The trie is constructed in such a way that leaves exist for all
* UTF-8 sequences that match the criteria from the "UTF-8 valid
* ranges" comment above, and only for those sequences. Therefore a
* lookup in the trie can be used to validate the UTF-8 input.
*/
typedef const unsigned char utf8leaf_t;
#define LEAF_GEN(LEAF) ((LEAF)[0])
#define LEAF_CCC(LEAF) ((LEAF)[1])
#define LEAF_STR(LEAF) ((const char *)((LEAF) + 2))
#define MINCCC (0)
#define MAXCCC (254)
#define STOPPER (0)
#define DECOMPOSE (255)
/* Marker for hangul syllable decomposition. */
#define HANGUL ((char)(255))
/* Size of the synthesized leaf used for Hangul syllable decomposition. */
#define UTF8HANGULLEAF (12)
/*
* Hangul decomposition (algorithm from Section 3.12 of Unicode 6.3.0)
*
* AC00;<Hangul Syllable, First>;Lo;0;L;;;;;N;;;;;
* D7A3;<Hangul Syllable, Last>;Lo;0;L;;;;;N;;;;;
*
* SBase = 0xAC00
* LBase = 0x1100
* VBase = 0x1161
* TBase = 0x11A7
* LCount = 19
* VCount = 21
* TCount = 28
* NCount = 588 (VCount * TCount)
* SCount = 11172 (LCount * NCount)
*
* Decomposition:
* SIndex = s - SBase
*
* LV (Canonical/Full)
* LIndex = SIndex / NCount
* VIndex = (Sindex % NCount) / TCount
* LPart = LBase + LIndex
* VPart = VBase + VIndex
*
* LVT (Canonical)
* LVIndex = (SIndex / TCount) * TCount
* TIndex = (Sindex % TCount)
* LVPart = SBase + LVIndex
* TPart = TBase + TIndex
*
* LVT (Full)
* LIndex = SIndex / NCount
* VIndex = (Sindex % NCount) / TCount
* TIndex = (Sindex % TCount)
* LPart = LBase + LIndex
* VPart = VBase + VIndex
* if (TIndex == 0) {
* d = <LPart, VPart>
* } else {
* TPart = TBase + TIndex
* d = <LPart, TPart, VPart>
* }
*/
/* Constants */
#define SB (0xAC00)
#define LB (0x1100)
#define VB (0x1161)
#define TB (0x11A7)
#define LC (19)
#define VC (21)
#define TC (28)
#define NC (VC * TC)
#define SC (LC * NC)
/* Algorithmic decomposition of hangul syllable. */
static utf8leaf_t *
utf8hangul(const char *str, unsigned char *hangul)
{
unsigned int si;
unsigned int li;
unsigned int vi;
unsigned int ti;
unsigned char *h;
/* Calculate the SI, LI, VI, and TI values. */
si = utf8decode3(str) - SB;
li = si / NC;
vi = (si % NC) / TC;
ti = si % TC;
/* Fill in base of leaf. */
h = hangul;
LEAF_GEN(h) = 2;
LEAF_CCC(h) = DECOMPOSE;
h += 2;
/* Add LPart, a 3-byte UTF-8 sequence. */
h += utf8encode3((char *)h, li + LB);
/* Add VPart, a 3-byte UTF-8 sequence. */
h += utf8encode3((char *)h, vi + VB);
/* Add TPart if required, also a 3-byte UTF-8 sequence. */
if (ti)
h += utf8encode3((char *)h, ti + TB);
/* Terminate string. */
h[0] = '\0';
return hangul;
}
/*
* Use trie to scan s, touching at most len bytes.
* Returns the leaf if one exists, NULL otherwise.
*
* A non-NULL return guarantees that the UTF-8 sequence starting at s
* is well-formed and corresponds to a known unicode code point. The
* shorthand for this will be "is valid UTF-8 unicode".
*/
static utf8leaf_t *utf8nlookup(const struct unicode_map *um,
enum utf8_normalization n, unsigned char *hangul, const char *s,
size_t len)
{
utf8trie_t *trie = um->tables->utf8data + um->ntab[n]->offset;
int offlen;
int offset;
int mask;
int node;
if (len == 0)
return NULL;
node = 1;
while (node) {
offlen = (*trie & OFFLEN) >> OFFLEN_SHIFT;
if (*trie & NEXTBYTE) {
if (--len == 0)
return NULL;
s++;
}
mask = 1 << (*trie & BITNUM);
if (*s & mask) {
/* Right leg */
if (offlen) {
/* Right node at offset of trie */
node = (*trie & RIGHTNODE);
offset = trie[offlen];
while (--offlen) {
offset <<= 8;
offset |= trie[offlen];
}
trie += offset;
} else if (*trie & RIGHTPATH) {
/* Right node after this node */
node = (*trie & TRIENODE);
trie++;
} else {
/* No right node. */
return NULL;
}
} else {
/* Left leg */
if (offlen) {
/* Left node after this node. */
node = (*trie & LEFTNODE);
trie += offlen + 1;
} else if (*trie & RIGHTPATH) {
/* No left node. */
return NULL;
} else {
/* Left node after this node */
node = (*trie & TRIENODE);
trie++;
}
}
}
/*
* Hangul decomposition is done algorithmically. These are the
* codepoints >= 0xAC00 and <= 0xD7A3. Their UTF-8 encoding is
* always 3 bytes long, so s has been advanced twice, and the
* start of the sequence is at s-2.
*/
if (LEAF_CCC(trie) == DECOMPOSE && LEAF_STR(trie)[0] == HANGUL)
trie = utf8hangul(s - 2, hangul);
return trie;
}
/*
* Use trie to scan s.
* Returns the leaf if one exists, NULL otherwise.
*
* Forwards to utf8nlookup().
*/
static utf8leaf_t *utf8lookup(const struct unicode_map *um,
enum utf8_normalization n, unsigned char *hangul, const char *s)
{
return utf8nlookup(um, n, hangul, s, (size_t)-1);
}
/*
* Length of the normalization of s, touch at most len bytes.
* Return -1 if s is not valid UTF-8 unicode.
*/
ssize_t utf8nlen(const struct unicode_map *um, enum utf8_normalization n,
const char *s, size_t len)
{
utf8leaf_t *leaf;
size_t ret = 0;
unsigned char hangul[UTF8HANGULLEAF];
while (len && *s) {
leaf = utf8nlookup(um, n, hangul, s, len);
if (!leaf)
return -1;
if (um->tables->utf8agetab[LEAF_GEN(leaf)] >
um->ntab[n]->maxage)
ret += utf8clen(s);
else if (LEAF_CCC(leaf) == DECOMPOSE)
ret += strlen(LEAF_STR(leaf));
else
ret += utf8clen(s);
len -= utf8clen(s);
s += utf8clen(s);
}
return ret;
}
/*
* Set up an utf8cursor for use by utf8byte().
*
* u8c : pointer to cursor.
* data : const struct utf8data to use for normalization.
* s : string.
* len : length of s.
*
* Returns -1 on error, 0 on success.
*/
int utf8ncursor(struct utf8cursor *u8c, const struct unicode_map *um,
enum utf8_normalization n, const char *s, size_t len)
{
if (!s)
return -1;
u8c->um = um;
u8c->n = n;
u8c->s = s;
u8c->p = NULL;
u8c->ss = NULL;
u8c->sp = NULL;
u8c->len = len;
u8c->slen = 0;
u8c->ccc = STOPPER;
u8c->nccc = STOPPER;
/* Check we didn't clobber the maximum length. */
if (u8c->len != len)
return -1;
/* The first byte of s may not be an utf8 continuation. */
if (len > 0 && (*s & 0xC0) == 0x80)
return -1;
return 0;
}
/*
* Get one byte from the normalized form of the string described by u8c.
*
* Returns the byte cast to an unsigned char on succes, and -1 on failure.
*
* The cursor keeps track of the location in the string in u8c->s.
* When a character is decomposed, the current location is stored in
* u8c->p, and u8c->s is set to the start of the decomposition. Note
* that bytes from a decomposition do not count against u8c->len.
*
* Characters are emitted if they match the current CCC in u8c->ccc.
* Hitting end-of-string while u8c->ccc == STOPPER means we're done,
* and the function returns 0 in that case.
*
* Sorting by CCC is done by repeatedly scanning the string. The
* values of u8c->s and u8c->p are stored in u8c->ss and u8c->sp at
* the start of the scan. The first pass finds the lowest CCC to be
* emitted and stores it in u8c->nccc, the second pass emits the
* characters with this CCC and finds the next lowest CCC. This limits
* the number of passes to 1 + the number of different CCCs in the
* sequence being scanned.
*
* Therefore:
* u8c->p != NULL -> a decomposition is being scanned.
* u8c->ss != NULL -> this is a repeating scan.
* u8c->ccc == -1 -> this is the first scan of a repeating scan.
*/
int utf8byte(struct utf8cursor *u8c)
{
utf8leaf_t *leaf;
int ccc;
for (;;) {
/* Check for the end of a decomposed character. */
if (u8c->p && *u8c->s == '\0') {
u8c->s = u8c->p;
u8c->p = NULL;
}
/* Check for end-of-string. */
if (!u8c->p && (u8c->len == 0 || *u8c->s == '\0')) {
/* There is no next byte. */
if (u8c->ccc == STOPPER)
return 0;
/* End-of-string during a scan counts as a stopper. */
ccc = STOPPER;
goto ccc_mismatch;
} else if ((*u8c->s & 0xC0) == 0x80) {
/* This is a continuation of the current character. */
if (!u8c->p)
u8c->len--;
return (unsigned char)*u8c->s++;
}
/* Look up the data for the current character. */
if (u8c->p) {
leaf = utf8lookup(u8c->um, u8c->n, u8c->hangul, u8c->s);
} else {
leaf = utf8nlookup(u8c->um, u8c->n, u8c->hangul,
u8c->s, u8c->len);
}
/* No leaf found implies that the input is a binary blob. */
if (!leaf)
return -1;
ccc = LEAF_CCC(leaf);
/* Characters that are too new have CCC 0. */
if (u8c->um->tables->utf8agetab[LEAF_GEN(leaf)] >
u8c->um->ntab[u8c->n]->maxage) {
ccc = STOPPER;
} else if (ccc == DECOMPOSE) {
u8c->len -= utf8clen(u8c->s);
u8c->p = u8c->s + utf8clen(u8c->s);
u8c->s = LEAF_STR(leaf);
/* Empty decomposition implies CCC 0. */
if (*u8c->s == '\0') {
if (u8c->ccc == STOPPER)
continue;
ccc = STOPPER;
goto ccc_mismatch;
}
leaf = utf8lookup(u8c->um, u8c->n, u8c->hangul, u8c->s);
if (!leaf)
return -1;
ccc = LEAF_CCC(leaf);
}
/*
* If this is not a stopper, then see if it updates
* the next canonical class to be emitted.
*/
if (ccc != STOPPER && u8c->ccc < ccc && ccc < u8c->nccc)
u8c->nccc = ccc;
/*
* Return the current byte if this is the current
* combining class.
*/
if (ccc == u8c->ccc) {
if (!u8c->p)
u8c->len--;
return (unsigned char)*u8c->s++;
}
/* Current combining class mismatch. */
ccc_mismatch:
if (u8c->nccc == STOPPER) {
/*
* Scan forward for the first canonical class
* to be emitted. Save the position from
* which to restart.
*/
u8c->ccc = MINCCC - 1;
u8c->nccc = ccc;
u8c->sp = u8c->p;
u8c->ss = u8c->s;
u8c->slen = u8c->len;
if (!u8c->p)
u8c->len -= utf8clen(u8c->s);
u8c->s += utf8clen(u8c->s);
} else if (ccc != STOPPER) {
/* Not a stopper, and not the ccc we're emitting. */
if (!u8c->p)
u8c->len -= utf8clen(u8c->s);
u8c->s += utf8clen(u8c->s);
} else if (u8c->nccc != MAXCCC + 1) {
/* At a stopper, restart for next ccc. */
u8c->ccc = u8c->nccc;
u8c->nccc = MAXCCC + 1;
u8c->s = u8c->ss;
u8c->p = u8c->sp;
u8c->len = u8c->slen;
} else {
/* All done, proceed from here. */
u8c->ccc = STOPPER;
u8c->nccc = STOPPER;
u8c->sp = NULL;
u8c->ss = NULL;
u8c->slen = 0;
}
}
}
#if IS_MODULE(CONFIG_UNICODE_NORMALIZATION_KUNIT_TEST)
EXPORT_SYMBOL_GPL(utf8version_is_supported);
EXPORT_SYMBOL_GPL(utf8nlen);
EXPORT_SYMBOL_GPL(utf8ncursor);
EXPORT_SYMBOL_GPL(utf8byte);
#endif
File diff suppressed because it is too large Load Diff
+83
View File
@@ -0,0 +1,83 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2014 SGI.
* All rights reserved.
*/
#ifndef UTF8NORM_H
#define UTF8NORM_H
#include <linux/types.h>
#include <linux/export.h>
#include <linux/string.h>
#include <linux/module.h>
#include <linux/unicode.h>
int utf8version_is_supported(const struct unicode_map *um, unsigned int version);
/*
* Determine the length of the normalized from of the string,
* excluding any terminating NULL byte.
* Returns 0 if only ignorable code points are present.
* Returns -1 if the input is not valid UTF-8.
*/
ssize_t utf8nlen(const struct unicode_map *um, enum utf8_normalization n,
const char *s, size_t len);
/* Needed in struct utf8cursor below. */
#define UTF8HANGULLEAF (12)
/*
* Cursor structure used by the normalizer.
*/
struct utf8cursor {
const struct unicode_map *um;
enum utf8_normalization n;
const char *s;
const char *p;
const char *ss;
const char *sp;
unsigned int len;
unsigned int slen;
short int ccc;
short int nccc;
unsigned char hangul[UTF8HANGULLEAF];
};
/*
* Initialize a utf8cursor to normalize a string.
* Returns 0 on success.
* Returns -1 on failure.
*/
int utf8ncursor(struct utf8cursor *u8c, const struct unicode_map *um,
enum utf8_normalization n, const char *s, size_t len);
/*
* Get the next byte in the normalization.
* Returns a value > 0 && < 256 on success.
* Returns 0 when the end of the normalization is reached.
* Returns -1 if the string being normalized is not valid UTF-8.
*/
extern int utf8byte(struct utf8cursor *u8c);
struct utf8data {
unsigned int maxage;
unsigned int offset;
};
struct utf8data_table {
const unsigned int *utf8agetab;
int utf8agetab_size;
const struct utf8data *utf8nfdicfdata;
int utf8nfdicfdata_size;
const struct utf8data *utf8nfdidata;
int utf8nfdidata_size;
const unsigned char *utf8data;
};
extern const struct utf8data_table utf8_data_table;
#endif /* UTF8NORM_H */