Linux filesystem development
 help / color / mirror / Atom feed
* [PATCH v1 0/4] exfat: memory optimisations and stringent integrity checks for up-case table
@ 2026-04-28 23:50 David Timber
  2026-04-28 23:50 ` [PATCH v1 1/4] exfat: refactor nls.c (tables) David Timber
                   ` (3 more replies)
  0 siblings, 4 replies; 16+ messages in thread
From: David Timber @ 2026-04-28 23:50 UTC (permalink / raw)
  To: Namjae Jeon, Sungjong Seo, Yuezhang Mo; +Cc: linux-fsdevel, David Timber

Reroll v1:

 - Mark the volume read-only if the up-case table seems damaged
 - Inline exfat_lookup_upcase_ptable()
 - Fix uninitialised variable(ret) in exfat_load_upcase_table()

Revised coverletter:

This round of patches introduces efficient upcase table implementation
to exFAT as well as more stringent check against the upcase table when
mounting the volume.

Link: https://github.com/exfatprogs/exfatprogs/pull/341

**Theoretical trade offs**

The use of "paged upcase table" saw runtime memory footprint reduced
from 131KB to 8KB(+some slab overhead). Other trade offs include:

 - Reduced .rodata usage from 5KB to 2KB at the cost of added delay in
   module initialisation for populating the default upcase table
 - Slight performance increase from reduced cache misses when traversing
   directories with entries with a wide range of unicode code points

**Tests**

Upcase table test cases are available in my
repo(https://github.com/dxdxdt/gists/tree/master/writeups/exfat):

 - exfat-default-upcase:
   - -c option: uncompresses and prints the default compressed upcase
     table
   - -p option: tests if exfat_populate_upcase_ptable() produces the
     same default upcase table included in the kernel prior to the
     patches
   - -pc option: generates .rodata included in fs/exfat/tables.c
 - exfat-test-upcase: brute forces combinations(2^32) of unicode upcase
   conversion to ensure that no regression is introduced. The test
   finishes within 24 hours ;)
 - exfat-profile-upcase.sh and exfat-print-all-allowed: profile kernel
   memory use per mount and the worse-case directory traversal, entries
   filled with a range of unicode code points. Run with `make profile`

**Profiling**

When tested with 16 volumes(`make profile`), ~1MB saving in memory
usage per volume is observed(available mem 122528 -> 140884).

**NOTES**

Errors found in the "recommended" upcase table are outlined in
fs/exfat/tables.c.

The value of EXFAT_UPTBL_PAGESIZE(512 __u16 entries or 1024 bytes) is
determined to be the best based on the data:

```
  $ ./exfat-default-upcase > test-control
  ...
  entries: 874 (1748 bytes)
  non-empty pages: 8 (nb_page * pagesize * 2 = 512 * 8 * 2 = 8192 bytes)
```

The program reports that the total of 8 "pages" of 1KiB are used by the
table.

Regression test:

```

  (with the mainline exfat module)
  $ ./exfat-test-upcase /PATH/TO/EXFAT/MOUNTPOINT > a
  ...

  (with the patched exfat module)
  $ ./exfat-test-upcase /PATH/TO/EXFAT/MOUNTPOINT > b
  ...

  $ diff a b
```

Let me know if the test programs need to be added in the source tree
(perhaps in /tools/testing/selftests/filesystems/exfat/ ??)

David Timber (4):
  exfat: refactor nls.c (tables)
  exfat: use upcase_ptable and upcase_range_info to reduce memory
    footprint
  exfat: add default_upcase option (read-only)
  exfat: more pedantic upcase table validity check

 fs/exfat/Makefile   |   2 +-
 fs/exfat/exfat_fs.h |  56 ++-
 fs/exfat/nls.c      | 552 +++++------------------------
 fs/exfat/super.c    |  10 +
 fs/exfat/tables.c   | 845 ++++++++++++++++++++++++++++++++++++++++++++
 fs/exfat/upcase.c   |  70 ++++
 6 files changed, 1073 insertions(+), 462 deletions(-)
 create mode 100644 fs/exfat/tables.c
 create mode 100644 fs/exfat/upcase.c

-- 
2.53.0.1.ga224b40d3f.dirty


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v1 1/4] exfat: refactor nls.c (tables)
  2026-04-28 23:50 [PATCH v1 0/4] exfat: memory optimisations and stringent integrity checks for up-case table David Timber
@ 2026-04-28 23:50 ` David Timber
  2026-04-30  7:53   ` Yuezhang.Mo
  2026-04-28 23:50 ` [PATCH v1 2/4] exfat: use upcase_ptable and upcase_range_info to reduce memory footprint David Timber
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 16+ messages in thread
From: David Timber @ 2026-04-28 23:50 UTC (permalink / raw)
  To: Namjae Jeon, Sungjong Seo, Yuezhang Mo; +Cc: linux-fsdevel, David Timber

Relocate large data tables in nls.c to tables.c.

Add const to the return type of exfat_wstrchr() for consistency.

No functional change.

Signed-off-by: David Timber <dxdt@dev.snart.me>
---
 fs/exfat/Makefile   |   2 +-
 fs/exfat/exfat_fs.h |   8 +
 fs/exfat/nls.c      | 404 +-------------------------------------------
 fs/exfat/tables.c   | 393 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 409 insertions(+), 398 deletions(-)
 create mode 100644 fs/exfat/tables.c

diff --git a/fs/exfat/Makefile b/fs/exfat/Makefile
index ed51926a4971..63e8e91c2ff3 100644
--- a/fs/exfat/Makefile
+++ b/fs/exfat/Makefile
@@ -5,4 +5,4 @@
 obj-$(CONFIG_EXFAT_FS) += exfat.o
 
 exfat-y	:= inode.o namei.o dir.o super.o fatent.o cache.o nls.o misc.o \
-	   file.o balloc.o
+	   file.o balloc.o tables.o
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 89ef5368277f..3c99be1ecdc8 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -608,6 +608,14 @@ void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
 		__exfat_fs_error(sb, __ratelimit(&EXFAT_SB(sb)->ratelimit), \
 		fmt, ## args)
 
+/* exfat/tables.c */
+/* Upcase table macro */
+#define EXFAT_NUM_UPCASE	(2918)
+#define EXFAT_UTBL_COUNT	(0x10000)
+
+extern const unsigned short exfat_uni_def_upcase[EXFAT_NUM_UPCASE];
+extern const unsigned short exfat_bad_uni_chars[];
+
 /* expand to pr_*() with prefix */
 #define exfat_err(sb, fmt, ...)						\
 	pr_err("exFAT-fs (%s): " fmt "\n", (sb)->s_id, ##__VA_ARGS__)
diff --git a/fs/exfat/nls.c b/fs/exfat/nls.c
index 055447edcf9a..7e85d15ffc0b 100644
--- a/fs/exfat/nls.c
+++ b/fs/exfat/nls.c
@@ -11,397 +11,6 @@
 #include "exfat_raw.h"
 #include "exfat_fs.h"
 
-/* Upcase table macro */
-#define EXFAT_NUM_UPCASE	(2918)
-#define UTBL_COUNT		(0x10000)
-
-/*
- * Upcase table in compressed format (7.2.5.1 Recommended Up-case Table
- * in exfat specification, See:
- * https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification).
- */
-static const unsigned short uni_def_upcase[EXFAT_NUM_UPCASE] = {
-	0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
-	0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
-	0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
-	0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
-	0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
-	0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
-	0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
-	0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
-	0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
-	0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
-	0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
-	0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
-	0x0060, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
-	0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
-	0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
-	0x0058, 0x0059, 0x005a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
-	0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
-	0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
-	0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
-	0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
-	0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
-	0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
-	0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
-	0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
-	0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
-	0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
-	0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
-	0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
-	0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
-	0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
-	0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00f7,
-	0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x0178,
-	0x0100, 0x0100, 0x0102, 0x0102, 0x0104, 0x0104, 0x0106, 0x0106,
-	0x0108, 0x0108, 0x010a, 0x010a, 0x010c, 0x010c, 0x010e, 0x010e,
-	0x0110, 0x0110, 0x0112, 0x0112, 0x0114, 0x0114, 0x0116, 0x0116,
-	0x0118, 0x0118, 0x011a, 0x011a, 0x011c, 0x011c, 0x011e, 0x011e,
-	0x0120, 0x0120, 0x0122, 0x0122, 0x0124, 0x0124, 0x0126, 0x0126,
-	0x0128, 0x0128, 0x012a, 0x012a, 0x012c, 0x012c, 0x012e, 0x012e,
-	0x0130, 0x0131, 0x0132, 0x0132, 0x0134, 0x0134, 0x0136, 0x0136,
-	0x0138, 0x0139, 0x0139, 0x013b, 0x013b, 0x013d, 0x013d, 0x013f,
-	0x013f, 0x0141, 0x0141, 0x0143, 0x0143, 0x0145, 0x0145, 0x0147,
-	0x0147, 0x0149, 0x014a, 0x014a, 0x014c, 0x014c, 0x014e, 0x014e,
-	0x0150, 0x0150, 0x0152, 0x0152, 0x0154, 0x0154, 0x0156, 0x0156,
-	0x0158, 0x0158, 0x015a, 0x015a, 0x015c, 0x015c, 0x015e, 0x015e,
-	0x0160, 0x0160, 0x0162, 0x0162, 0x0164, 0x0164, 0x0166, 0x0166,
-	0x0168, 0x0168, 0x016a, 0x016a, 0x016c, 0x016c, 0x016e, 0x016e,
-	0x0170, 0x0170, 0x0172, 0x0172, 0x0174, 0x0174, 0x0176, 0x0176,
-	0x0178, 0x0179, 0x0179, 0x017b, 0x017b, 0x017d, 0x017d, 0x017f,
-	0x0243, 0x0181, 0x0182, 0x0182, 0x0184, 0x0184, 0x0186, 0x0187,
-	0x0187, 0x0189, 0x018a, 0x018b, 0x018b, 0x018d, 0x018e, 0x018f,
-	0x0190, 0x0191, 0x0191, 0x0193, 0x0194, 0x01f6, 0x0196, 0x0197,
-	0x0198, 0x0198, 0x023d, 0x019b, 0x019c, 0x019d, 0x0220, 0x019f,
-	0x01a0, 0x01a0, 0x01a2, 0x01a2, 0x01a4, 0x01a4, 0x01a6, 0x01a7,
-	0x01a7, 0x01a9, 0x01aa, 0x01ab, 0x01ac, 0x01ac, 0x01ae, 0x01af,
-	0x01af, 0x01b1, 0x01b2, 0x01b3, 0x01b3, 0x01b5, 0x01b5, 0x01b7,
-	0x01b8, 0x01b8, 0x01ba, 0x01bb, 0x01bc, 0x01bc, 0x01be, 0x01f7,
-	0x01c0, 0x01c1, 0x01c2, 0x01c3, 0x01c4, 0x01c5, 0x01c4, 0x01c7,
-	0x01c8, 0x01c7, 0x01ca, 0x01cb, 0x01ca, 0x01cd, 0x01cd, 0x01cf,
-	0x01cf, 0x01d1, 0x01d1, 0x01d3, 0x01d3, 0x01d5, 0x01d5, 0x01d7,
-	0x01d7, 0x01d9, 0x01d9, 0x01db, 0x01db, 0x018e, 0x01de, 0x01de,
-	0x01e0, 0x01e0, 0x01e2, 0x01e2, 0x01e4, 0x01e4, 0x01e6, 0x01e6,
-	0x01e8, 0x01e8, 0x01ea, 0x01ea, 0x01ec, 0x01ec, 0x01ee, 0x01ee,
-	0x01f0, 0x01f1, 0x01f2, 0x01f1, 0x01f4, 0x01f4, 0x01f6, 0x01f7,
-	0x01f8, 0x01f8, 0x01fa, 0x01fa, 0x01fc, 0x01fc, 0x01fe, 0x01fe,
-	0x0200, 0x0200, 0x0202, 0x0202, 0x0204, 0x0204, 0x0206, 0x0206,
-	0x0208, 0x0208, 0x020a, 0x020a, 0x020c, 0x020c, 0x020e, 0x020e,
-	0x0210, 0x0210, 0x0212, 0x0212, 0x0214, 0x0214, 0x0216, 0x0216,
-	0x0218, 0x0218, 0x021a, 0x021a, 0x021c, 0x021c, 0x021e, 0x021e,
-	0x0220, 0x0221, 0x0222, 0x0222, 0x0224, 0x0224, 0x0226, 0x0226,
-	0x0228, 0x0228, 0x022a, 0x022a, 0x022c, 0x022c, 0x022e, 0x022e,
-	0x0230, 0x0230, 0x0232, 0x0232, 0x0234, 0x0235, 0x0236, 0x0237,
-	0x0238, 0x0239, 0x2c65, 0x023b, 0x023b, 0x023d, 0x2c66, 0x023f,
-	0x0240, 0x0241, 0x0241, 0x0243, 0x0244, 0x0245, 0x0246, 0x0246,
-	0x0248, 0x0248, 0x024a, 0x024a, 0x024c, 0x024c, 0x024e, 0x024e,
-	0x0250, 0x0251, 0x0252, 0x0181, 0x0186, 0x0255, 0x0189, 0x018a,
-	0x0258, 0x018f, 0x025a, 0x0190, 0x025c, 0x025d, 0x025e, 0x025f,
-	0x0193, 0x0261, 0x0262, 0x0194, 0x0264, 0x0265, 0x0266, 0x0267,
-	0x0197, 0x0196, 0x026a, 0x2c62, 0x026c, 0x026d, 0x026e, 0x019c,
-	0x0270, 0x0271, 0x019d, 0x0273, 0x0274, 0x019f, 0x0276, 0x0277,
-	0x0278, 0x0279, 0x027a, 0x027b, 0x027c, 0x2c64, 0x027e, 0x027f,
-	0x01a6, 0x0281, 0x0282, 0x01a9, 0x0284, 0x0285, 0x0286, 0x0287,
-	0x01ae, 0x0244, 0x01b1, 0x01b2, 0x0245, 0x028d, 0x028e, 0x028f,
-	0x0290, 0x0291, 0x01b7, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297,
-	0x0298, 0x0299, 0x029a, 0x029b, 0x029c, 0x029d, 0x029e, 0x029f,
-	0x02a0, 0x02a1, 0x02a2, 0x02a3, 0x02a4, 0x02a5, 0x02a6, 0x02a7,
-	0x02a8, 0x02a9, 0x02aa, 0x02ab, 0x02ac, 0x02ad, 0x02ae, 0x02af,
-	0x02b0, 0x02b1, 0x02b2, 0x02b3, 0x02b4, 0x02b5, 0x02b6, 0x02b7,
-	0x02b8, 0x02b9, 0x02ba, 0x02bb, 0x02bc, 0x02bd, 0x02be, 0x02bf,
-	0x02c0, 0x02c1, 0x02c2, 0x02c3, 0x02c4, 0x02c5, 0x02c6, 0x02c7,
-	0x02c8, 0x02c9, 0x02ca, 0x02cb, 0x02cc, 0x02cd, 0x02ce, 0x02cf,
-	0x02d0, 0x02d1, 0x02d2, 0x02d3, 0x02d4, 0x02d5, 0x02d6, 0x02d7,
-	0x02d8, 0x02d9, 0x02da, 0x02db, 0x02dc, 0x02dd, 0x02de, 0x02df,
-	0x02e0, 0x02e1, 0x02e2, 0x02e3, 0x02e4, 0x02e5, 0x02e6, 0x02e7,
-	0x02e8, 0x02e9, 0x02ea, 0x02eb, 0x02ec, 0x02ed, 0x02ee, 0x02ef,
-	0x02f0, 0x02f1, 0x02f2, 0x02f3, 0x02f4, 0x02f5, 0x02f6, 0x02f7,
-	0x02f8, 0x02f9, 0x02fa, 0x02fb, 0x02fc, 0x02fd, 0x02fe, 0x02ff,
-	0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307,
-	0x0308, 0x0309, 0x030a, 0x030b, 0x030c, 0x030d, 0x030e, 0x030f,
-	0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317,
-	0x0318, 0x0319, 0x031a, 0x031b, 0x031c, 0x031d, 0x031e, 0x031f,
-	0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327,
-	0x0328, 0x0329, 0x032a, 0x032b, 0x032c, 0x032d, 0x032e, 0x032f,
-	0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337,
-	0x0338, 0x0339, 0x033a, 0x033b, 0x033c, 0x033d, 0x033e, 0x033f,
-	0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347,
-	0x0348, 0x0349, 0x034a, 0x034b, 0x034c, 0x034d, 0x034e, 0x034f,
-	0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357,
-	0x0358, 0x0359, 0x035a, 0x035b, 0x035c, 0x035d, 0x035e, 0x035f,
-	0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367,
-	0x0368, 0x0369, 0x036a, 0x036b, 0x036c, 0x036d, 0x036e, 0x036f,
-	0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377,
-	0x0378, 0x0379, 0x037a, 0x03fd, 0x03fe, 0x03ff, 0x037e, 0x037f,
-	0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0386, 0x0387,
-	0x0388, 0x0389, 0x038a, 0x038b, 0x038c, 0x038d, 0x038e, 0x038f,
-	0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
-	0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f,
-	0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7,
-	0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x0386, 0x0388, 0x0389, 0x038a,
-	0x03b0, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
-	0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f,
-	0x03a0, 0x03a1, 0x03a3, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7,
-	0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x038c, 0x038e, 0x038f, 0x03cf,
-	0x03d0, 0x03d1, 0x03d2, 0x03d3, 0x03d4, 0x03d5, 0x03d6, 0x03d7,
-	0x03d8, 0x03d8, 0x03da, 0x03da, 0x03dc, 0x03dc, 0x03de, 0x03de,
-	0x03e0, 0x03e0, 0x03e2, 0x03e2, 0x03e4, 0x03e4, 0x03e6, 0x03e6,
-	0x03e8, 0x03e8, 0x03ea, 0x03ea, 0x03ec, 0x03ec, 0x03ee, 0x03ee,
-	0x03f0, 0x03f1, 0x03f9, 0x03f3, 0x03f4, 0x03f5, 0x03f6, 0x03f7,
-	0x03f7, 0x03f9, 0x03fa, 0x03fa, 0x03fc, 0x03fd, 0x03fe, 0x03ff,
-	0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407,
-	0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x040d, 0x040e, 0x040f,
-	0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
-	0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f,
-	0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
-	0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f,
-	0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
-	0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f,
-	0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
-	0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f,
-	0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407,
-	0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x040d, 0x040e, 0x040f,
-	0x0460, 0x0460, 0x0462, 0x0462, 0x0464, 0x0464, 0x0466, 0x0466,
-	0x0468, 0x0468, 0x046a, 0x046a, 0x046c, 0x046c, 0x046e, 0x046e,
-	0x0470, 0x0470, 0x0472, 0x0472, 0x0474, 0x0474, 0x0476, 0x0476,
-	0x0478, 0x0478, 0x047a, 0x047a, 0x047c, 0x047c, 0x047e, 0x047e,
-	0x0480, 0x0480, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487,
-	0x0488, 0x0489, 0x048a, 0x048a, 0x048c, 0x048c, 0x048e, 0x048e,
-	0x0490, 0x0490, 0x0492, 0x0492, 0x0494, 0x0494, 0x0496, 0x0496,
-	0x0498, 0x0498, 0x049a, 0x049a, 0x049c, 0x049c, 0x049e, 0x049e,
-	0x04a0, 0x04a0, 0x04a2, 0x04a2, 0x04a4, 0x04a4, 0x04a6, 0x04a6,
-	0x04a8, 0x04a8, 0x04aa, 0x04aa, 0x04ac, 0x04ac, 0x04ae, 0x04ae,
-	0x04b0, 0x04b0, 0x04b2, 0x04b2, 0x04b4, 0x04b4, 0x04b6, 0x04b6,
-	0x04b8, 0x04b8, 0x04ba, 0x04ba, 0x04bc, 0x04bc, 0x04be, 0x04be,
-	0x04c0, 0x04c1, 0x04c1, 0x04c3, 0x04c3, 0x04c5, 0x04c5, 0x04c7,
-	0x04c7, 0x04c9, 0x04c9, 0x04cb, 0x04cb, 0x04cd, 0x04cd, 0x04c0,
-	0x04d0, 0x04d0, 0x04d2, 0x04d2, 0x04d4, 0x04d4, 0x04d6, 0x04d6,
-	0x04d8, 0x04d8, 0x04da, 0x04da, 0x04dc, 0x04dc, 0x04de, 0x04de,
-	0x04e0, 0x04e0, 0x04e2, 0x04e2, 0x04e4, 0x04e4, 0x04e6, 0x04e6,
-	0x04e8, 0x04e8, 0x04ea, 0x04ea, 0x04ec, 0x04ec, 0x04ee, 0x04ee,
-	0x04f0, 0x04f0, 0x04f2, 0x04f2, 0x04f4, 0x04f4, 0x04f6, 0x04f6,
-	0x04f8, 0x04f8, 0x04fa, 0x04fa, 0x04fc, 0x04fc, 0x04fe, 0x04fe,
-	0x0500, 0x0500, 0x0502, 0x0502, 0x0504, 0x0504, 0x0506, 0x0506,
-	0x0508, 0x0508, 0x050a, 0x050a, 0x050c, 0x050c, 0x050e, 0x050e,
-	0x0510, 0x0510, 0x0512, 0x0512, 0x0514, 0x0515, 0x0516, 0x0517,
-	0x0518, 0x0519, 0x051a, 0x051b, 0x051c, 0x051d, 0x051e, 0x051f,
-	0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527,
-	0x0528, 0x0529, 0x052a, 0x052b, 0x052c, 0x052d, 0x052e, 0x052f,
-	0x0530, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537,
-	0x0538, 0x0539, 0x053a, 0x053b, 0x053c, 0x053d, 0x053e, 0x053f,
-	0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547,
-	0x0548, 0x0549, 0x054a, 0x054b, 0x054c, 0x054d, 0x054e, 0x054f,
-	0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556, 0x0557,
-	0x0558, 0x0559, 0x055a, 0x055b, 0x055c, 0x055d, 0x055e, 0x055f,
-	0x0560, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537,
-	0x0538, 0x0539, 0x053a, 0x053b, 0x053c, 0x053d, 0x053e, 0x053f,
-	0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547,
-	0x0548, 0x0549, 0x054a, 0x054b, 0x054c, 0x054d, 0x054e, 0x054f,
-	0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556, 0xffff,
-	0x17f6, 0x2c63, 0x1d7e, 0x1d7f, 0x1d80, 0x1d81, 0x1d82, 0x1d83,
-	0x1d84, 0x1d85, 0x1d86, 0x1d87, 0x1d88, 0x1d89, 0x1d8a, 0x1d8b,
-	0x1d8c, 0x1d8d, 0x1d8e, 0x1d8f, 0x1d90, 0x1d91, 0x1d92, 0x1d93,
-	0x1d94, 0x1d95, 0x1d96, 0x1d97, 0x1d98, 0x1d99, 0x1d9a, 0x1d9b,
-	0x1d9c, 0x1d9d, 0x1d9e, 0x1d9f, 0x1da0, 0x1da1, 0x1da2, 0x1da3,
-	0x1da4, 0x1da5, 0x1da6, 0x1da7, 0x1da8, 0x1da9, 0x1daa, 0x1dab,
-	0x1dac, 0x1dad, 0x1dae, 0x1daf, 0x1db0, 0x1db1, 0x1db2, 0x1db3,
-	0x1db4, 0x1db5, 0x1db6, 0x1db7, 0x1db8, 0x1db9, 0x1dba, 0x1dbb,
-	0x1dbc, 0x1dbd, 0x1dbe, 0x1dbf, 0x1dc0, 0x1dc1, 0x1dc2, 0x1dc3,
-	0x1dc4, 0x1dc5, 0x1dc6, 0x1dc7, 0x1dc8, 0x1dc9, 0x1dca, 0x1dcb,
-	0x1dcc, 0x1dcd, 0x1dce, 0x1dcf, 0x1dd0, 0x1dd1, 0x1dd2, 0x1dd3,
-	0x1dd4, 0x1dd5, 0x1dd6, 0x1dd7, 0x1dd8, 0x1dd9, 0x1dda, 0x1ddb,
-	0x1ddc, 0x1ddd, 0x1dde, 0x1ddf, 0x1de0, 0x1de1, 0x1de2, 0x1de3,
-	0x1de4, 0x1de5, 0x1de6, 0x1de7, 0x1de8, 0x1de9, 0x1dea, 0x1deb,
-	0x1dec, 0x1ded, 0x1dee, 0x1def, 0x1df0, 0x1df1, 0x1df2, 0x1df3,
-	0x1df4, 0x1df5, 0x1df6, 0x1df7, 0x1df8, 0x1df9, 0x1dfa, 0x1dfb,
-	0x1dfc, 0x1dfd, 0x1dfe, 0x1dff, 0x1e00, 0x1e00, 0x1e02, 0x1e02,
-	0x1e04, 0x1e04, 0x1e06, 0x1e06, 0x1e08, 0x1e08, 0x1e0a, 0x1e0a,
-	0x1e0c, 0x1e0c, 0x1e0e, 0x1e0e, 0x1e10, 0x1e10, 0x1e12, 0x1e12,
-	0x1e14, 0x1e14, 0x1e16, 0x1e16, 0x1e18, 0x1e18, 0x1e1a, 0x1e1a,
-	0x1e1c, 0x1e1c, 0x1e1e, 0x1e1e, 0x1e20, 0x1e20, 0x1e22, 0x1e22,
-	0x1e24, 0x1e24, 0x1e26, 0x1e26, 0x1e28, 0x1e28, 0x1e2a, 0x1e2a,
-	0x1e2c, 0x1e2c, 0x1e2e, 0x1e2e, 0x1e30, 0x1e30, 0x1e32, 0x1e32,
-	0x1e34, 0x1e34, 0x1e36, 0x1e36, 0x1e38, 0x1e38, 0x1e3a, 0x1e3a,
-	0x1e3c, 0x1e3c, 0x1e3e, 0x1e3e, 0x1e40, 0x1e40, 0x1e42, 0x1e42,
-	0x1e44, 0x1e44, 0x1e46, 0x1e46, 0x1e48, 0x1e48, 0x1e4a, 0x1e4a,
-	0x1e4c, 0x1e4c, 0x1e4e, 0x1e4e, 0x1e50, 0x1e50, 0x1e52, 0x1e52,
-	0x1e54, 0x1e54, 0x1e56, 0x1e56, 0x1e58, 0x1e58, 0x1e5a, 0x1e5a,
-	0x1e5c, 0x1e5c, 0x1e5e, 0x1e5e, 0x1e60, 0x1e60, 0x1e62, 0x1e62,
-	0x1e64, 0x1e64, 0x1e66, 0x1e66, 0x1e68, 0x1e68, 0x1e6a, 0x1e6a,
-	0x1e6c, 0x1e6c, 0x1e6e, 0x1e6e, 0x1e70, 0x1e70, 0x1e72, 0x1e72,
-	0x1e74, 0x1e74, 0x1e76, 0x1e76, 0x1e78, 0x1e78, 0x1e7a, 0x1e7a,
-	0x1e7c, 0x1e7c, 0x1e7e, 0x1e7e, 0x1e80, 0x1e80, 0x1e82, 0x1e82,
-	0x1e84, 0x1e84, 0x1e86, 0x1e86, 0x1e88, 0x1e88, 0x1e8a, 0x1e8a,
-	0x1e8c, 0x1e8c, 0x1e8e, 0x1e8e, 0x1e90, 0x1e90, 0x1e92, 0x1e92,
-	0x1e94, 0x1e94, 0x1e96, 0x1e97, 0x1e98, 0x1e99, 0x1e9a, 0x1e9b,
-	0x1e9c, 0x1e9d, 0x1e9e, 0x1e9f, 0x1ea0, 0x1ea0, 0x1ea2, 0x1ea2,
-	0x1ea4, 0x1ea4, 0x1ea6, 0x1ea6, 0x1ea8, 0x1ea8, 0x1eaa, 0x1eaa,
-	0x1eac, 0x1eac, 0x1eae, 0x1eae, 0x1eb0, 0x1eb0, 0x1eb2, 0x1eb2,
-	0x1eb4, 0x1eb4, 0x1eb6, 0x1eb6, 0x1eb8, 0x1eb8, 0x1eba, 0x1eba,
-	0x1ebc, 0x1ebc, 0x1ebe, 0x1ebe, 0x1ec0, 0x1ec0, 0x1ec2, 0x1ec2,
-	0x1ec4, 0x1ec4, 0x1ec6, 0x1ec6, 0x1ec8, 0x1ec8, 0x1eca, 0x1eca,
-	0x1ecc, 0x1ecc, 0x1ece, 0x1ece, 0x1ed0, 0x1ed0, 0x1ed2, 0x1ed2,
-	0x1ed4, 0x1ed4, 0x1ed6, 0x1ed6, 0x1ed8, 0x1ed8, 0x1eda, 0x1eda,
-	0x1edc, 0x1edc, 0x1ede, 0x1ede, 0x1ee0, 0x1ee0, 0x1ee2, 0x1ee2,
-	0x1ee4, 0x1ee4, 0x1ee6, 0x1ee6, 0x1ee8, 0x1ee8, 0x1eea, 0x1eea,
-	0x1eec, 0x1eec, 0x1eee, 0x1eee, 0x1ef0, 0x1ef0, 0x1ef2, 0x1ef2,
-	0x1ef4, 0x1ef4, 0x1ef6, 0x1ef6, 0x1ef8, 0x1ef8, 0x1efa, 0x1efb,
-	0x1efc, 0x1efd, 0x1efe, 0x1eff, 0x1f08, 0x1f09, 0x1f0a, 0x1f0b,
-	0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, 0x1f08, 0x1f09, 0x1f0a, 0x1f0b,
-	0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, 0x1f18, 0x1f19, 0x1f1a, 0x1f1b,
-	0x1f1c, 0x1f1d, 0x1f16, 0x1f17, 0x1f18, 0x1f19, 0x1f1a, 0x1f1b,
-	0x1f1c, 0x1f1d, 0x1f1e, 0x1f1f, 0x1f28, 0x1f29, 0x1f2a, 0x1f2b,
-	0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, 0x1f28, 0x1f29, 0x1f2a, 0x1f2b,
-	0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, 0x1f38, 0x1f39, 0x1f3a, 0x1f3b,
-	0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, 0x1f38, 0x1f39, 0x1f3a, 0x1f3b,
-	0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, 0x1f48, 0x1f49, 0x1f4a, 0x1f4b,
-	0x1f4c, 0x1f4d, 0x1f46, 0x1f47, 0x1f48, 0x1f49, 0x1f4a, 0x1f4b,
-	0x1f4c, 0x1f4d, 0x1f4e, 0x1f4f, 0x1f50, 0x1f59, 0x1f52, 0x1f5b,
-	0x1f54, 0x1f5d, 0x1f56, 0x1f5f, 0x1f58, 0x1f59, 0x1f5a, 0x1f5b,
-	0x1f5c, 0x1f5d, 0x1f5e, 0x1f5f, 0x1f68, 0x1f69, 0x1f6a, 0x1f6b,
-	0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, 0x1f68, 0x1f69, 0x1f6a, 0x1f6b,
-	0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, 0x1fba, 0x1fbb, 0x1fc8, 0x1fc9,
-	0x1fca, 0x1fcb, 0x1fda, 0x1fdb, 0x1ff8, 0x1ff9, 0x1fea, 0x1feb,
-	0x1ffa, 0x1ffb, 0x1f7e, 0x1f7f, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b,
-	0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b,
-	0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b,
-	0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b,
-	0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab,
-	0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab,
-	0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fb8, 0x1fb9, 0x1fb2, 0x1fbc,
-	0x1fb4, 0x1fb5, 0x1fb6, 0x1fb7, 0x1fb8, 0x1fb9, 0x1fba, 0x1fbb,
-	0x1fbc, 0x1fbd, 0x1fbe, 0x1fbf, 0x1fc0, 0x1fc1, 0x1fc2, 0x1fc3,
-	0x1fc4, 0x1fc5, 0x1fc6, 0x1fc7, 0x1fc8, 0x1fc9, 0x1fca, 0x1fcb,
-	0x1fc3, 0x1fcd, 0x1fce, 0x1fcf, 0x1fd8, 0x1fd9, 0x1fd2, 0x1fd3,
-	0x1fd4, 0x1fd5, 0x1fd6, 0x1fd7, 0x1fd8, 0x1fd9, 0x1fda, 0x1fdb,
-	0x1fdc, 0x1fdd, 0x1fde, 0x1fdf, 0x1fe8, 0x1fe9, 0x1fe2, 0x1fe3,
-	0x1fe4, 0x1fec, 0x1fe6, 0x1fe7, 0x1fe8, 0x1fe9, 0x1fea, 0x1feb,
-	0x1fec, 0x1fed, 0x1fee, 0x1fef, 0x1ff0, 0x1ff1, 0x1ff2, 0x1ff3,
-	0x1ff4, 0x1ff5, 0x1ff6, 0x1ff7, 0x1ff8, 0x1ff9, 0x1ffa, 0x1ffb,
-	0x1ff3, 0x1ffd, 0x1ffe, 0x1fff, 0x2000, 0x2001, 0x2002, 0x2003,
-	0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200a, 0x200b,
-	0x200c, 0x200d, 0x200e, 0x200f, 0x2010, 0x2011, 0x2012, 0x2013,
-	0x2014, 0x2015, 0x2016, 0x2017, 0x2018, 0x2019, 0x201a, 0x201b,
-	0x201c, 0x201d, 0x201e, 0x201f, 0x2020, 0x2021, 0x2022, 0x2023,
-	0x2024, 0x2025, 0x2026, 0x2027, 0x2028, 0x2029, 0x202a, 0x202b,
-	0x202c, 0x202d, 0x202e, 0x202f, 0x2030, 0x2031, 0x2032, 0x2033,
-	0x2034, 0x2035, 0x2036, 0x2037, 0x2038, 0x2039, 0x203a, 0x203b,
-	0x203c, 0x203d, 0x203e, 0x203f, 0x2040, 0x2041, 0x2042, 0x2043,
-	0x2044, 0x2045, 0x2046, 0x2047, 0x2048, 0x2049, 0x204a, 0x204b,
-	0x204c, 0x204d, 0x204e, 0x204f, 0x2050, 0x2051, 0x2052, 0x2053,
-	0x2054, 0x2055, 0x2056, 0x2057, 0x2058, 0x2059, 0x205a, 0x205b,
-	0x205c, 0x205d, 0x205e, 0x205f, 0x2060, 0x2061, 0x2062, 0x2063,
-	0x2064, 0x2065, 0x2066, 0x2067, 0x2068, 0x2069, 0x206a, 0x206b,
-	0x206c, 0x206d, 0x206e, 0x206f, 0x2070, 0x2071, 0x2072, 0x2073,
-	0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, 0x207a, 0x207b,
-	0x207c, 0x207d, 0x207e, 0x207f, 0x2080, 0x2081, 0x2082, 0x2083,
-	0x2084, 0x2085, 0x2086, 0x2087, 0x2088, 0x2089, 0x208a, 0x208b,
-	0x208c, 0x208d, 0x208e, 0x208f, 0x2090, 0x2091, 0x2092, 0x2093,
-	0x2094, 0x2095, 0x2096, 0x2097, 0x2098, 0x2099, 0x209a, 0x209b,
-	0x209c, 0x209d, 0x209e, 0x209f, 0x20a0, 0x20a1, 0x20a2, 0x20a3,
-	0x20a4, 0x20a5, 0x20a6, 0x20a7, 0x20a8, 0x20a9, 0x20aa, 0x20ab,
-	0x20ac, 0x20ad, 0x20ae, 0x20af, 0x20b0, 0x20b1, 0x20b2, 0x20b3,
-	0x20b4, 0x20b5, 0x20b6, 0x20b7, 0x20b8, 0x20b9, 0x20ba, 0x20bb,
-	0x20bc, 0x20bd, 0x20be, 0x20bf, 0x20c0, 0x20c1, 0x20c2, 0x20c3,
-	0x20c4, 0x20c5, 0x20c6, 0x20c7, 0x20c8, 0x20c9, 0x20ca, 0x20cb,
-	0x20cc, 0x20cd, 0x20ce, 0x20cf, 0x20d0, 0x20d1, 0x20d2, 0x20d3,
-	0x20d4, 0x20d5, 0x20d6, 0x20d7, 0x20d8, 0x20d9, 0x20da, 0x20db,
-	0x20dc, 0x20dd, 0x20de, 0x20df, 0x20e0, 0x20e1, 0x20e2, 0x20e3,
-	0x20e4, 0x20e5, 0x20e6, 0x20e7, 0x20e8, 0x20e9, 0x20ea, 0x20eb,
-	0x20ec, 0x20ed, 0x20ee, 0x20ef, 0x20f0, 0x20f1, 0x20f2, 0x20f3,
-	0x20f4, 0x20f5, 0x20f6, 0x20f7, 0x20f8, 0x20f9, 0x20fa, 0x20fb,
-	0x20fc, 0x20fd, 0x20fe, 0x20ff, 0x2100, 0x2101, 0x2102, 0x2103,
-	0x2104, 0x2105, 0x2106, 0x2107, 0x2108, 0x2109, 0x210a, 0x210b,
-	0x210c, 0x210d, 0x210e, 0x210f, 0x2110, 0x2111, 0x2112, 0x2113,
-	0x2114, 0x2115, 0x2116, 0x2117, 0x2118, 0x2119, 0x211a, 0x211b,
-	0x211c, 0x211d, 0x211e, 0x211f, 0x2120, 0x2121, 0x2122, 0x2123,
-	0x2124, 0x2125, 0x2126, 0x2127, 0x2128, 0x2129, 0x212a, 0x212b,
-	0x212c, 0x212d, 0x212e, 0x212f, 0x2130, 0x2131, 0x2132, 0x2133,
-	0x2134, 0x2135, 0x2136, 0x2137, 0x2138, 0x2139, 0x213a, 0x213b,
-	0x213c, 0x213d, 0x213e, 0x213f, 0x2140, 0x2141, 0x2142, 0x2143,
-	0x2144, 0x2145, 0x2146, 0x2147, 0x2148, 0x2149, 0x214a, 0x214b,
-	0x214c, 0x214d, 0x2132, 0x214f, 0x2150, 0x2151, 0x2152, 0x2153,
-	0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a, 0x215b,
-	0x215c, 0x215d, 0x215e, 0x215f, 0x2160, 0x2161, 0x2162, 0x2163,
-	0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216a, 0x216b,
-	0x216c, 0x216d, 0x216e, 0x216f, 0x2160, 0x2161, 0x2162, 0x2163,
-	0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216a, 0x216b,
-	0x216c, 0x216d, 0x216e, 0x216f, 0x2180, 0x2181, 0x2182, 0x2183,
-	0x2183, 0xffff, 0x034b, 0x24b6, 0x24b7, 0x24b8, 0x24b9, 0x24ba,
-	0x24bb, 0x24bc, 0x24bd, 0x24be, 0x24bf, 0x24c0, 0x24c1, 0x24c2,
-	0x24c3, 0x24c4, 0x24c5, 0x24c6, 0x24c7, 0x24c8, 0x24c9, 0x24ca,
-	0x24cb, 0x24cc, 0x24cd, 0x24ce, 0x24cf, 0xffff, 0x0746, 0x2c00,
-	0x2c01, 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x2c08,
-	0x2c09, 0x2c0a, 0x2c0b, 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x2c10,
-	0x2c11, 0x2c12, 0x2c13, 0x2c14, 0x2c15, 0x2c16, 0x2c17, 0x2c18,
-	0x2c19, 0x2c1a, 0x2c1b, 0x2c1c, 0x2c1d, 0x2c1e, 0x2c1f, 0x2c20,
-	0x2c21, 0x2c22, 0x2c23, 0x2c24, 0x2c25, 0x2c26, 0x2c27, 0x2c28,
-	0x2c29, 0x2c2a, 0x2c2b, 0x2c2c, 0x2c2d, 0x2c2e, 0x2c5f, 0x2c60,
-	0x2c60, 0x2c62, 0x2c63, 0x2c64, 0x2c65, 0x2c66, 0x2c67, 0x2c67,
-	0x2c69, 0x2c69, 0x2c6b, 0x2c6b, 0x2c6d, 0x2c6e, 0x2c6f, 0x2c70,
-	0x2c71, 0x2c72, 0x2c73, 0x2c74, 0x2c75, 0x2c75, 0x2c77, 0x2c78,
-	0x2c79, 0x2c7a, 0x2c7b, 0x2c7c, 0x2c7d, 0x2c7e, 0x2c7f, 0x2c80,
-	0x2c80, 0x2c82, 0x2c82, 0x2c84, 0x2c84, 0x2c86, 0x2c86, 0x2c88,
-	0x2c88, 0x2c8a, 0x2c8a, 0x2c8c, 0x2c8c, 0x2c8e, 0x2c8e, 0x2c90,
-	0x2c90, 0x2c92, 0x2c92, 0x2c94, 0x2c94, 0x2c96, 0x2c96, 0x2c98,
-	0x2c98, 0x2c9a, 0x2c9a, 0x2c9c, 0x2c9c, 0x2c9e, 0x2c9e, 0x2ca0,
-	0x2ca0, 0x2ca2, 0x2ca2, 0x2ca4, 0x2ca4, 0x2ca6, 0x2ca6, 0x2ca8,
-	0x2ca8, 0x2caa, 0x2caa, 0x2cac, 0x2cac, 0x2cae, 0x2cae, 0x2cb0,
-	0x2cb0, 0x2cb2, 0x2cb2, 0x2cb4, 0x2cb4, 0x2cb6, 0x2cb6, 0x2cb8,
-	0x2cb8, 0x2cba, 0x2cba, 0x2cbc, 0x2cbc, 0x2cbe, 0x2cbe, 0x2cc0,
-	0x2cc0, 0x2cc2, 0x2cc2, 0x2cc4, 0x2cc4, 0x2cc6, 0x2cc6, 0x2cc8,
-	0x2cc8, 0x2cca, 0x2cca, 0x2ccc, 0x2ccc, 0x2cce, 0x2cce, 0x2cd0,
-	0x2cd0, 0x2cd2, 0x2cd2, 0x2cd4, 0x2cd4, 0x2cd6, 0x2cd6, 0x2cd8,
-	0x2cd8, 0x2cda, 0x2cda, 0x2cdc, 0x2cdc, 0x2cde, 0x2cde, 0x2ce0,
-	0x2ce0, 0x2ce2, 0x2ce2, 0x2ce4, 0x2ce5, 0x2ce6, 0x2ce7, 0x2ce8,
-	0x2ce9, 0x2cea, 0x2ceb, 0x2cec, 0x2ced, 0x2cee, 0x2cef, 0x2cf0,
-	0x2cf1, 0x2cf2, 0x2cf3, 0x2cf4, 0x2cf5, 0x2cf6, 0x2cf7, 0x2cf8,
-	0x2cf9, 0x2cfa, 0x2cfb, 0x2cfc, 0x2cfd, 0x2cfe, 0x2cff, 0x10a0,
-	0x10a1, 0x10a2, 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7, 0x10a8,
-	0x10a9, 0x10aa, 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af, 0x10b0,
-	0x10b1, 0x10b2, 0x10b3, 0x10b4, 0x10b5, 0x10b6, 0x10b7, 0x10b8,
-	0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd, 0x10be, 0x10bf, 0x10c0,
-	0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5, 0xffff, 0xd21b, 0xff21,
-	0xff22, 0xff23, 0xff24, 0xff25, 0xff26, 0xff27, 0xff28, 0xff29,
-	0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e, 0xff2f, 0xff30, 0xff31,
-	0xff32, 0xff33, 0xff34, 0xff35, 0xff36, 0xff37, 0xff38, 0xff39,
-	0xff3a, 0xff5b, 0xff5c, 0xff5d, 0xff5e, 0xff5f, 0xff60, 0xff61,
-	0xff62, 0xff63, 0xff64, 0xff65, 0xff66, 0xff67, 0xff68, 0xff69,
-	0xff6a, 0xff6b, 0xff6c, 0xff6d, 0xff6e, 0xff6f, 0xff70, 0xff71,
-	0xff72, 0xff73, 0xff74, 0xff75, 0xff76, 0xff77, 0xff78, 0xff79,
-	0xff7a, 0xff7b, 0xff7c, 0xff7d, 0xff7e, 0xff7f, 0xff80, 0xff81,
-	0xff82, 0xff83, 0xff84, 0xff85, 0xff86, 0xff87, 0xff88, 0xff89,
-	0xff8a, 0xff8b, 0xff8c, 0xff8d, 0xff8e, 0xff8f, 0xff90, 0xff91,
-	0xff92, 0xff93, 0xff94, 0xff95, 0xff96, 0xff97, 0xff98, 0xff99,
-	0xff9a, 0xff9b, 0xff9c, 0xff9d, 0xff9e, 0xff9f, 0xffa0, 0xffa1,
-	0xffa2, 0xffa3, 0xffa4, 0xffa5, 0xffa6, 0xffa7, 0xffa8, 0xffa9,
-	0xffaa, 0xffab, 0xffac, 0xffad, 0xffae, 0xffaf, 0xffb0, 0xffb1,
-	0xffb2, 0xffb3, 0xffb4, 0xffb5, 0xffb6, 0xffb7, 0xffb8, 0xffb9,
-	0xffba, 0xffbb, 0xffbc, 0xffbd, 0xffbe, 0xffbf, 0xffc0, 0xffc1,
-	0xffc2, 0xffc3, 0xffc4, 0xffc5, 0xffc6, 0xffc7, 0xffc8, 0xffc9,
-	0xffca, 0xffcb, 0xffcc, 0xffcd, 0xffce, 0xffcf, 0xffd0, 0xffd1,
-	0xffd2, 0xffd3, 0xffd4, 0xffd5, 0xffd6, 0xffd7, 0xffd8, 0xffd9,
-	0xffda, 0xffdb, 0xffdc, 0xffdd, 0xffde, 0xffdf, 0xffe0, 0xffe1,
-	0xffe2, 0xffe3, 0xffe4, 0xffe5, 0xffe6, 0xffe7, 0xffe8, 0xffe9,
-	0xffea, 0xffeb, 0xffec, 0xffed, 0xffee, 0xffef, 0xfff0, 0xfff1,
-	0xfff2, 0xfff3, 0xfff4, 0xfff5, 0xfff6, 0xfff7, 0xfff8, 0xfff9,
-	0xfffa, 0xfffb, 0xfffc, 0xfffd, 0xfffe, 0xffff,
-};
-
-/*
- * Allow full-width illegal characters :
- * "MS windows 7" supports full-width-invalid-name-characters.
- * So we should check half-width-invalid-name-characters(ASCII) only
- * for compatibility.
- *
- * " * / : < > ? \ |
- */
-static unsigned short bad_uni_chars[] = {
-	0x0022,         0x002A, 0x002F, 0x003A,
-	0x003C, 0x003E, 0x003F, 0x005C, 0x007C,
-	0
-};
-
 static int exfat_convert_char_to_ucs2(struct nls_table *nls,
 		const unsigned char *ch, int ch_len, unsigned short *ucs2,
 		int *lossy)
@@ -456,7 +65,8 @@ unsigned short exfat_toupper(struct super_block *sb, unsigned short a)
 	return sbi->vol_utbl[a] ? sbi->vol_utbl[a] : a;
 }
 
-static unsigned short *exfat_wstrchr(unsigned short *str, unsigned short wchar)
+static const unsigned short *exfat_wstrchr(const unsigned short *str,
+		const unsigned short wchar)
 {
 	while (*str) {
 		if (*(str++) == wchar)
@@ -516,7 +126,7 @@ static int exfat_utf8_to_utf16(struct super_block *sb,
 
 	for (i = 0; i < unilen; i++) {
 		if (*uniname < 0x0020 ||
-		    exfat_wstrchr(bad_uni_chars, *uniname))
+		    exfat_wstrchr(exfat_bad_uni_chars, *uniname))
 			lossy |= NLS_NAME_LOSSY;
 
 		upname[i] = cpu_to_le16(exfat_toupper(sb, *uniname));
@@ -608,7 +218,7 @@ static int exfat_nls_to_ucs2(struct super_block *sb,
 				uniname, &lossy);
 
 		if (*uniname < 0x0020 ||
-		    exfat_wstrchr(bad_uni_chars, *uniname))
+		    exfat_wstrchr(exfat_bad_uni_chars, *uniname))
 			lossy |= NLS_NAME_LOSSY;
 
 		upname[unilen] = cpu_to_le16(exfat_toupper(sb, *uniname));
@@ -655,7 +265,7 @@ static int exfat_load_upcase_table(struct super_block *sb,
 	unsigned char skip = false;
 	unsigned short *upcase_table;
 
-	upcase_table = kvcalloc(UTBL_COUNT, sizeof(unsigned short), GFP_KERNEL);
+	upcase_table = kvcalloc(EXFAT_UTBL_COUNT, sizeof(unsigned short), GFP_KERNEL);
 	if (!upcase_table)
 		return -ENOMEM;
 
@@ -707,14 +317,14 @@ static int exfat_load_default_upcase_table(struct super_block *sb)
 	unsigned short uni = 0, *upcase_table;
 	unsigned int index = 0;
 
-	upcase_table = kvcalloc(UTBL_COUNT, sizeof(unsigned short), GFP_KERNEL);
+	upcase_table = kvcalloc(EXFAT_UTBL_COUNT, sizeof(unsigned short), GFP_KERNEL);
 	if (!upcase_table)
 		return -ENOMEM;
 
 	sbi->vol_utbl = upcase_table;
 
 	for (i = 0; index <= 0xFFFF && i < EXFAT_NUM_UPCASE; i++) {
-		uni = uni_def_upcase[i];
+		uni = exfat_uni_def_upcase[i];
 		if (skip) {
 			index += uni;
 			skip = false;
diff --git a/fs/exfat/tables.c b/fs/exfat/tables.c
new file mode 100644
index 000000000000..d8efade76979
--- /dev/null
+++ b/fs/exfat/tables.c
@@ -0,0 +1,393 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/buffer_head.h>
+
+#include "exfat_raw.h"
+#include "exfat_fs.h"
+
+/*
+ * Upcase table in compressed format (7.2.5.1 Recommended Up-case Table
+ * in exfat specification, See:
+ * https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification).
+ */
+const unsigned short exfat_uni_def_upcase[EXFAT_NUM_UPCASE] = {
+	0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+	0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+	0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+	0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+	0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+	0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+	0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+	0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+	0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+	0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+	0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+	0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+	0x0060, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+	0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+	0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+	0x0058, 0x0059, 0x005a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
+	0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
+	0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
+	0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
+	0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
+	0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
+	0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
+	0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
+	0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
+	0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
+	0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+	0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
+	0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
+	0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
+	0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+	0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00f7,
+	0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x0178,
+	0x0100, 0x0100, 0x0102, 0x0102, 0x0104, 0x0104, 0x0106, 0x0106,
+	0x0108, 0x0108, 0x010a, 0x010a, 0x010c, 0x010c, 0x010e, 0x010e,
+	0x0110, 0x0110, 0x0112, 0x0112, 0x0114, 0x0114, 0x0116, 0x0116,
+	0x0118, 0x0118, 0x011a, 0x011a, 0x011c, 0x011c, 0x011e, 0x011e,
+	0x0120, 0x0120, 0x0122, 0x0122, 0x0124, 0x0124, 0x0126, 0x0126,
+	0x0128, 0x0128, 0x012a, 0x012a, 0x012c, 0x012c, 0x012e, 0x012e,
+	0x0130, 0x0131, 0x0132, 0x0132, 0x0134, 0x0134, 0x0136, 0x0136,
+	0x0138, 0x0139, 0x0139, 0x013b, 0x013b, 0x013d, 0x013d, 0x013f,
+	0x013f, 0x0141, 0x0141, 0x0143, 0x0143, 0x0145, 0x0145, 0x0147,
+	0x0147, 0x0149, 0x014a, 0x014a, 0x014c, 0x014c, 0x014e, 0x014e,
+	0x0150, 0x0150, 0x0152, 0x0152, 0x0154, 0x0154, 0x0156, 0x0156,
+	0x0158, 0x0158, 0x015a, 0x015a, 0x015c, 0x015c, 0x015e, 0x015e,
+	0x0160, 0x0160, 0x0162, 0x0162, 0x0164, 0x0164, 0x0166, 0x0166,
+	0x0168, 0x0168, 0x016a, 0x016a, 0x016c, 0x016c, 0x016e, 0x016e,
+	0x0170, 0x0170, 0x0172, 0x0172, 0x0174, 0x0174, 0x0176, 0x0176,
+	0x0178, 0x0179, 0x0179, 0x017b, 0x017b, 0x017d, 0x017d, 0x017f,
+	0x0243, 0x0181, 0x0182, 0x0182, 0x0184, 0x0184, 0x0186, 0x0187,
+	0x0187, 0x0189, 0x018a, 0x018b, 0x018b, 0x018d, 0x018e, 0x018f,
+	0x0190, 0x0191, 0x0191, 0x0193, 0x0194, 0x01f6, 0x0196, 0x0197,
+	0x0198, 0x0198, 0x023d, 0x019b, 0x019c, 0x019d, 0x0220, 0x019f,
+	0x01a0, 0x01a0, 0x01a2, 0x01a2, 0x01a4, 0x01a4, 0x01a6, 0x01a7,
+	0x01a7, 0x01a9, 0x01aa, 0x01ab, 0x01ac, 0x01ac, 0x01ae, 0x01af,
+	0x01af, 0x01b1, 0x01b2, 0x01b3, 0x01b3, 0x01b5, 0x01b5, 0x01b7,
+	0x01b8, 0x01b8, 0x01ba, 0x01bb, 0x01bc, 0x01bc, 0x01be, 0x01f7,
+	0x01c0, 0x01c1, 0x01c2, 0x01c3, 0x01c4, 0x01c5, 0x01c4, 0x01c7,
+	0x01c8, 0x01c7, 0x01ca, 0x01cb, 0x01ca, 0x01cd, 0x01cd, 0x01cf,
+	0x01cf, 0x01d1, 0x01d1, 0x01d3, 0x01d3, 0x01d5, 0x01d5, 0x01d7,
+	0x01d7, 0x01d9, 0x01d9, 0x01db, 0x01db, 0x018e, 0x01de, 0x01de,
+	0x01e0, 0x01e0, 0x01e2, 0x01e2, 0x01e4, 0x01e4, 0x01e6, 0x01e6,
+	0x01e8, 0x01e8, 0x01ea, 0x01ea, 0x01ec, 0x01ec, 0x01ee, 0x01ee,
+	0x01f0, 0x01f1, 0x01f2, 0x01f1, 0x01f4, 0x01f4, 0x01f6, 0x01f7,
+	0x01f8, 0x01f8, 0x01fa, 0x01fa, 0x01fc, 0x01fc, 0x01fe, 0x01fe,
+	0x0200, 0x0200, 0x0202, 0x0202, 0x0204, 0x0204, 0x0206, 0x0206,
+	0x0208, 0x0208, 0x020a, 0x020a, 0x020c, 0x020c, 0x020e, 0x020e,
+	0x0210, 0x0210, 0x0212, 0x0212, 0x0214, 0x0214, 0x0216, 0x0216,
+	0x0218, 0x0218, 0x021a, 0x021a, 0x021c, 0x021c, 0x021e, 0x021e,
+	0x0220, 0x0221, 0x0222, 0x0222, 0x0224, 0x0224, 0x0226, 0x0226,
+	0x0228, 0x0228, 0x022a, 0x022a, 0x022c, 0x022c, 0x022e, 0x022e,
+	0x0230, 0x0230, 0x0232, 0x0232, 0x0234, 0x0235, 0x0236, 0x0237,
+	0x0238, 0x0239, 0x2c65, 0x023b, 0x023b, 0x023d, 0x2c66, 0x023f,
+	0x0240, 0x0241, 0x0241, 0x0243, 0x0244, 0x0245, 0x0246, 0x0246,
+	0x0248, 0x0248, 0x024a, 0x024a, 0x024c, 0x024c, 0x024e, 0x024e,
+	0x0250, 0x0251, 0x0252, 0x0181, 0x0186, 0x0255, 0x0189, 0x018a,
+	0x0258, 0x018f, 0x025a, 0x0190, 0x025c, 0x025d, 0x025e, 0x025f,
+	0x0193, 0x0261, 0x0262, 0x0194, 0x0264, 0x0265, 0x0266, 0x0267,
+	0x0197, 0x0196, 0x026a, 0x2c62, 0x026c, 0x026d, 0x026e, 0x019c,
+	0x0270, 0x0271, 0x019d, 0x0273, 0x0274, 0x019f, 0x0276, 0x0277,
+	0x0278, 0x0279, 0x027a, 0x027b, 0x027c, 0x2c64, 0x027e, 0x027f,
+	0x01a6, 0x0281, 0x0282, 0x01a9, 0x0284, 0x0285, 0x0286, 0x0287,
+	0x01ae, 0x0244, 0x01b1, 0x01b2, 0x0245, 0x028d, 0x028e, 0x028f,
+	0x0290, 0x0291, 0x01b7, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297,
+	0x0298, 0x0299, 0x029a, 0x029b, 0x029c, 0x029d, 0x029e, 0x029f,
+	0x02a0, 0x02a1, 0x02a2, 0x02a3, 0x02a4, 0x02a5, 0x02a6, 0x02a7,
+	0x02a8, 0x02a9, 0x02aa, 0x02ab, 0x02ac, 0x02ad, 0x02ae, 0x02af,
+	0x02b0, 0x02b1, 0x02b2, 0x02b3, 0x02b4, 0x02b5, 0x02b6, 0x02b7,
+	0x02b8, 0x02b9, 0x02ba, 0x02bb, 0x02bc, 0x02bd, 0x02be, 0x02bf,
+	0x02c0, 0x02c1, 0x02c2, 0x02c3, 0x02c4, 0x02c5, 0x02c6, 0x02c7,
+	0x02c8, 0x02c9, 0x02ca, 0x02cb, 0x02cc, 0x02cd, 0x02ce, 0x02cf,
+	0x02d0, 0x02d1, 0x02d2, 0x02d3, 0x02d4, 0x02d5, 0x02d6, 0x02d7,
+	0x02d8, 0x02d9, 0x02da, 0x02db, 0x02dc, 0x02dd, 0x02de, 0x02df,
+	0x02e0, 0x02e1, 0x02e2, 0x02e3, 0x02e4, 0x02e5, 0x02e6, 0x02e7,
+	0x02e8, 0x02e9, 0x02ea, 0x02eb, 0x02ec, 0x02ed, 0x02ee, 0x02ef,
+	0x02f0, 0x02f1, 0x02f2, 0x02f3, 0x02f4, 0x02f5, 0x02f6, 0x02f7,
+	0x02f8, 0x02f9, 0x02fa, 0x02fb, 0x02fc, 0x02fd, 0x02fe, 0x02ff,
+	0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307,
+	0x0308, 0x0309, 0x030a, 0x030b, 0x030c, 0x030d, 0x030e, 0x030f,
+	0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317,
+	0x0318, 0x0319, 0x031a, 0x031b, 0x031c, 0x031d, 0x031e, 0x031f,
+	0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327,
+	0x0328, 0x0329, 0x032a, 0x032b, 0x032c, 0x032d, 0x032e, 0x032f,
+	0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337,
+	0x0338, 0x0339, 0x033a, 0x033b, 0x033c, 0x033d, 0x033e, 0x033f,
+	0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347,
+	0x0348, 0x0349, 0x034a, 0x034b, 0x034c, 0x034d, 0x034e, 0x034f,
+	0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357,
+	0x0358, 0x0359, 0x035a, 0x035b, 0x035c, 0x035d, 0x035e, 0x035f,
+	0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367,
+	0x0368, 0x0369, 0x036a, 0x036b, 0x036c, 0x036d, 0x036e, 0x036f,
+	0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377,
+	0x0378, 0x0379, 0x037a, 0x03fd, 0x03fe, 0x03ff, 0x037e, 0x037f,
+	0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0386, 0x0387,
+	0x0388, 0x0389, 0x038a, 0x038b, 0x038c, 0x038d, 0x038e, 0x038f,
+	0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
+	0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f,
+	0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7,
+	0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x0386, 0x0388, 0x0389, 0x038a,
+	0x03b0, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
+	0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f,
+	0x03a0, 0x03a1, 0x03a3, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7,
+	0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x038c, 0x038e, 0x038f, 0x03cf,
+	0x03d0, 0x03d1, 0x03d2, 0x03d3, 0x03d4, 0x03d5, 0x03d6, 0x03d7,
+	0x03d8, 0x03d8, 0x03da, 0x03da, 0x03dc, 0x03dc, 0x03de, 0x03de,
+	0x03e0, 0x03e0, 0x03e2, 0x03e2, 0x03e4, 0x03e4, 0x03e6, 0x03e6,
+	0x03e8, 0x03e8, 0x03ea, 0x03ea, 0x03ec, 0x03ec, 0x03ee, 0x03ee,
+	0x03f0, 0x03f1, 0x03f9, 0x03f3, 0x03f4, 0x03f5, 0x03f6, 0x03f7,
+	0x03f7, 0x03f9, 0x03fa, 0x03fa, 0x03fc, 0x03fd, 0x03fe, 0x03ff,
+	0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407,
+	0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x040d, 0x040e, 0x040f,
+	0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
+	0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f,
+	0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
+	0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f,
+	0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
+	0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f,
+	0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
+	0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f,
+	0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407,
+	0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x040d, 0x040e, 0x040f,
+	0x0460, 0x0460, 0x0462, 0x0462, 0x0464, 0x0464, 0x0466, 0x0466,
+	0x0468, 0x0468, 0x046a, 0x046a, 0x046c, 0x046c, 0x046e, 0x046e,
+	0x0470, 0x0470, 0x0472, 0x0472, 0x0474, 0x0474, 0x0476, 0x0476,
+	0x0478, 0x0478, 0x047a, 0x047a, 0x047c, 0x047c, 0x047e, 0x047e,
+	0x0480, 0x0480, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487,
+	0x0488, 0x0489, 0x048a, 0x048a, 0x048c, 0x048c, 0x048e, 0x048e,
+	0x0490, 0x0490, 0x0492, 0x0492, 0x0494, 0x0494, 0x0496, 0x0496,
+	0x0498, 0x0498, 0x049a, 0x049a, 0x049c, 0x049c, 0x049e, 0x049e,
+	0x04a0, 0x04a0, 0x04a2, 0x04a2, 0x04a4, 0x04a4, 0x04a6, 0x04a6,
+	0x04a8, 0x04a8, 0x04aa, 0x04aa, 0x04ac, 0x04ac, 0x04ae, 0x04ae,
+	0x04b0, 0x04b0, 0x04b2, 0x04b2, 0x04b4, 0x04b4, 0x04b6, 0x04b6,
+	0x04b8, 0x04b8, 0x04ba, 0x04ba, 0x04bc, 0x04bc, 0x04be, 0x04be,
+	0x04c0, 0x04c1, 0x04c1, 0x04c3, 0x04c3, 0x04c5, 0x04c5, 0x04c7,
+	0x04c7, 0x04c9, 0x04c9, 0x04cb, 0x04cb, 0x04cd, 0x04cd, 0x04c0,
+	0x04d0, 0x04d0, 0x04d2, 0x04d2, 0x04d4, 0x04d4, 0x04d6, 0x04d6,
+	0x04d8, 0x04d8, 0x04da, 0x04da, 0x04dc, 0x04dc, 0x04de, 0x04de,
+	0x04e0, 0x04e0, 0x04e2, 0x04e2, 0x04e4, 0x04e4, 0x04e6, 0x04e6,
+	0x04e8, 0x04e8, 0x04ea, 0x04ea, 0x04ec, 0x04ec, 0x04ee, 0x04ee,
+	0x04f0, 0x04f0, 0x04f2, 0x04f2, 0x04f4, 0x04f4, 0x04f6, 0x04f6,
+	0x04f8, 0x04f8, 0x04fa, 0x04fa, 0x04fc, 0x04fc, 0x04fe, 0x04fe,
+	0x0500, 0x0500, 0x0502, 0x0502, 0x0504, 0x0504, 0x0506, 0x0506,
+	0x0508, 0x0508, 0x050a, 0x050a, 0x050c, 0x050c, 0x050e, 0x050e,
+	0x0510, 0x0510, 0x0512, 0x0512, 0x0514, 0x0515, 0x0516, 0x0517,
+	0x0518, 0x0519, 0x051a, 0x051b, 0x051c, 0x051d, 0x051e, 0x051f,
+	0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527,
+	0x0528, 0x0529, 0x052a, 0x052b, 0x052c, 0x052d, 0x052e, 0x052f,
+	0x0530, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537,
+	0x0538, 0x0539, 0x053a, 0x053b, 0x053c, 0x053d, 0x053e, 0x053f,
+	0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547,
+	0x0548, 0x0549, 0x054a, 0x054b, 0x054c, 0x054d, 0x054e, 0x054f,
+	0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556, 0x0557,
+	0x0558, 0x0559, 0x055a, 0x055b, 0x055c, 0x055d, 0x055e, 0x055f,
+	0x0560, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537,
+	0x0538, 0x0539, 0x053a, 0x053b, 0x053c, 0x053d, 0x053e, 0x053f,
+	0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547,
+	0x0548, 0x0549, 0x054a, 0x054b, 0x054c, 0x054d, 0x054e, 0x054f,
+	0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556, 0xffff,
+	0x17f6, 0x2c63, 0x1d7e, 0x1d7f, 0x1d80, 0x1d81, 0x1d82, 0x1d83,
+	0x1d84, 0x1d85, 0x1d86, 0x1d87, 0x1d88, 0x1d89, 0x1d8a, 0x1d8b,
+	0x1d8c, 0x1d8d, 0x1d8e, 0x1d8f, 0x1d90, 0x1d91, 0x1d92, 0x1d93,
+	0x1d94, 0x1d95, 0x1d96, 0x1d97, 0x1d98, 0x1d99, 0x1d9a, 0x1d9b,
+	0x1d9c, 0x1d9d, 0x1d9e, 0x1d9f, 0x1da0, 0x1da1, 0x1da2, 0x1da3,
+	0x1da4, 0x1da5, 0x1da6, 0x1da7, 0x1da8, 0x1da9, 0x1daa, 0x1dab,
+	0x1dac, 0x1dad, 0x1dae, 0x1daf, 0x1db0, 0x1db1, 0x1db2, 0x1db3,
+	0x1db4, 0x1db5, 0x1db6, 0x1db7, 0x1db8, 0x1db9, 0x1dba, 0x1dbb,
+	0x1dbc, 0x1dbd, 0x1dbe, 0x1dbf, 0x1dc0, 0x1dc1, 0x1dc2, 0x1dc3,
+	0x1dc4, 0x1dc5, 0x1dc6, 0x1dc7, 0x1dc8, 0x1dc9, 0x1dca, 0x1dcb,
+	0x1dcc, 0x1dcd, 0x1dce, 0x1dcf, 0x1dd0, 0x1dd1, 0x1dd2, 0x1dd3,
+	0x1dd4, 0x1dd5, 0x1dd6, 0x1dd7, 0x1dd8, 0x1dd9, 0x1dda, 0x1ddb,
+	0x1ddc, 0x1ddd, 0x1dde, 0x1ddf, 0x1de0, 0x1de1, 0x1de2, 0x1de3,
+	0x1de4, 0x1de5, 0x1de6, 0x1de7, 0x1de8, 0x1de9, 0x1dea, 0x1deb,
+	0x1dec, 0x1ded, 0x1dee, 0x1def, 0x1df0, 0x1df1, 0x1df2, 0x1df3,
+	0x1df4, 0x1df5, 0x1df6, 0x1df7, 0x1df8, 0x1df9, 0x1dfa, 0x1dfb,
+	0x1dfc, 0x1dfd, 0x1dfe, 0x1dff, 0x1e00, 0x1e00, 0x1e02, 0x1e02,
+	0x1e04, 0x1e04, 0x1e06, 0x1e06, 0x1e08, 0x1e08, 0x1e0a, 0x1e0a,
+	0x1e0c, 0x1e0c, 0x1e0e, 0x1e0e, 0x1e10, 0x1e10, 0x1e12, 0x1e12,
+	0x1e14, 0x1e14, 0x1e16, 0x1e16, 0x1e18, 0x1e18, 0x1e1a, 0x1e1a,
+	0x1e1c, 0x1e1c, 0x1e1e, 0x1e1e, 0x1e20, 0x1e20, 0x1e22, 0x1e22,
+	0x1e24, 0x1e24, 0x1e26, 0x1e26, 0x1e28, 0x1e28, 0x1e2a, 0x1e2a,
+	0x1e2c, 0x1e2c, 0x1e2e, 0x1e2e, 0x1e30, 0x1e30, 0x1e32, 0x1e32,
+	0x1e34, 0x1e34, 0x1e36, 0x1e36, 0x1e38, 0x1e38, 0x1e3a, 0x1e3a,
+	0x1e3c, 0x1e3c, 0x1e3e, 0x1e3e, 0x1e40, 0x1e40, 0x1e42, 0x1e42,
+	0x1e44, 0x1e44, 0x1e46, 0x1e46, 0x1e48, 0x1e48, 0x1e4a, 0x1e4a,
+	0x1e4c, 0x1e4c, 0x1e4e, 0x1e4e, 0x1e50, 0x1e50, 0x1e52, 0x1e52,
+	0x1e54, 0x1e54, 0x1e56, 0x1e56, 0x1e58, 0x1e58, 0x1e5a, 0x1e5a,
+	0x1e5c, 0x1e5c, 0x1e5e, 0x1e5e, 0x1e60, 0x1e60, 0x1e62, 0x1e62,
+	0x1e64, 0x1e64, 0x1e66, 0x1e66, 0x1e68, 0x1e68, 0x1e6a, 0x1e6a,
+	0x1e6c, 0x1e6c, 0x1e6e, 0x1e6e, 0x1e70, 0x1e70, 0x1e72, 0x1e72,
+	0x1e74, 0x1e74, 0x1e76, 0x1e76, 0x1e78, 0x1e78, 0x1e7a, 0x1e7a,
+	0x1e7c, 0x1e7c, 0x1e7e, 0x1e7e, 0x1e80, 0x1e80, 0x1e82, 0x1e82,
+	0x1e84, 0x1e84, 0x1e86, 0x1e86, 0x1e88, 0x1e88, 0x1e8a, 0x1e8a,
+	0x1e8c, 0x1e8c, 0x1e8e, 0x1e8e, 0x1e90, 0x1e90, 0x1e92, 0x1e92,
+	0x1e94, 0x1e94, 0x1e96, 0x1e97, 0x1e98, 0x1e99, 0x1e9a, 0x1e9b,
+	0x1e9c, 0x1e9d, 0x1e9e, 0x1e9f, 0x1ea0, 0x1ea0, 0x1ea2, 0x1ea2,
+	0x1ea4, 0x1ea4, 0x1ea6, 0x1ea6, 0x1ea8, 0x1ea8, 0x1eaa, 0x1eaa,
+	0x1eac, 0x1eac, 0x1eae, 0x1eae, 0x1eb0, 0x1eb0, 0x1eb2, 0x1eb2,
+	0x1eb4, 0x1eb4, 0x1eb6, 0x1eb6, 0x1eb8, 0x1eb8, 0x1eba, 0x1eba,
+	0x1ebc, 0x1ebc, 0x1ebe, 0x1ebe, 0x1ec0, 0x1ec0, 0x1ec2, 0x1ec2,
+	0x1ec4, 0x1ec4, 0x1ec6, 0x1ec6, 0x1ec8, 0x1ec8, 0x1eca, 0x1eca,
+	0x1ecc, 0x1ecc, 0x1ece, 0x1ece, 0x1ed0, 0x1ed0, 0x1ed2, 0x1ed2,
+	0x1ed4, 0x1ed4, 0x1ed6, 0x1ed6, 0x1ed8, 0x1ed8, 0x1eda, 0x1eda,
+	0x1edc, 0x1edc, 0x1ede, 0x1ede, 0x1ee0, 0x1ee0, 0x1ee2, 0x1ee2,
+	0x1ee4, 0x1ee4, 0x1ee6, 0x1ee6, 0x1ee8, 0x1ee8, 0x1eea, 0x1eea,
+	0x1eec, 0x1eec, 0x1eee, 0x1eee, 0x1ef0, 0x1ef0, 0x1ef2, 0x1ef2,
+	0x1ef4, 0x1ef4, 0x1ef6, 0x1ef6, 0x1ef8, 0x1ef8, 0x1efa, 0x1efb,
+	0x1efc, 0x1efd, 0x1efe, 0x1eff, 0x1f08, 0x1f09, 0x1f0a, 0x1f0b,
+	0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, 0x1f08, 0x1f09, 0x1f0a, 0x1f0b,
+	0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, 0x1f18, 0x1f19, 0x1f1a, 0x1f1b,
+	0x1f1c, 0x1f1d, 0x1f16, 0x1f17, 0x1f18, 0x1f19, 0x1f1a, 0x1f1b,
+	0x1f1c, 0x1f1d, 0x1f1e, 0x1f1f, 0x1f28, 0x1f29, 0x1f2a, 0x1f2b,
+	0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, 0x1f28, 0x1f29, 0x1f2a, 0x1f2b,
+	0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, 0x1f38, 0x1f39, 0x1f3a, 0x1f3b,
+	0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, 0x1f38, 0x1f39, 0x1f3a, 0x1f3b,
+	0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, 0x1f48, 0x1f49, 0x1f4a, 0x1f4b,
+	0x1f4c, 0x1f4d, 0x1f46, 0x1f47, 0x1f48, 0x1f49, 0x1f4a, 0x1f4b,
+	0x1f4c, 0x1f4d, 0x1f4e, 0x1f4f, 0x1f50, 0x1f59, 0x1f52, 0x1f5b,
+	0x1f54, 0x1f5d, 0x1f56, 0x1f5f, 0x1f58, 0x1f59, 0x1f5a, 0x1f5b,
+	0x1f5c, 0x1f5d, 0x1f5e, 0x1f5f, 0x1f68, 0x1f69, 0x1f6a, 0x1f6b,
+	0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, 0x1f68, 0x1f69, 0x1f6a, 0x1f6b,
+	0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, 0x1fba, 0x1fbb, 0x1fc8, 0x1fc9,
+	0x1fca, 0x1fcb, 0x1fda, 0x1fdb, 0x1ff8, 0x1ff9, 0x1fea, 0x1feb,
+	0x1ffa, 0x1ffb, 0x1f7e, 0x1f7f, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b,
+	0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b,
+	0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b,
+	0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b,
+	0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab,
+	0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab,
+	0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fb8, 0x1fb9, 0x1fb2, 0x1fbc,
+	0x1fb4, 0x1fb5, 0x1fb6, 0x1fb7, 0x1fb8, 0x1fb9, 0x1fba, 0x1fbb,
+	0x1fbc, 0x1fbd, 0x1fbe, 0x1fbf, 0x1fc0, 0x1fc1, 0x1fc2, 0x1fc3,
+	0x1fc4, 0x1fc5, 0x1fc6, 0x1fc7, 0x1fc8, 0x1fc9, 0x1fca, 0x1fcb,
+	0x1fc3, 0x1fcd, 0x1fce, 0x1fcf, 0x1fd8, 0x1fd9, 0x1fd2, 0x1fd3,
+	0x1fd4, 0x1fd5, 0x1fd6, 0x1fd7, 0x1fd8, 0x1fd9, 0x1fda, 0x1fdb,
+	0x1fdc, 0x1fdd, 0x1fde, 0x1fdf, 0x1fe8, 0x1fe9, 0x1fe2, 0x1fe3,
+	0x1fe4, 0x1fec, 0x1fe6, 0x1fe7, 0x1fe8, 0x1fe9, 0x1fea, 0x1feb,
+	0x1fec, 0x1fed, 0x1fee, 0x1fef, 0x1ff0, 0x1ff1, 0x1ff2, 0x1ff3,
+	0x1ff4, 0x1ff5, 0x1ff6, 0x1ff7, 0x1ff8, 0x1ff9, 0x1ffa, 0x1ffb,
+	0x1ff3, 0x1ffd, 0x1ffe, 0x1fff, 0x2000, 0x2001, 0x2002, 0x2003,
+	0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200a, 0x200b,
+	0x200c, 0x200d, 0x200e, 0x200f, 0x2010, 0x2011, 0x2012, 0x2013,
+	0x2014, 0x2015, 0x2016, 0x2017, 0x2018, 0x2019, 0x201a, 0x201b,
+	0x201c, 0x201d, 0x201e, 0x201f, 0x2020, 0x2021, 0x2022, 0x2023,
+	0x2024, 0x2025, 0x2026, 0x2027, 0x2028, 0x2029, 0x202a, 0x202b,
+	0x202c, 0x202d, 0x202e, 0x202f, 0x2030, 0x2031, 0x2032, 0x2033,
+	0x2034, 0x2035, 0x2036, 0x2037, 0x2038, 0x2039, 0x203a, 0x203b,
+	0x203c, 0x203d, 0x203e, 0x203f, 0x2040, 0x2041, 0x2042, 0x2043,
+	0x2044, 0x2045, 0x2046, 0x2047, 0x2048, 0x2049, 0x204a, 0x204b,
+	0x204c, 0x204d, 0x204e, 0x204f, 0x2050, 0x2051, 0x2052, 0x2053,
+	0x2054, 0x2055, 0x2056, 0x2057, 0x2058, 0x2059, 0x205a, 0x205b,
+	0x205c, 0x205d, 0x205e, 0x205f, 0x2060, 0x2061, 0x2062, 0x2063,
+	0x2064, 0x2065, 0x2066, 0x2067, 0x2068, 0x2069, 0x206a, 0x206b,
+	0x206c, 0x206d, 0x206e, 0x206f, 0x2070, 0x2071, 0x2072, 0x2073,
+	0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, 0x207a, 0x207b,
+	0x207c, 0x207d, 0x207e, 0x207f, 0x2080, 0x2081, 0x2082, 0x2083,
+	0x2084, 0x2085, 0x2086, 0x2087, 0x2088, 0x2089, 0x208a, 0x208b,
+	0x208c, 0x208d, 0x208e, 0x208f, 0x2090, 0x2091, 0x2092, 0x2093,
+	0x2094, 0x2095, 0x2096, 0x2097, 0x2098, 0x2099, 0x209a, 0x209b,
+	0x209c, 0x209d, 0x209e, 0x209f, 0x20a0, 0x20a1, 0x20a2, 0x20a3,
+	0x20a4, 0x20a5, 0x20a6, 0x20a7, 0x20a8, 0x20a9, 0x20aa, 0x20ab,
+	0x20ac, 0x20ad, 0x20ae, 0x20af, 0x20b0, 0x20b1, 0x20b2, 0x20b3,
+	0x20b4, 0x20b5, 0x20b6, 0x20b7, 0x20b8, 0x20b9, 0x20ba, 0x20bb,
+	0x20bc, 0x20bd, 0x20be, 0x20bf, 0x20c0, 0x20c1, 0x20c2, 0x20c3,
+	0x20c4, 0x20c5, 0x20c6, 0x20c7, 0x20c8, 0x20c9, 0x20ca, 0x20cb,
+	0x20cc, 0x20cd, 0x20ce, 0x20cf, 0x20d0, 0x20d1, 0x20d2, 0x20d3,
+	0x20d4, 0x20d5, 0x20d6, 0x20d7, 0x20d8, 0x20d9, 0x20da, 0x20db,
+	0x20dc, 0x20dd, 0x20de, 0x20df, 0x20e0, 0x20e1, 0x20e2, 0x20e3,
+	0x20e4, 0x20e5, 0x20e6, 0x20e7, 0x20e8, 0x20e9, 0x20ea, 0x20eb,
+	0x20ec, 0x20ed, 0x20ee, 0x20ef, 0x20f0, 0x20f1, 0x20f2, 0x20f3,
+	0x20f4, 0x20f5, 0x20f6, 0x20f7, 0x20f8, 0x20f9, 0x20fa, 0x20fb,
+	0x20fc, 0x20fd, 0x20fe, 0x20ff, 0x2100, 0x2101, 0x2102, 0x2103,
+	0x2104, 0x2105, 0x2106, 0x2107, 0x2108, 0x2109, 0x210a, 0x210b,
+	0x210c, 0x210d, 0x210e, 0x210f, 0x2110, 0x2111, 0x2112, 0x2113,
+	0x2114, 0x2115, 0x2116, 0x2117, 0x2118, 0x2119, 0x211a, 0x211b,
+	0x211c, 0x211d, 0x211e, 0x211f, 0x2120, 0x2121, 0x2122, 0x2123,
+	0x2124, 0x2125, 0x2126, 0x2127, 0x2128, 0x2129, 0x212a, 0x212b,
+	0x212c, 0x212d, 0x212e, 0x212f, 0x2130, 0x2131, 0x2132, 0x2133,
+	0x2134, 0x2135, 0x2136, 0x2137, 0x2138, 0x2139, 0x213a, 0x213b,
+	0x213c, 0x213d, 0x213e, 0x213f, 0x2140, 0x2141, 0x2142, 0x2143,
+	0x2144, 0x2145, 0x2146, 0x2147, 0x2148, 0x2149, 0x214a, 0x214b,
+	0x214c, 0x214d, 0x2132, 0x214f, 0x2150, 0x2151, 0x2152, 0x2153,
+	0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a, 0x215b,
+	0x215c, 0x215d, 0x215e, 0x215f, 0x2160, 0x2161, 0x2162, 0x2163,
+	0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216a, 0x216b,
+	0x216c, 0x216d, 0x216e, 0x216f, 0x2160, 0x2161, 0x2162, 0x2163,
+	0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216a, 0x216b,
+	0x216c, 0x216d, 0x216e, 0x216f, 0x2180, 0x2181, 0x2182, 0x2183,
+	0x2183, 0xffff, 0x034b, 0x24b6, 0x24b7, 0x24b8, 0x24b9, 0x24ba,
+	0x24bb, 0x24bc, 0x24bd, 0x24be, 0x24bf, 0x24c0, 0x24c1, 0x24c2,
+	0x24c3, 0x24c4, 0x24c5, 0x24c6, 0x24c7, 0x24c8, 0x24c9, 0x24ca,
+	0x24cb, 0x24cc, 0x24cd, 0x24ce, 0x24cf, 0xffff, 0x0746, 0x2c00,
+	0x2c01, 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x2c08,
+	0x2c09, 0x2c0a, 0x2c0b, 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x2c10,
+	0x2c11, 0x2c12, 0x2c13, 0x2c14, 0x2c15, 0x2c16, 0x2c17, 0x2c18,
+	0x2c19, 0x2c1a, 0x2c1b, 0x2c1c, 0x2c1d, 0x2c1e, 0x2c1f, 0x2c20,
+	0x2c21, 0x2c22, 0x2c23, 0x2c24, 0x2c25, 0x2c26, 0x2c27, 0x2c28,
+	0x2c29, 0x2c2a, 0x2c2b, 0x2c2c, 0x2c2d, 0x2c2e, 0x2c5f, 0x2c60,
+	0x2c60, 0x2c62, 0x2c63, 0x2c64, 0x2c65, 0x2c66, 0x2c67, 0x2c67,
+	0x2c69, 0x2c69, 0x2c6b, 0x2c6b, 0x2c6d, 0x2c6e, 0x2c6f, 0x2c70,
+	0x2c71, 0x2c72, 0x2c73, 0x2c74, 0x2c75, 0x2c75, 0x2c77, 0x2c78,
+	0x2c79, 0x2c7a, 0x2c7b, 0x2c7c, 0x2c7d, 0x2c7e, 0x2c7f, 0x2c80,
+	0x2c80, 0x2c82, 0x2c82, 0x2c84, 0x2c84, 0x2c86, 0x2c86, 0x2c88,
+	0x2c88, 0x2c8a, 0x2c8a, 0x2c8c, 0x2c8c, 0x2c8e, 0x2c8e, 0x2c90,
+	0x2c90, 0x2c92, 0x2c92, 0x2c94, 0x2c94, 0x2c96, 0x2c96, 0x2c98,
+	0x2c98, 0x2c9a, 0x2c9a, 0x2c9c, 0x2c9c, 0x2c9e, 0x2c9e, 0x2ca0,
+	0x2ca0, 0x2ca2, 0x2ca2, 0x2ca4, 0x2ca4, 0x2ca6, 0x2ca6, 0x2ca8,
+	0x2ca8, 0x2caa, 0x2caa, 0x2cac, 0x2cac, 0x2cae, 0x2cae, 0x2cb0,
+	0x2cb0, 0x2cb2, 0x2cb2, 0x2cb4, 0x2cb4, 0x2cb6, 0x2cb6, 0x2cb8,
+	0x2cb8, 0x2cba, 0x2cba, 0x2cbc, 0x2cbc, 0x2cbe, 0x2cbe, 0x2cc0,
+	0x2cc0, 0x2cc2, 0x2cc2, 0x2cc4, 0x2cc4, 0x2cc6, 0x2cc6, 0x2cc8,
+	0x2cc8, 0x2cca, 0x2cca, 0x2ccc, 0x2ccc, 0x2cce, 0x2cce, 0x2cd0,
+	0x2cd0, 0x2cd2, 0x2cd2, 0x2cd4, 0x2cd4, 0x2cd6, 0x2cd6, 0x2cd8,
+	0x2cd8, 0x2cda, 0x2cda, 0x2cdc, 0x2cdc, 0x2cde, 0x2cde, 0x2ce0,
+	0x2ce0, 0x2ce2, 0x2ce2, 0x2ce4, 0x2ce5, 0x2ce6, 0x2ce7, 0x2ce8,
+	0x2ce9, 0x2cea, 0x2ceb, 0x2cec, 0x2ced, 0x2cee, 0x2cef, 0x2cf0,
+	0x2cf1, 0x2cf2, 0x2cf3, 0x2cf4, 0x2cf5, 0x2cf6, 0x2cf7, 0x2cf8,
+	0x2cf9, 0x2cfa, 0x2cfb, 0x2cfc, 0x2cfd, 0x2cfe, 0x2cff, 0x10a0,
+	0x10a1, 0x10a2, 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7, 0x10a8,
+	0x10a9, 0x10aa, 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af, 0x10b0,
+	0x10b1, 0x10b2, 0x10b3, 0x10b4, 0x10b5, 0x10b6, 0x10b7, 0x10b8,
+	0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd, 0x10be, 0x10bf, 0x10c0,
+	0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5, 0xffff, 0xd21b, 0xff21,
+	0xff22, 0xff23, 0xff24, 0xff25, 0xff26, 0xff27, 0xff28, 0xff29,
+	0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e, 0xff2f, 0xff30, 0xff31,
+	0xff32, 0xff33, 0xff34, 0xff35, 0xff36, 0xff37, 0xff38, 0xff39,
+	0xff3a, 0xff5b, 0xff5c, 0xff5d, 0xff5e, 0xff5f, 0xff60, 0xff61,
+	0xff62, 0xff63, 0xff64, 0xff65, 0xff66, 0xff67, 0xff68, 0xff69,
+	0xff6a, 0xff6b, 0xff6c, 0xff6d, 0xff6e, 0xff6f, 0xff70, 0xff71,
+	0xff72, 0xff73, 0xff74, 0xff75, 0xff76, 0xff77, 0xff78, 0xff79,
+	0xff7a, 0xff7b, 0xff7c, 0xff7d, 0xff7e, 0xff7f, 0xff80, 0xff81,
+	0xff82, 0xff83, 0xff84, 0xff85, 0xff86, 0xff87, 0xff88, 0xff89,
+	0xff8a, 0xff8b, 0xff8c, 0xff8d, 0xff8e, 0xff8f, 0xff90, 0xff91,
+	0xff92, 0xff93, 0xff94, 0xff95, 0xff96, 0xff97, 0xff98, 0xff99,
+	0xff9a, 0xff9b, 0xff9c, 0xff9d, 0xff9e, 0xff9f, 0xffa0, 0xffa1,
+	0xffa2, 0xffa3, 0xffa4, 0xffa5, 0xffa6, 0xffa7, 0xffa8, 0xffa9,
+	0xffaa, 0xffab, 0xffac, 0xffad, 0xffae, 0xffaf, 0xffb0, 0xffb1,
+	0xffb2, 0xffb3, 0xffb4, 0xffb5, 0xffb6, 0xffb7, 0xffb8, 0xffb9,
+	0xffba, 0xffbb, 0xffbc, 0xffbd, 0xffbe, 0xffbf, 0xffc0, 0xffc1,
+	0xffc2, 0xffc3, 0xffc4, 0xffc5, 0xffc6, 0xffc7, 0xffc8, 0xffc9,
+	0xffca, 0xffcb, 0xffcc, 0xffcd, 0xffce, 0xffcf, 0xffd0, 0xffd1,
+	0xffd2, 0xffd3, 0xffd4, 0xffd5, 0xffd6, 0xffd7, 0xffd8, 0xffd9,
+	0xffda, 0xffdb, 0xffdc, 0xffdd, 0xffde, 0xffdf, 0xffe0, 0xffe1,
+	0xffe2, 0xffe3, 0xffe4, 0xffe5, 0xffe6, 0xffe7, 0xffe8, 0xffe9,
+	0xffea, 0xffeb, 0xffec, 0xffed, 0xffee, 0xffef, 0xfff0, 0xfff1,
+	0xfff2, 0xfff3, 0xfff4, 0xfff5, 0xfff6, 0xfff7, 0xfff8, 0xfff9,
+	0xfffa, 0xfffb, 0xfffc, 0xfffd, 0xfffe, 0xffff,
+};
+
+/*
+ * Allow full-width illegal characters :
+ * "MS windows 7" supports full-width-invalid-name-characters.
+ * So we should check half-width-invalid-name-characters(ASCII) only
+ * for compatibility.
+ *
+ * " * / : < > ? \ |
+ */
+const unsigned short exfat_bad_uni_chars[] = {
+	0x0022,         0x002A, 0x002F, 0x003A,
+	0x003C, 0x003E, 0x003F, 0x005C, 0x007C,
+	0
+};
-- 
2.53.0.1.ga224b40d3f.dirty


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 2/4] exfat: use upcase_ptable and upcase_range_info to reduce memory footprint
  2026-04-28 23:50 [PATCH v1 0/4] exfat: memory optimisations and stringent integrity checks for up-case table David Timber
  2026-04-28 23:50 ` [PATCH v1 1/4] exfat: refactor nls.c (tables) David Timber
@ 2026-04-28 23:50 ` David Timber
  2026-04-30  7:58   ` Yuezhang.Mo
  2026-04-28 23:50 ` [PATCH v1 3/4] exfat: add default_upcase option (read-only) David Timber
  2026-04-28 23:50 ` [PATCH v1 4/4] exfat: more pedantic upcase table validity check David Timber
  3 siblings, 1 reply; 16+ messages in thread
From: David Timber @ 2026-04-28 23:50 UTC (permalink / raw)
  To: Namjae Jeon, Sungjong Seo, Yuezhang Mo; +Cc: linux-fsdevel, David Timber

Introduce upcase_ptable("paged up-case table"). Instead of allocating
the entire 131KB of memory(2^16 possible entries times 2 bytes), break
up the upcase table into 1024-byte pages and do not allocate memory
for pages containing no data. The size of default upcase table
populated during module initialisation is 8KB.

Reduce the size of .rodata section further by employing
exfat_upcase_range_info, each representing a run in the unicode BMP.
Each entry in exfat_def_utbl_ri represent the exact copy of the
"recommended upcase table" from the exFAT spec but compressed in a
format of our own(less than 1KB of .rodata compared to 5836 bytes of
the compressed format in the spec).

When the contents of the upcase table loaded from the volume is
identical to that of the default upcase table during mounting process,
free the memory dynamically allocated to load the table and set up the
super block to use the default table already populated during module
initialisation.

As a custom upcase table is almost never used, keeping a copy of upcase
table for every super block is wasteful. If one is used, it means that
name hashing behaviour may not work as expected so warn the user by
printing a kernel message.

Signed-off-by: David Timber <dxdt@dev.snart.me>
---
 fs/exfat/Makefile   |    2 +-
 fs/exfat/exfat_fs.h |   54 +-
 fs/exfat/nls.c      |  140 ++---
 fs/exfat/super.c    |    7 +
 fs/exfat/tables.c   | 1190 +++++++++++++++++++++++++++++--------------
 fs/exfat/upcase.c   |   70 +++
 6 files changed, 1029 insertions(+), 434 deletions(-)
 create mode 100644 fs/exfat/upcase.c

diff --git a/fs/exfat/Makefile b/fs/exfat/Makefile
index 63e8e91c2ff3..81618ac12a72 100644
--- a/fs/exfat/Makefile
+++ b/fs/exfat/Makefile
@@ -5,4 +5,4 @@
 obj-$(CONFIG_EXFAT_FS) += exfat.o
 
 exfat-y	:= inode.o namei.o dir.o super.o fatent.o cache.o nls.o misc.o \
-	   file.o balloc.o tables.o
+	   file.o balloc.o tables.o upcase.o
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 3c99be1ecdc8..057ae8225a57 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -11,6 +11,7 @@
 #include <linux/nls.h>
 #include <linux/blkdev.h>
 #include <linux/backing-dev.h>
+#include <linux/build_bug.h>
 #include <uapi/linux/exfat.h>
 
 #define EXFAT_ROOT_INO		1
@@ -223,6 +224,30 @@ struct exfat_dir_entry {
 	struct exfat_dentry_namebuf namebuf;
 };
 
+/*
+ * exfat upcase paged table
+ */
+/* magic values */
+#define EXFAT_UPTBL_PAGESIZE	(512)
+#define EXFAT_UPTBL_SIZE	(1 << 16)
+#define EXFAT_UPTBL_ARRSIZE	(EXFAT_UPTBL_SIZE / EXFAT_UPTBL_PAGESIZE)
+
+struct exfat_upcase_ptable {
+	__u16 *pages[EXFAT_UPTBL_ARRSIZE];
+	size_t cnt;
+};
+
+struct exfat_upcase_range_info {
+	__u16 start;
+	__u16 end;
+	__u16 value;
+	__u16 inc;
+};
+
+/* some safety checks */
+static_assert(EXFAT_UPTBL_SIZE % EXFAT_UPTBL_PAGESIZE == 0);
+static_assert(0xFFFF / EXFAT_UPTBL_PAGESIZE < EXFAT_UPTBL_ARRSIZE);
+
 /*
  * exfat mount in-memory data
  */
@@ -270,7 +295,8 @@ struct exfat_sb_info {
 	unsigned int map_sectors; /* num of allocation bitmap sectors */
 	struct buffer_head **vol_amap; /* allocation bitmap */
 
-	unsigned short *vol_utbl; /* upcase table */
+	const struct exfat_upcase_ptable *vol_utbl; /* selected upcase ptable */
+	struct exfat_upcase_ptable *vol_utbl_own; /* loaded upcase ptable, if not default */
 
 	unsigned int clu_srch_ptr; /* cluster search pointer */
 	unsigned int used_clusters; /* number of used clusters */
@@ -598,6 +624,8 @@ int exfat_nls_to_utf16(struct super_block *sb,
 		struct exfat_uni_name *uniname, int *p_lossy);
 int exfat_create_upcase_table(struct super_block *sb);
 void exfat_free_upcase_table(struct exfat_sb_info *sbi);
+int exfat_init_default_upcase_ptable(void);
+void exfat_free_default_upcase_ptable(void);
 
 /* exfat/misc.c */
 void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
@@ -608,12 +636,30 @@ void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
 		__exfat_fs_error(sb, __ratelimit(&EXFAT_SB(sb)->ratelimit), \
 		fmt, ## args)
 
+/* exfat/upcase.c */
+int exfat_set_upcase_ptable(struct exfat_upcase_ptable *ptbl,
+		const __u16 index, const __u16 value);
+
+static inline __u16 exfat_lookup_upcase_ptable(const struct exfat_upcase_ptable *ptbl,
+		const __u16 index)
+{
+	const size_t page_idx = index / EXFAT_UPTBL_PAGESIZE;
+	const size_t idx_in_page = index % EXFAT_UPTBL_PAGESIZE;
+
+	return ptbl->pages[page_idx] == NULL ? 0 : ptbl->pages[page_idx][idx_in_page];
+}
+
+void exfat_free_upcase_ptable(struct exfat_upcase_ptable *ptbl);
+int exfat_populate_upcase_ptable(struct exfat_upcase_ptable *ptbl,
+		const struct exfat_upcase_range_info *ri,
+		const size_t cnt);
+
 /* exfat/tables.c */
 /* Upcase table macro */
-#define EXFAT_NUM_UPCASE	(2918)
-#define EXFAT_UTBL_COUNT	(0x10000)
+#define EXFAT_DEF_UTBL_RI_COUNT	(112)
+#define EXFAT_DEF_UTBL_CHKSUM	(0xE619D30D)
 
-extern const unsigned short exfat_uni_def_upcase[EXFAT_NUM_UPCASE];
+extern const struct exfat_upcase_range_info exfat_def_utbl_ri[EXFAT_DEF_UTBL_RI_COUNT];
 extern const unsigned short exfat_bad_uni_chars[];
 
 /* expand to pr_*() with prefix */
diff --git a/fs/exfat/nls.c b/fs/exfat/nls.c
index 7e85d15ffc0b..c56d501659b3 100644
--- a/fs/exfat/nls.c
+++ b/fs/exfat/nls.c
@@ -11,6 +11,8 @@
 #include "exfat_raw.h"
 #include "exfat_fs.h"
 
+static struct exfat_upcase_ptable default_upcase_table;
+
 static int exfat_convert_char_to_ucs2(struct nls_table *nls,
 		const unsigned char *ch, int ch_len, unsigned short *ucs2,
 		int *lossy)
@@ -61,8 +63,11 @@ static int exfat_convert_ucs2_to_char(struct nls_table *nls,
 unsigned short exfat_toupper(struct super_block *sb, unsigned short a)
 {
 	struct exfat_sb_info *sbi = EXFAT_SB(sb);
+	unsigned short ret;
+
+	ret = exfat_lookup_upcase_ptable(sbi->vol_utbl, a);
 
-	return sbi->vol_utbl[a] ? sbi->vol_utbl[a] : a;
+	return ret ? ret : a;
 }
 
 static const unsigned short *exfat_wstrchr(const unsigned short *str,
@@ -263,14 +268,18 @@ static int exfat_load_upcase_table(struct super_block *sb,
 	unsigned int i, index = 0;
 	u32 chksum = 0;
 	unsigned char skip = false;
-	unsigned short *upcase_table;
+	struct exfat_upcase_ptable *upcase_table;
+	unsigned short def_upcase;
+	bool is_default;
+	unsigned int entries = 0;
+	int ret = -EINVAL;
 
-	upcase_table = kvcalloc(EXFAT_UTBL_COUNT, sizeof(unsigned short), GFP_KERNEL);
+	upcase_table = kvcalloc(1, sizeof(struct exfat_upcase_ptable), GFP_KERNEL);
 	if (!upcase_table)
 		return -ENOMEM;
 
-	sbi->vol_utbl = upcase_table;
 	num_sectors += sector;
+	is_default = sector < num_sectors;
 
 	while (sector < num_sectors) {
 		struct buffer_head *bh;
@@ -279,7 +288,8 @@ static int exfat_load_upcase_table(struct super_block *sb,
 		if (!bh) {
 			exfat_err(sb, "failed to read sector(0x%llx)",
 				  (unsigned long long)sector);
-			return -EIO;
+			ret = -EIO;
+			goto err;
 		}
 		sector++;
 		for (i = 0; i < sect_size && index <= 0xFFFF; i += 2) {
@@ -293,7 +303,17 @@ static int exfat_load_upcase_table(struct super_block *sb,
 			} else if (uni == 0xFFFF) {
 				skip = true;
 			} else { /* uni != index , uni != 0xFFFF */
-				upcase_table[index] = uni;
+				ret = exfat_set_upcase_ptable(upcase_table, index, uni);
+				if (ret) {
+					brelse(bh);
+					goto err;
+				}
+
+				def_upcase = exfat_lookup_upcase_ptable(&default_upcase_table,
+									index);
+				is_default &= def_upcase == uni;
+
+				entries++;
 				index++;
 			}
 		}
@@ -301,53 +321,36 @@ static int exfat_load_upcase_table(struct super_block *sb,
 		brelse(bh);
 	}
 
-	if (index >= 0xFFFF && utbl_checksum == chksum)
+	if (index >= 0xFFFF && utbl_checksum == chksum) {
+		/*
+		 * is_default being set does not necessarily mean the contents are exact same as the
+		 * upcase table loaded from the volume may be missing some entries. The checksum
+		 * matching should be enough to cover that case.
+		 */
+		if (is_default && utbl_checksum == EXFAT_DEF_UTBL_CHKSUM) {
+			exfat_free_upcase_ptable(upcase_table);
+			kvfree(upcase_table);
+		} else {
+			sbi->vol_utbl = sbi->vol_utbl_own = upcase_table;
+			exfat_info(sb, "using non-default upcase table (chksum: 0x%08x, entries: %u, memsize: %zu+)",
+				   chksum, entries, upcase_table->cnt * EXFAT_UPTBL_PAGESIZE);
+		}
+
 		return 0;
+	}
 
 	exfat_err(sb, "failed to load upcase table (idx : 0x%08x, chksum : 0x%08x, utbl_chksum : 0x%08x)",
 		  index, chksum, utbl_checksum);
-	return -EINVAL;
-}
-
-static int exfat_load_default_upcase_table(struct super_block *sb)
-{
-	int i;
-	struct exfat_sb_info *sbi = EXFAT_SB(sb);
-	unsigned char skip = false;
-	unsigned short uni = 0, *upcase_table;
-	unsigned int index = 0;
-
-	upcase_table = kvcalloc(EXFAT_UTBL_COUNT, sizeof(unsigned short), GFP_KERNEL);
-	if (!upcase_table)
-		return -ENOMEM;
-
-	sbi->vol_utbl = upcase_table;
-
-	for (i = 0; index <= 0xFFFF && i < EXFAT_NUM_UPCASE; i++) {
-		uni = exfat_uni_def_upcase[i];
-		if (skip) {
-			index += uni;
-			skip = false;
-		} else if (uni == index) {
-			index++;
-		} else if (uni == 0xFFFF) {
-			skip = true;
-		} else {
-			upcase_table[index] = uni;
-			index++;
-		}
-	}
 
-	if (index >= 0xFFFF)
-		return 0;
+err:
+	exfat_free_upcase_ptable(upcase_table);
+	kvfree(upcase_table);
 
-	/* FATAL error: default upcase table has error */
-	return -EIO;
+	return ret;
 }
 
 int exfat_create_upcase_table(struct super_block *sb)
 {
-	int i, ret;
 	unsigned int tbl_clu, type;
 	sector_t sector;
 	unsigned long long tbl_size, num_sectors;
@@ -357,11 +360,16 @@ int exfat_create_upcase_table(struct super_block *sb)
 	struct exfat_sb_info *sbi = EXFAT_SB(sb);
 	struct buffer_head *bh;
 
+	/* fallback to the default table on error */
+	sbi->vol_utbl = &default_upcase_table;
+
 	clu.dir = sbi->root_dir;
 	clu.flags = ALLOC_FAT_CHAIN;
 
 	while (clu.dir != EXFAT_EOF_CLUSTER) {
-		for (i = 0; i < sbi->dentries_per_clu; i++) {
+		for (unsigned int i = 0; i < sbi->dentries_per_clu; i++) {
+			int ret = 0;
+
 			ep = exfat_get_dentry(sb, &clu, i, &bh);
 			if (!ep)
 				return -EIO;
@@ -384,20 +392,13 @@ int exfat_create_upcase_table(struct super_block *sb)
 				num_sectors = ((tbl_size - 1) >> blksize_bits) + 1;
 				ret = exfat_load_upcase_table(sb, sector, num_sectors,
 					le32_to_cpu(ep->dentry.upcase.checksum));
-			} else {
+			} else
 				exfat_fs_error(sb,
 					       "bad upcase table size (0 bytes). Please run fsck");
-				ret = -EINVAL;
-			}
-			brelse(bh);
 
-			if (ret && ret != -EIO) {
-				/* free memory from exfat_load_upcase_table call */
-				exfat_free_upcase_table(sbi);
-				goto load_default;
-			}
-
-			/* load successfully */
+			brelse(bh);
+			if (ret && ret != -EIO)
+				ret = 0;
 			return ret;
 		}
 
@@ -407,13 +408,32 @@ int exfat_create_upcase_table(struct super_block *sb)
 
 	exfat_fs_error(sb, "no upcase table entry. Please run fsck");
 
-load_default:
-	/* load default upcase table */
-	return exfat_load_default_upcase_table(sb);
+	return 0;
 }
 
 void exfat_free_upcase_table(struct exfat_sb_info *sbi)
 {
-	kvfree(sbi->vol_utbl);
-	sbi->vol_utbl = NULL;
+	exfat_free_upcase_ptable(sbi->vol_utbl_own);
+	kvfree(sbi->vol_utbl_own);
+	sbi->vol_utbl = sbi->vol_utbl_own = NULL;
+}
+
+int exfat_init_default_upcase_ptable(void)
+{
+	int err;
+
+	err = exfat_populate_upcase_ptable(&default_upcase_table,
+					   exfat_def_utbl_ri,
+					   EXFAT_DEF_UTBL_RI_COUNT);
+	if (err) {
+		exfat_free_upcase_ptable(&default_upcase_table);
+		return err;
+	}
+
+	return 0;
+}
+
+void exfat_free_default_upcase_ptable(void)
+{
+	exfat_free_upcase_ptable(&default_upcase_table);
 }
diff --git a/fs/exfat/super.c b/fs/exfat/super.c
index 95d87e2d7717..200d93bd5abd 100644
--- a/fs/exfat/super.c
+++ b/fs/exfat/super.c
@@ -896,6 +896,12 @@ static int __init init_exfat_fs(void)
 {
 	int err;
 
+	err = exfat_init_default_upcase_ptable();
+	if (err) {
+		WARN_ON(err == -EINVAL);
+		return err;
+	}
+
 	err = exfat_cache_init();
 	if (err)
 		return err;
@@ -932,6 +938,7 @@ static void __exit exit_exfat_fs(void)
 	kmem_cache_destroy(exfat_inode_cachep);
 	unregister_filesystem(&exfat_fs_type);
 	exfat_cache_shutdown();
+	exfat_free_default_upcase_ptable();
 }
 
 module_init(init_exfat_fs);
diff --git a/fs/exfat/tables.c b/fs/exfat/tables.c
index d8efade76979..b62f0f5ad6e4 100644
--- a/fs/exfat/tables.c
+++ b/fs/exfat/tables.c
@@ -6,376 +6,828 @@
 #include "exfat_fs.h"
 
 /*
- * Upcase table in compressed format (7.2.5.1 Recommended Up-case Table
- * in exfat specification, See:
- * https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification).
+ * The recommended upcase table(7.2.5.1 Recommended Up-case Table in exfat
+ * specification available at
+ * https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification),
+ * expressed in Linux's own format.
+ *
+ * It is found that the table contains following errors or subtle caveats.
+ *
+ *   - Uppercase converted to lowercase
+ *     - U+023A -> U+2C65
+ *     - U+023E -> U+2C66
+ *     - U+1FCC -> U+1FC3
+ *     - U+1FFC -> U+1FF3
+ *   - Letters that have multiple corresponding lower or upper case letters(Greek letter sigma)
+ *     - U+03C2 -> U+03C3
+ *     - U+03C3 -> U+03C2
+ *
+ * To maintain interoperability, these errors are not corrected.
  */
-const unsigned short exfat_uni_def_upcase[EXFAT_NUM_UPCASE] = {
-	0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
-	0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
-	0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
-	0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
-	0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
-	0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
-	0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
-	0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
-	0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
-	0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
-	0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
-	0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
-	0x0060, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
-	0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
-	0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
-	0x0058, 0x0059, 0x005a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
-	0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
-	0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
-	0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
-	0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
-	0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
-	0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
-	0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
-	0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
-	0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
-	0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
-	0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
-	0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
-	0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
-	0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
-	0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00f7,
-	0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x0178,
-	0x0100, 0x0100, 0x0102, 0x0102, 0x0104, 0x0104, 0x0106, 0x0106,
-	0x0108, 0x0108, 0x010a, 0x010a, 0x010c, 0x010c, 0x010e, 0x010e,
-	0x0110, 0x0110, 0x0112, 0x0112, 0x0114, 0x0114, 0x0116, 0x0116,
-	0x0118, 0x0118, 0x011a, 0x011a, 0x011c, 0x011c, 0x011e, 0x011e,
-	0x0120, 0x0120, 0x0122, 0x0122, 0x0124, 0x0124, 0x0126, 0x0126,
-	0x0128, 0x0128, 0x012a, 0x012a, 0x012c, 0x012c, 0x012e, 0x012e,
-	0x0130, 0x0131, 0x0132, 0x0132, 0x0134, 0x0134, 0x0136, 0x0136,
-	0x0138, 0x0139, 0x0139, 0x013b, 0x013b, 0x013d, 0x013d, 0x013f,
-	0x013f, 0x0141, 0x0141, 0x0143, 0x0143, 0x0145, 0x0145, 0x0147,
-	0x0147, 0x0149, 0x014a, 0x014a, 0x014c, 0x014c, 0x014e, 0x014e,
-	0x0150, 0x0150, 0x0152, 0x0152, 0x0154, 0x0154, 0x0156, 0x0156,
-	0x0158, 0x0158, 0x015a, 0x015a, 0x015c, 0x015c, 0x015e, 0x015e,
-	0x0160, 0x0160, 0x0162, 0x0162, 0x0164, 0x0164, 0x0166, 0x0166,
-	0x0168, 0x0168, 0x016a, 0x016a, 0x016c, 0x016c, 0x016e, 0x016e,
-	0x0170, 0x0170, 0x0172, 0x0172, 0x0174, 0x0174, 0x0176, 0x0176,
-	0x0178, 0x0179, 0x0179, 0x017b, 0x017b, 0x017d, 0x017d, 0x017f,
-	0x0243, 0x0181, 0x0182, 0x0182, 0x0184, 0x0184, 0x0186, 0x0187,
-	0x0187, 0x0189, 0x018a, 0x018b, 0x018b, 0x018d, 0x018e, 0x018f,
-	0x0190, 0x0191, 0x0191, 0x0193, 0x0194, 0x01f6, 0x0196, 0x0197,
-	0x0198, 0x0198, 0x023d, 0x019b, 0x019c, 0x019d, 0x0220, 0x019f,
-	0x01a0, 0x01a0, 0x01a2, 0x01a2, 0x01a4, 0x01a4, 0x01a6, 0x01a7,
-	0x01a7, 0x01a9, 0x01aa, 0x01ab, 0x01ac, 0x01ac, 0x01ae, 0x01af,
-	0x01af, 0x01b1, 0x01b2, 0x01b3, 0x01b3, 0x01b5, 0x01b5, 0x01b7,
-	0x01b8, 0x01b8, 0x01ba, 0x01bb, 0x01bc, 0x01bc, 0x01be, 0x01f7,
-	0x01c0, 0x01c1, 0x01c2, 0x01c3, 0x01c4, 0x01c5, 0x01c4, 0x01c7,
-	0x01c8, 0x01c7, 0x01ca, 0x01cb, 0x01ca, 0x01cd, 0x01cd, 0x01cf,
-	0x01cf, 0x01d1, 0x01d1, 0x01d3, 0x01d3, 0x01d5, 0x01d5, 0x01d7,
-	0x01d7, 0x01d9, 0x01d9, 0x01db, 0x01db, 0x018e, 0x01de, 0x01de,
-	0x01e0, 0x01e0, 0x01e2, 0x01e2, 0x01e4, 0x01e4, 0x01e6, 0x01e6,
-	0x01e8, 0x01e8, 0x01ea, 0x01ea, 0x01ec, 0x01ec, 0x01ee, 0x01ee,
-	0x01f0, 0x01f1, 0x01f2, 0x01f1, 0x01f4, 0x01f4, 0x01f6, 0x01f7,
-	0x01f8, 0x01f8, 0x01fa, 0x01fa, 0x01fc, 0x01fc, 0x01fe, 0x01fe,
-	0x0200, 0x0200, 0x0202, 0x0202, 0x0204, 0x0204, 0x0206, 0x0206,
-	0x0208, 0x0208, 0x020a, 0x020a, 0x020c, 0x020c, 0x020e, 0x020e,
-	0x0210, 0x0210, 0x0212, 0x0212, 0x0214, 0x0214, 0x0216, 0x0216,
-	0x0218, 0x0218, 0x021a, 0x021a, 0x021c, 0x021c, 0x021e, 0x021e,
-	0x0220, 0x0221, 0x0222, 0x0222, 0x0224, 0x0224, 0x0226, 0x0226,
-	0x0228, 0x0228, 0x022a, 0x022a, 0x022c, 0x022c, 0x022e, 0x022e,
-	0x0230, 0x0230, 0x0232, 0x0232, 0x0234, 0x0235, 0x0236, 0x0237,
-	0x0238, 0x0239, 0x2c65, 0x023b, 0x023b, 0x023d, 0x2c66, 0x023f,
-	0x0240, 0x0241, 0x0241, 0x0243, 0x0244, 0x0245, 0x0246, 0x0246,
-	0x0248, 0x0248, 0x024a, 0x024a, 0x024c, 0x024c, 0x024e, 0x024e,
-	0x0250, 0x0251, 0x0252, 0x0181, 0x0186, 0x0255, 0x0189, 0x018a,
-	0x0258, 0x018f, 0x025a, 0x0190, 0x025c, 0x025d, 0x025e, 0x025f,
-	0x0193, 0x0261, 0x0262, 0x0194, 0x0264, 0x0265, 0x0266, 0x0267,
-	0x0197, 0x0196, 0x026a, 0x2c62, 0x026c, 0x026d, 0x026e, 0x019c,
-	0x0270, 0x0271, 0x019d, 0x0273, 0x0274, 0x019f, 0x0276, 0x0277,
-	0x0278, 0x0279, 0x027a, 0x027b, 0x027c, 0x2c64, 0x027e, 0x027f,
-	0x01a6, 0x0281, 0x0282, 0x01a9, 0x0284, 0x0285, 0x0286, 0x0287,
-	0x01ae, 0x0244, 0x01b1, 0x01b2, 0x0245, 0x028d, 0x028e, 0x028f,
-	0x0290, 0x0291, 0x01b7, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297,
-	0x0298, 0x0299, 0x029a, 0x029b, 0x029c, 0x029d, 0x029e, 0x029f,
-	0x02a0, 0x02a1, 0x02a2, 0x02a3, 0x02a4, 0x02a5, 0x02a6, 0x02a7,
-	0x02a8, 0x02a9, 0x02aa, 0x02ab, 0x02ac, 0x02ad, 0x02ae, 0x02af,
-	0x02b0, 0x02b1, 0x02b2, 0x02b3, 0x02b4, 0x02b5, 0x02b6, 0x02b7,
-	0x02b8, 0x02b9, 0x02ba, 0x02bb, 0x02bc, 0x02bd, 0x02be, 0x02bf,
-	0x02c0, 0x02c1, 0x02c2, 0x02c3, 0x02c4, 0x02c5, 0x02c6, 0x02c7,
-	0x02c8, 0x02c9, 0x02ca, 0x02cb, 0x02cc, 0x02cd, 0x02ce, 0x02cf,
-	0x02d0, 0x02d1, 0x02d2, 0x02d3, 0x02d4, 0x02d5, 0x02d6, 0x02d7,
-	0x02d8, 0x02d9, 0x02da, 0x02db, 0x02dc, 0x02dd, 0x02de, 0x02df,
-	0x02e0, 0x02e1, 0x02e2, 0x02e3, 0x02e4, 0x02e5, 0x02e6, 0x02e7,
-	0x02e8, 0x02e9, 0x02ea, 0x02eb, 0x02ec, 0x02ed, 0x02ee, 0x02ef,
-	0x02f0, 0x02f1, 0x02f2, 0x02f3, 0x02f4, 0x02f5, 0x02f6, 0x02f7,
-	0x02f8, 0x02f9, 0x02fa, 0x02fb, 0x02fc, 0x02fd, 0x02fe, 0x02ff,
-	0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307,
-	0x0308, 0x0309, 0x030a, 0x030b, 0x030c, 0x030d, 0x030e, 0x030f,
-	0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317,
-	0x0318, 0x0319, 0x031a, 0x031b, 0x031c, 0x031d, 0x031e, 0x031f,
-	0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327,
-	0x0328, 0x0329, 0x032a, 0x032b, 0x032c, 0x032d, 0x032e, 0x032f,
-	0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337,
-	0x0338, 0x0339, 0x033a, 0x033b, 0x033c, 0x033d, 0x033e, 0x033f,
-	0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347,
-	0x0348, 0x0349, 0x034a, 0x034b, 0x034c, 0x034d, 0x034e, 0x034f,
-	0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357,
-	0x0358, 0x0359, 0x035a, 0x035b, 0x035c, 0x035d, 0x035e, 0x035f,
-	0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367,
-	0x0368, 0x0369, 0x036a, 0x036b, 0x036c, 0x036d, 0x036e, 0x036f,
-	0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377,
-	0x0378, 0x0379, 0x037a, 0x03fd, 0x03fe, 0x03ff, 0x037e, 0x037f,
-	0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0386, 0x0387,
-	0x0388, 0x0389, 0x038a, 0x038b, 0x038c, 0x038d, 0x038e, 0x038f,
-	0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
-	0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f,
-	0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7,
-	0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x0386, 0x0388, 0x0389, 0x038a,
-	0x03b0, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
-	0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f,
-	0x03a0, 0x03a1, 0x03a3, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7,
-	0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x038c, 0x038e, 0x038f, 0x03cf,
-	0x03d0, 0x03d1, 0x03d2, 0x03d3, 0x03d4, 0x03d5, 0x03d6, 0x03d7,
-	0x03d8, 0x03d8, 0x03da, 0x03da, 0x03dc, 0x03dc, 0x03de, 0x03de,
-	0x03e0, 0x03e0, 0x03e2, 0x03e2, 0x03e4, 0x03e4, 0x03e6, 0x03e6,
-	0x03e8, 0x03e8, 0x03ea, 0x03ea, 0x03ec, 0x03ec, 0x03ee, 0x03ee,
-	0x03f0, 0x03f1, 0x03f9, 0x03f3, 0x03f4, 0x03f5, 0x03f6, 0x03f7,
-	0x03f7, 0x03f9, 0x03fa, 0x03fa, 0x03fc, 0x03fd, 0x03fe, 0x03ff,
-	0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407,
-	0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x040d, 0x040e, 0x040f,
-	0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
-	0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f,
-	0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
-	0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f,
-	0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
-	0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f,
-	0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
-	0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f,
-	0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407,
-	0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x040d, 0x040e, 0x040f,
-	0x0460, 0x0460, 0x0462, 0x0462, 0x0464, 0x0464, 0x0466, 0x0466,
-	0x0468, 0x0468, 0x046a, 0x046a, 0x046c, 0x046c, 0x046e, 0x046e,
-	0x0470, 0x0470, 0x0472, 0x0472, 0x0474, 0x0474, 0x0476, 0x0476,
-	0x0478, 0x0478, 0x047a, 0x047a, 0x047c, 0x047c, 0x047e, 0x047e,
-	0x0480, 0x0480, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487,
-	0x0488, 0x0489, 0x048a, 0x048a, 0x048c, 0x048c, 0x048e, 0x048e,
-	0x0490, 0x0490, 0x0492, 0x0492, 0x0494, 0x0494, 0x0496, 0x0496,
-	0x0498, 0x0498, 0x049a, 0x049a, 0x049c, 0x049c, 0x049e, 0x049e,
-	0x04a0, 0x04a0, 0x04a2, 0x04a2, 0x04a4, 0x04a4, 0x04a6, 0x04a6,
-	0x04a8, 0x04a8, 0x04aa, 0x04aa, 0x04ac, 0x04ac, 0x04ae, 0x04ae,
-	0x04b0, 0x04b0, 0x04b2, 0x04b2, 0x04b4, 0x04b4, 0x04b6, 0x04b6,
-	0x04b8, 0x04b8, 0x04ba, 0x04ba, 0x04bc, 0x04bc, 0x04be, 0x04be,
-	0x04c0, 0x04c1, 0x04c1, 0x04c3, 0x04c3, 0x04c5, 0x04c5, 0x04c7,
-	0x04c7, 0x04c9, 0x04c9, 0x04cb, 0x04cb, 0x04cd, 0x04cd, 0x04c0,
-	0x04d0, 0x04d0, 0x04d2, 0x04d2, 0x04d4, 0x04d4, 0x04d6, 0x04d6,
-	0x04d8, 0x04d8, 0x04da, 0x04da, 0x04dc, 0x04dc, 0x04de, 0x04de,
-	0x04e0, 0x04e0, 0x04e2, 0x04e2, 0x04e4, 0x04e4, 0x04e6, 0x04e6,
-	0x04e8, 0x04e8, 0x04ea, 0x04ea, 0x04ec, 0x04ec, 0x04ee, 0x04ee,
-	0x04f0, 0x04f0, 0x04f2, 0x04f2, 0x04f4, 0x04f4, 0x04f6, 0x04f6,
-	0x04f8, 0x04f8, 0x04fa, 0x04fa, 0x04fc, 0x04fc, 0x04fe, 0x04fe,
-	0x0500, 0x0500, 0x0502, 0x0502, 0x0504, 0x0504, 0x0506, 0x0506,
-	0x0508, 0x0508, 0x050a, 0x050a, 0x050c, 0x050c, 0x050e, 0x050e,
-	0x0510, 0x0510, 0x0512, 0x0512, 0x0514, 0x0515, 0x0516, 0x0517,
-	0x0518, 0x0519, 0x051a, 0x051b, 0x051c, 0x051d, 0x051e, 0x051f,
-	0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527,
-	0x0528, 0x0529, 0x052a, 0x052b, 0x052c, 0x052d, 0x052e, 0x052f,
-	0x0530, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537,
-	0x0538, 0x0539, 0x053a, 0x053b, 0x053c, 0x053d, 0x053e, 0x053f,
-	0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547,
-	0x0548, 0x0549, 0x054a, 0x054b, 0x054c, 0x054d, 0x054e, 0x054f,
-	0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556, 0x0557,
-	0x0558, 0x0559, 0x055a, 0x055b, 0x055c, 0x055d, 0x055e, 0x055f,
-	0x0560, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537,
-	0x0538, 0x0539, 0x053a, 0x053b, 0x053c, 0x053d, 0x053e, 0x053f,
-	0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547,
-	0x0548, 0x0549, 0x054a, 0x054b, 0x054c, 0x054d, 0x054e, 0x054f,
-	0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556, 0xffff,
-	0x17f6, 0x2c63, 0x1d7e, 0x1d7f, 0x1d80, 0x1d81, 0x1d82, 0x1d83,
-	0x1d84, 0x1d85, 0x1d86, 0x1d87, 0x1d88, 0x1d89, 0x1d8a, 0x1d8b,
-	0x1d8c, 0x1d8d, 0x1d8e, 0x1d8f, 0x1d90, 0x1d91, 0x1d92, 0x1d93,
-	0x1d94, 0x1d95, 0x1d96, 0x1d97, 0x1d98, 0x1d99, 0x1d9a, 0x1d9b,
-	0x1d9c, 0x1d9d, 0x1d9e, 0x1d9f, 0x1da0, 0x1da1, 0x1da2, 0x1da3,
-	0x1da4, 0x1da5, 0x1da6, 0x1da7, 0x1da8, 0x1da9, 0x1daa, 0x1dab,
-	0x1dac, 0x1dad, 0x1dae, 0x1daf, 0x1db0, 0x1db1, 0x1db2, 0x1db3,
-	0x1db4, 0x1db5, 0x1db6, 0x1db7, 0x1db8, 0x1db9, 0x1dba, 0x1dbb,
-	0x1dbc, 0x1dbd, 0x1dbe, 0x1dbf, 0x1dc0, 0x1dc1, 0x1dc2, 0x1dc3,
-	0x1dc4, 0x1dc5, 0x1dc6, 0x1dc7, 0x1dc8, 0x1dc9, 0x1dca, 0x1dcb,
-	0x1dcc, 0x1dcd, 0x1dce, 0x1dcf, 0x1dd0, 0x1dd1, 0x1dd2, 0x1dd3,
-	0x1dd4, 0x1dd5, 0x1dd6, 0x1dd7, 0x1dd8, 0x1dd9, 0x1dda, 0x1ddb,
-	0x1ddc, 0x1ddd, 0x1dde, 0x1ddf, 0x1de0, 0x1de1, 0x1de2, 0x1de3,
-	0x1de4, 0x1de5, 0x1de6, 0x1de7, 0x1de8, 0x1de9, 0x1dea, 0x1deb,
-	0x1dec, 0x1ded, 0x1dee, 0x1def, 0x1df0, 0x1df1, 0x1df2, 0x1df3,
-	0x1df4, 0x1df5, 0x1df6, 0x1df7, 0x1df8, 0x1df9, 0x1dfa, 0x1dfb,
-	0x1dfc, 0x1dfd, 0x1dfe, 0x1dff, 0x1e00, 0x1e00, 0x1e02, 0x1e02,
-	0x1e04, 0x1e04, 0x1e06, 0x1e06, 0x1e08, 0x1e08, 0x1e0a, 0x1e0a,
-	0x1e0c, 0x1e0c, 0x1e0e, 0x1e0e, 0x1e10, 0x1e10, 0x1e12, 0x1e12,
-	0x1e14, 0x1e14, 0x1e16, 0x1e16, 0x1e18, 0x1e18, 0x1e1a, 0x1e1a,
-	0x1e1c, 0x1e1c, 0x1e1e, 0x1e1e, 0x1e20, 0x1e20, 0x1e22, 0x1e22,
-	0x1e24, 0x1e24, 0x1e26, 0x1e26, 0x1e28, 0x1e28, 0x1e2a, 0x1e2a,
-	0x1e2c, 0x1e2c, 0x1e2e, 0x1e2e, 0x1e30, 0x1e30, 0x1e32, 0x1e32,
-	0x1e34, 0x1e34, 0x1e36, 0x1e36, 0x1e38, 0x1e38, 0x1e3a, 0x1e3a,
-	0x1e3c, 0x1e3c, 0x1e3e, 0x1e3e, 0x1e40, 0x1e40, 0x1e42, 0x1e42,
-	0x1e44, 0x1e44, 0x1e46, 0x1e46, 0x1e48, 0x1e48, 0x1e4a, 0x1e4a,
-	0x1e4c, 0x1e4c, 0x1e4e, 0x1e4e, 0x1e50, 0x1e50, 0x1e52, 0x1e52,
-	0x1e54, 0x1e54, 0x1e56, 0x1e56, 0x1e58, 0x1e58, 0x1e5a, 0x1e5a,
-	0x1e5c, 0x1e5c, 0x1e5e, 0x1e5e, 0x1e60, 0x1e60, 0x1e62, 0x1e62,
-	0x1e64, 0x1e64, 0x1e66, 0x1e66, 0x1e68, 0x1e68, 0x1e6a, 0x1e6a,
-	0x1e6c, 0x1e6c, 0x1e6e, 0x1e6e, 0x1e70, 0x1e70, 0x1e72, 0x1e72,
-	0x1e74, 0x1e74, 0x1e76, 0x1e76, 0x1e78, 0x1e78, 0x1e7a, 0x1e7a,
-	0x1e7c, 0x1e7c, 0x1e7e, 0x1e7e, 0x1e80, 0x1e80, 0x1e82, 0x1e82,
-	0x1e84, 0x1e84, 0x1e86, 0x1e86, 0x1e88, 0x1e88, 0x1e8a, 0x1e8a,
-	0x1e8c, 0x1e8c, 0x1e8e, 0x1e8e, 0x1e90, 0x1e90, 0x1e92, 0x1e92,
-	0x1e94, 0x1e94, 0x1e96, 0x1e97, 0x1e98, 0x1e99, 0x1e9a, 0x1e9b,
-	0x1e9c, 0x1e9d, 0x1e9e, 0x1e9f, 0x1ea0, 0x1ea0, 0x1ea2, 0x1ea2,
-	0x1ea4, 0x1ea4, 0x1ea6, 0x1ea6, 0x1ea8, 0x1ea8, 0x1eaa, 0x1eaa,
-	0x1eac, 0x1eac, 0x1eae, 0x1eae, 0x1eb0, 0x1eb0, 0x1eb2, 0x1eb2,
-	0x1eb4, 0x1eb4, 0x1eb6, 0x1eb6, 0x1eb8, 0x1eb8, 0x1eba, 0x1eba,
-	0x1ebc, 0x1ebc, 0x1ebe, 0x1ebe, 0x1ec0, 0x1ec0, 0x1ec2, 0x1ec2,
-	0x1ec4, 0x1ec4, 0x1ec6, 0x1ec6, 0x1ec8, 0x1ec8, 0x1eca, 0x1eca,
-	0x1ecc, 0x1ecc, 0x1ece, 0x1ece, 0x1ed0, 0x1ed0, 0x1ed2, 0x1ed2,
-	0x1ed4, 0x1ed4, 0x1ed6, 0x1ed6, 0x1ed8, 0x1ed8, 0x1eda, 0x1eda,
-	0x1edc, 0x1edc, 0x1ede, 0x1ede, 0x1ee0, 0x1ee0, 0x1ee2, 0x1ee2,
-	0x1ee4, 0x1ee4, 0x1ee6, 0x1ee6, 0x1ee8, 0x1ee8, 0x1eea, 0x1eea,
-	0x1eec, 0x1eec, 0x1eee, 0x1eee, 0x1ef0, 0x1ef0, 0x1ef2, 0x1ef2,
-	0x1ef4, 0x1ef4, 0x1ef6, 0x1ef6, 0x1ef8, 0x1ef8, 0x1efa, 0x1efb,
-	0x1efc, 0x1efd, 0x1efe, 0x1eff, 0x1f08, 0x1f09, 0x1f0a, 0x1f0b,
-	0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, 0x1f08, 0x1f09, 0x1f0a, 0x1f0b,
-	0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, 0x1f18, 0x1f19, 0x1f1a, 0x1f1b,
-	0x1f1c, 0x1f1d, 0x1f16, 0x1f17, 0x1f18, 0x1f19, 0x1f1a, 0x1f1b,
-	0x1f1c, 0x1f1d, 0x1f1e, 0x1f1f, 0x1f28, 0x1f29, 0x1f2a, 0x1f2b,
-	0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, 0x1f28, 0x1f29, 0x1f2a, 0x1f2b,
-	0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, 0x1f38, 0x1f39, 0x1f3a, 0x1f3b,
-	0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, 0x1f38, 0x1f39, 0x1f3a, 0x1f3b,
-	0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, 0x1f48, 0x1f49, 0x1f4a, 0x1f4b,
-	0x1f4c, 0x1f4d, 0x1f46, 0x1f47, 0x1f48, 0x1f49, 0x1f4a, 0x1f4b,
-	0x1f4c, 0x1f4d, 0x1f4e, 0x1f4f, 0x1f50, 0x1f59, 0x1f52, 0x1f5b,
-	0x1f54, 0x1f5d, 0x1f56, 0x1f5f, 0x1f58, 0x1f59, 0x1f5a, 0x1f5b,
-	0x1f5c, 0x1f5d, 0x1f5e, 0x1f5f, 0x1f68, 0x1f69, 0x1f6a, 0x1f6b,
-	0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, 0x1f68, 0x1f69, 0x1f6a, 0x1f6b,
-	0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, 0x1fba, 0x1fbb, 0x1fc8, 0x1fc9,
-	0x1fca, 0x1fcb, 0x1fda, 0x1fdb, 0x1ff8, 0x1ff9, 0x1fea, 0x1feb,
-	0x1ffa, 0x1ffb, 0x1f7e, 0x1f7f, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b,
-	0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b,
-	0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b,
-	0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b,
-	0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab,
-	0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab,
-	0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fb8, 0x1fb9, 0x1fb2, 0x1fbc,
-	0x1fb4, 0x1fb5, 0x1fb6, 0x1fb7, 0x1fb8, 0x1fb9, 0x1fba, 0x1fbb,
-	0x1fbc, 0x1fbd, 0x1fbe, 0x1fbf, 0x1fc0, 0x1fc1, 0x1fc2, 0x1fc3,
-	0x1fc4, 0x1fc5, 0x1fc6, 0x1fc7, 0x1fc8, 0x1fc9, 0x1fca, 0x1fcb,
-	0x1fc3, 0x1fcd, 0x1fce, 0x1fcf, 0x1fd8, 0x1fd9, 0x1fd2, 0x1fd3,
-	0x1fd4, 0x1fd5, 0x1fd6, 0x1fd7, 0x1fd8, 0x1fd9, 0x1fda, 0x1fdb,
-	0x1fdc, 0x1fdd, 0x1fde, 0x1fdf, 0x1fe8, 0x1fe9, 0x1fe2, 0x1fe3,
-	0x1fe4, 0x1fec, 0x1fe6, 0x1fe7, 0x1fe8, 0x1fe9, 0x1fea, 0x1feb,
-	0x1fec, 0x1fed, 0x1fee, 0x1fef, 0x1ff0, 0x1ff1, 0x1ff2, 0x1ff3,
-	0x1ff4, 0x1ff5, 0x1ff6, 0x1ff7, 0x1ff8, 0x1ff9, 0x1ffa, 0x1ffb,
-	0x1ff3, 0x1ffd, 0x1ffe, 0x1fff, 0x2000, 0x2001, 0x2002, 0x2003,
-	0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200a, 0x200b,
-	0x200c, 0x200d, 0x200e, 0x200f, 0x2010, 0x2011, 0x2012, 0x2013,
-	0x2014, 0x2015, 0x2016, 0x2017, 0x2018, 0x2019, 0x201a, 0x201b,
-	0x201c, 0x201d, 0x201e, 0x201f, 0x2020, 0x2021, 0x2022, 0x2023,
-	0x2024, 0x2025, 0x2026, 0x2027, 0x2028, 0x2029, 0x202a, 0x202b,
-	0x202c, 0x202d, 0x202e, 0x202f, 0x2030, 0x2031, 0x2032, 0x2033,
-	0x2034, 0x2035, 0x2036, 0x2037, 0x2038, 0x2039, 0x203a, 0x203b,
-	0x203c, 0x203d, 0x203e, 0x203f, 0x2040, 0x2041, 0x2042, 0x2043,
-	0x2044, 0x2045, 0x2046, 0x2047, 0x2048, 0x2049, 0x204a, 0x204b,
-	0x204c, 0x204d, 0x204e, 0x204f, 0x2050, 0x2051, 0x2052, 0x2053,
-	0x2054, 0x2055, 0x2056, 0x2057, 0x2058, 0x2059, 0x205a, 0x205b,
-	0x205c, 0x205d, 0x205e, 0x205f, 0x2060, 0x2061, 0x2062, 0x2063,
-	0x2064, 0x2065, 0x2066, 0x2067, 0x2068, 0x2069, 0x206a, 0x206b,
-	0x206c, 0x206d, 0x206e, 0x206f, 0x2070, 0x2071, 0x2072, 0x2073,
-	0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, 0x207a, 0x207b,
-	0x207c, 0x207d, 0x207e, 0x207f, 0x2080, 0x2081, 0x2082, 0x2083,
-	0x2084, 0x2085, 0x2086, 0x2087, 0x2088, 0x2089, 0x208a, 0x208b,
-	0x208c, 0x208d, 0x208e, 0x208f, 0x2090, 0x2091, 0x2092, 0x2093,
-	0x2094, 0x2095, 0x2096, 0x2097, 0x2098, 0x2099, 0x209a, 0x209b,
-	0x209c, 0x209d, 0x209e, 0x209f, 0x20a0, 0x20a1, 0x20a2, 0x20a3,
-	0x20a4, 0x20a5, 0x20a6, 0x20a7, 0x20a8, 0x20a9, 0x20aa, 0x20ab,
-	0x20ac, 0x20ad, 0x20ae, 0x20af, 0x20b0, 0x20b1, 0x20b2, 0x20b3,
-	0x20b4, 0x20b5, 0x20b6, 0x20b7, 0x20b8, 0x20b9, 0x20ba, 0x20bb,
-	0x20bc, 0x20bd, 0x20be, 0x20bf, 0x20c0, 0x20c1, 0x20c2, 0x20c3,
-	0x20c4, 0x20c5, 0x20c6, 0x20c7, 0x20c8, 0x20c9, 0x20ca, 0x20cb,
-	0x20cc, 0x20cd, 0x20ce, 0x20cf, 0x20d0, 0x20d1, 0x20d2, 0x20d3,
-	0x20d4, 0x20d5, 0x20d6, 0x20d7, 0x20d8, 0x20d9, 0x20da, 0x20db,
-	0x20dc, 0x20dd, 0x20de, 0x20df, 0x20e0, 0x20e1, 0x20e2, 0x20e3,
-	0x20e4, 0x20e5, 0x20e6, 0x20e7, 0x20e8, 0x20e9, 0x20ea, 0x20eb,
-	0x20ec, 0x20ed, 0x20ee, 0x20ef, 0x20f0, 0x20f1, 0x20f2, 0x20f3,
-	0x20f4, 0x20f5, 0x20f6, 0x20f7, 0x20f8, 0x20f9, 0x20fa, 0x20fb,
-	0x20fc, 0x20fd, 0x20fe, 0x20ff, 0x2100, 0x2101, 0x2102, 0x2103,
-	0x2104, 0x2105, 0x2106, 0x2107, 0x2108, 0x2109, 0x210a, 0x210b,
-	0x210c, 0x210d, 0x210e, 0x210f, 0x2110, 0x2111, 0x2112, 0x2113,
-	0x2114, 0x2115, 0x2116, 0x2117, 0x2118, 0x2119, 0x211a, 0x211b,
-	0x211c, 0x211d, 0x211e, 0x211f, 0x2120, 0x2121, 0x2122, 0x2123,
-	0x2124, 0x2125, 0x2126, 0x2127, 0x2128, 0x2129, 0x212a, 0x212b,
-	0x212c, 0x212d, 0x212e, 0x212f, 0x2130, 0x2131, 0x2132, 0x2133,
-	0x2134, 0x2135, 0x2136, 0x2137, 0x2138, 0x2139, 0x213a, 0x213b,
-	0x213c, 0x213d, 0x213e, 0x213f, 0x2140, 0x2141, 0x2142, 0x2143,
-	0x2144, 0x2145, 0x2146, 0x2147, 0x2148, 0x2149, 0x214a, 0x214b,
-	0x214c, 0x214d, 0x2132, 0x214f, 0x2150, 0x2151, 0x2152, 0x2153,
-	0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a, 0x215b,
-	0x215c, 0x215d, 0x215e, 0x215f, 0x2160, 0x2161, 0x2162, 0x2163,
-	0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216a, 0x216b,
-	0x216c, 0x216d, 0x216e, 0x216f, 0x2160, 0x2161, 0x2162, 0x2163,
-	0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216a, 0x216b,
-	0x216c, 0x216d, 0x216e, 0x216f, 0x2180, 0x2181, 0x2182, 0x2183,
-	0x2183, 0xffff, 0x034b, 0x24b6, 0x24b7, 0x24b8, 0x24b9, 0x24ba,
-	0x24bb, 0x24bc, 0x24bd, 0x24be, 0x24bf, 0x24c0, 0x24c1, 0x24c2,
-	0x24c3, 0x24c4, 0x24c5, 0x24c6, 0x24c7, 0x24c8, 0x24c9, 0x24ca,
-	0x24cb, 0x24cc, 0x24cd, 0x24ce, 0x24cf, 0xffff, 0x0746, 0x2c00,
-	0x2c01, 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x2c08,
-	0x2c09, 0x2c0a, 0x2c0b, 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x2c10,
-	0x2c11, 0x2c12, 0x2c13, 0x2c14, 0x2c15, 0x2c16, 0x2c17, 0x2c18,
-	0x2c19, 0x2c1a, 0x2c1b, 0x2c1c, 0x2c1d, 0x2c1e, 0x2c1f, 0x2c20,
-	0x2c21, 0x2c22, 0x2c23, 0x2c24, 0x2c25, 0x2c26, 0x2c27, 0x2c28,
-	0x2c29, 0x2c2a, 0x2c2b, 0x2c2c, 0x2c2d, 0x2c2e, 0x2c5f, 0x2c60,
-	0x2c60, 0x2c62, 0x2c63, 0x2c64, 0x2c65, 0x2c66, 0x2c67, 0x2c67,
-	0x2c69, 0x2c69, 0x2c6b, 0x2c6b, 0x2c6d, 0x2c6e, 0x2c6f, 0x2c70,
-	0x2c71, 0x2c72, 0x2c73, 0x2c74, 0x2c75, 0x2c75, 0x2c77, 0x2c78,
-	0x2c79, 0x2c7a, 0x2c7b, 0x2c7c, 0x2c7d, 0x2c7e, 0x2c7f, 0x2c80,
-	0x2c80, 0x2c82, 0x2c82, 0x2c84, 0x2c84, 0x2c86, 0x2c86, 0x2c88,
-	0x2c88, 0x2c8a, 0x2c8a, 0x2c8c, 0x2c8c, 0x2c8e, 0x2c8e, 0x2c90,
-	0x2c90, 0x2c92, 0x2c92, 0x2c94, 0x2c94, 0x2c96, 0x2c96, 0x2c98,
-	0x2c98, 0x2c9a, 0x2c9a, 0x2c9c, 0x2c9c, 0x2c9e, 0x2c9e, 0x2ca0,
-	0x2ca0, 0x2ca2, 0x2ca2, 0x2ca4, 0x2ca4, 0x2ca6, 0x2ca6, 0x2ca8,
-	0x2ca8, 0x2caa, 0x2caa, 0x2cac, 0x2cac, 0x2cae, 0x2cae, 0x2cb0,
-	0x2cb0, 0x2cb2, 0x2cb2, 0x2cb4, 0x2cb4, 0x2cb6, 0x2cb6, 0x2cb8,
-	0x2cb8, 0x2cba, 0x2cba, 0x2cbc, 0x2cbc, 0x2cbe, 0x2cbe, 0x2cc0,
-	0x2cc0, 0x2cc2, 0x2cc2, 0x2cc4, 0x2cc4, 0x2cc6, 0x2cc6, 0x2cc8,
-	0x2cc8, 0x2cca, 0x2cca, 0x2ccc, 0x2ccc, 0x2cce, 0x2cce, 0x2cd0,
-	0x2cd0, 0x2cd2, 0x2cd2, 0x2cd4, 0x2cd4, 0x2cd6, 0x2cd6, 0x2cd8,
-	0x2cd8, 0x2cda, 0x2cda, 0x2cdc, 0x2cdc, 0x2cde, 0x2cde, 0x2ce0,
-	0x2ce0, 0x2ce2, 0x2ce2, 0x2ce4, 0x2ce5, 0x2ce6, 0x2ce7, 0x2ce8,
-	0x2ce9, 0x2cea, 0x2ceb, 0x2cec, 0x2ced, 0x2cee, 0x2cef, 0x2cf0,
-	0x2cf1, 0x2cf2, 0x2cf3, 0x2cf4, 0x2cf5, 0x2cf6, 0x2cf7, 0x2cf8,
-	0x2cf9, 0x2cfa, 0x2cfb, 0x2cfc, 0x2cfd, 0x2cfe, 0x2cff, 0x10a0,
-	0x10a1, 0x10a2, 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7, 0x10a8,
-	0x10a9, 0x10aa, 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af, 0x10b0,
-	0x10b1, 0x10b2, 0x10b3, 0x10b4, 0x10b5, 0x10b6, 0x10b7, 0x10b8,
-	0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd, 0x10be, 0x10bf, 0x10c0,
-	0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5, 0xffff, 0xd21b, 0xff21,
-	0xff22, 0xff23, 0xff24, 0xff25, 0xff26, 0xff27, 0xff28, 0xff29,
-	0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e, 0xff2f, 0xff30, 0xff31,
-	0xff32, 0xff33, 0xff34, 0xff35, 0xff36, 0xff37, 0xff38, 0xff39,
-	0xff3a, 0xff5b, 0xff5c, 0xff5d, 0xff5e, 0xff5f, 0xff60, 0xff61,
-	0xff62, 0xff63, 0xff64, 0xff65, 0xff66, 0xff67, 0xff68, 0xff69,
-	0xff6a, 0xff6b, 0xff6c, 0xff6d, 0xff6e, 0xff6f, 0xff70, 0xff71,
-	0xff72, 0xff73, 0xff74, 0xff75, 0xff76, 0xff77, 0xff78, 0xff79,
-	0xff7a, 0xff7b, 0xff7c, 0xff7d, 0xff7e, 0xff7f, 0xff80, 0xff81,
-	0xff82, 0xff83, 0xff84, 0xff85, 0xff86, 0xff87, 0xff88, 0xff89,
-	0xff8a, 0xff8b, 0xff8c, 0xff8d, 0xff8e, 0xff8f, 0xff90, 0xff91,
-	0xff92, 0xff93, 0xff94, 0xff95, 0xff96, 0xff97, 0xff98, 0xff99,
-	0xff9a, 0xff9b, 0xff9c, 0xff9d, 0xff9e, 0xff9f, 0xffa0, 0xffa1,
-	0xffa2, 0xffa3, 0xffa4, 0xffa5, 0xffa6, 0xffa7, 0xffa8, 0xffa9,
-	0xffaa, 0xffab, 0xffac, 0xffad, 0xffae, 0xffaf, 0xffb0, 0xffb1,
-	0xffb2, 0xffb3, 0xffb4, 0xffb5, 0xffb6, 0xffb7, 0xffb8, 0xffb9,
-	0xffba, 0xffbb, 0xffbc, 0xffbd, 0xffbe, 0xffbf, 0xffc0, 0xffc1,
-	0xffc2, 0xffc3, 0xffc4, 0xffc5, 0xffc6, 0xffc7, 0xffc8, 0xffc9,
-	0xffca, 0xffcb, 0xffcc, 0xffcd, 0xffce, 0xffcf, 0xffd0, 0xffd1,
-	0xffd2, 0xffd3, 0xffd4, 0xffd5, 0xffd6, 0xffd7, 0xffd8, 0xffd9,
-	0xffda, 0xffdb, 0xffdc, 0xffdd, 0xffde, 0xffdf, 0xffe0, 0xffe1,
-	0xffe2, 0xffe3, 0xffe4, 0xffe5, 0xffe6, 0xffe7, 0xffe8, 0xffe9,
-	0xffea, 0xffeb, 0xffec, 0xffed, 0xffee, 0xffef, 0xfff0, 0xfff1,
-	0xfff2, 0xfff3, 0xfff4, 0xfff5, 0xfff6, 0xfff7, 0xfff8, 0xfff9,
-	0xfffa, 0xfffb, 0xfffc, 0xfffd, 0xfffe, 0xffff,
+const struct exfat_upcase_range_info exfat_def_utbl_ri[EXFAT_DEF_UTBL_RI_COUNT] = {
+	/* ASCII */
+	{
+		/* (index = 0, len = 26) */
+		.start = 0x0061,
+		.end   = 0x007B,
+		.value = 0x0041,
+		.inc   = 0x0001,
+	},
+	/* Latin-1 Supplement */
+	{
+		/* (index = 1, len = 23) */
+		.start = 0x00E0,
+		.end   = 0x00F7,
+		.value = 0x00C0,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 2, len = 7) */
+		.start = 0x00F8,
+		.end   = 0x00FF,
+		.value = 0x00D8,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 3, len = 1) */
+		.start = 0x00FF,
+		.end   = 0x0100,
+		.value = 0x0178,
+		.inc   = 0x0001,
+	},
+	/* Latin Extended-A */
+	{
+		/* (index = 4, len = 47) */
+		.start = 0x0101,
+		.end   = 0x0130,
+		.value = 0x0100,
+		.inc   = 0x0002,
+	},
+	{
+		/* (index = 5, len = 5) */
+		.start = 0x0133,
+		.end   = 0x0138,
+		.value = 0x0132,
+		.inc   = 0x0002,
+	},
+	{
+		/* (index = 6, len = 15) */
+		.start = 0x013A,
+		.end   = 0x0149,
+		.value = 0x0139,
+		.inc   = 0x0002,
+	},
+	{
+		/* (index = 7, len = 45) */
+		.start = 0x014B,
+		.end   = 0x0178,
+		.value = 0x014A,
+		.inc   = 0x0002,
+	},
+	{
+		/* (index = 8, len = 6) */
+		.start = 0x017A,
+		.end   = 0x0180,
+		.value = 0x0179,
+		.inc   = 0x0002,
+	},
+	/* Latin Extended-B */
+	{
+		/* (index = 9, len = 1) */
+		.start = 0x0180,
+		.end   = 0x0181,
+		.value = 0x0243,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 10, len = 3) */
+		.start = 0x0183,
+		.end   = 0x0186,
+		.value = 0x0182,
+		.inc   = 0x0002,
+	},
+	{
+		/* (index = 11, len = 5) */
+		.start = 0x0188,
+		.end   = 0x018D,
+		.value = 0x0187,
+		.inc   = 0x0004,
+	},
+	{
+		/* (index = 12, len = 1) */
+		.start = 0x0192,
+		.end   = 0x0193,
+		.value = 0x0191,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 13, len = 1) */
+		.start = 0x0195,
+		.end   = 0x0196,
+		.value = 0x01F6,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 14, len = 1) */
+		.start = 0x0199,
+		.end   = 0x019A,
+		.value = 0x0198,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 15, len = 1) */
+		.start = 0x019A,
+		.end   = 0x019B,
+		.value = 0x023D,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 16, len = 1) */
+		.start = 0x019E,
+		.end   = 0x019F,
+		.value = 0x0220,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 17, len = 5) */
+		.start = 0x01A1,
+		.end   = 0x01A6,
+		.value = 0x01A0,
+		.inc   = 0x0002,
+	},
+	{
+		/* (index = 18, len = 8) */
+		.start = 0x01A8,
+		.end   = 0x01B0,
+		.value = 0x01A7,
+		.inc   = 0x0005,
+	},
+	{
+		/* (index = 19, len = 6) */
+		.start = 0x01B0,
+		.end   = 0x01B6,
+		.value = 0x01AF,
+		.inc   = 0x0004,
+	},
+	{
+		/* (index = 20, len = 4) */
+		.start = 0x01B6,
+		.end   = 0x01BA,
+		.value = 0x01B5,
+		.inc   = 0x0003,
+	},
+	{
+		/* (index = 21, len = 1) */
+		.start = 0x01BD,
+		.end   = 0x01BE,
+		.value = 0x01BC,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 22, len = 1) */
+		.start = 0x01BF,
+		.end   = 0x01C0,
+		.value = 0x01F7,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 23, len = 8) */
+		.start = 0x01C6,
+		.end   = 0x01CE,
+		.value = 0x01C4,
+		.inc   = 0x0003,
+	},
+	{
+		/* (index = 24, len = 15) */
+		.start = 0x01CE,
+		.end   = 0x01DD,
+		.value = 0x01CD,
+		.inc   = 0x0002,
+	},
+	{
+		/* (index = 25, len = 1) */
+		.start = 0x01DD,
+		.end   = 0x01DE,
+		.value = 0x018E,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 26, len = 17) */
+		.start = 0x01DF,
+		.end   = 0x01F0,
+		.value = 0x01DE,
+		.inc   = 0x0002,
+	},
+	{
+		/* (index = 27, len = 1) */
+		.start = 0x01F3,
+		.end   = 0x01F4,
+		.value = 0x01F1,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 28, len = 6) */
+		.start = 0x01F5,
+		.end   = 0x01FB,
+		.value = 0x01F4,
+		.inc   = 0x0004,
+	},
+	{
+		/* (index = 29, len = 37) */
+		.start = 0x01FB,
+		.end   = 0x0220,
+		.value = 0x01FA,
+		.inc   = 0x0002,
+	},
+	{
+		/* (index = 30, len = 17) */
+		.start = 0x0223,
+		.end   = 0x0234,
+		.value = 0x0222,
+		.inc   = 0x0002,
+	},
+	{
+		/* (index = 31, len = 1) */
+		.start = 0x023A,
+		.end   = 0x023B,
+		.value = 0x2C65,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 32, len = 1) */
+		.start = 0x023C,
+		.end   = 0x023D,
+		.value = 0x023B,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 33, len = 1) */
+		.start = 0x023E,
+		.end   = 0x023F,
+		.value = 0x2C66,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 34, len = 7) */
+		.start = 0x0242,
+		.end   = 0x0249,
+		.value = 0x0241,
+		.inc   = 0x0005,
+	},
+	{
+		/* (index = 35, len = 7) */
+		.start = 0x0249,
+		.end   = 0x0250,
+		.value = 0x0248,
+		.inc   = 0x0002,
+	},
+	/* IPA Extensions */
+	{
+		/* (index = 36, len = 1) */
+		.start = 0x0253,
+		.end   = 0x0254,
+		.value = 0x0181,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 37, len = 1) */
+		.start = 0x0254,
+		.end   = 0x0255,
+		.value = 0x0186,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 38, len = 2) */
+		.start = 0x0256,
+		.end   = 0x0258,
+		.value = 0x0189,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 39, len = 1) */
+		.start = 0x0259,
+		.end   = 0x025A,
+		.value = 0x018F,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 40, len = 1) */
+		.start = 0x025B,
+		.end   = 0x025C,
+		.value = 0x0190,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 41, len = 1) */
+		.start = 0x0260,
+		.end   = 0x0261,
+		.value = 0x0193,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 42, len = 1) */
+		.start = 0x0263,
+		.end   = 0x0264,
+		.value = 0x0194,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 43, len = 1) */
+		.start = 0x0268,
+		.end   = 0x0269,
+		.value = 0x0197,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 44, len = 1) */
+		.start = 0x0269,
+		.end   = 0x026A,
+		.value = 0x0196,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 45, len = 1) */
+		.start = 0x026B,
+		.end   = 0x026C,
+		.value = 0x2C62,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 46, len = 1) */
+		.start = 0x026F,
+		.end   = 0x0270,
+		.value = 0x019C,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 47, len = 1) */
+		.start = 0x0272,
+		.end   = 0x0273,
+		.value = 0x019D,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 48, len = 1) */
+		.start = 0x0275,
+		.end   = 0x0276,
+		.value = 0x019F,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 49, len = 1) */
+		.start = 0x027D,
+		.end   = 0x027E,
+		.value = 0x2C64,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 50, len = 4) */
+		.start = 0x0280,
+		.end   = 0x0284,
+		.value = 0x01A6,
+		.inc   = 0x0003,
+	},
+	{
+		/* (index = 51, len = 1) */
+		.start = 0x0288,
+		.end   = 0x0289,
+		.value = 0x01AE,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 52, len = 1) */
+		.start = 0x0289,
+		.end   = 0x028A,
+		.value = 0x0244,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 53, len = 2) */
+		.start = 0x028A,
+		.end   = 0x028C,
+		.value = 0x01B1,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 54, len = 1) */
+		.start = 0x028C,
+		.end   = 0x028D,
+		.value = 0x0245,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 55, len = 1) */
+		.start = 0x0292,
+		.end   = 0x0293,
+		.value = 0x01B7,
+		.inc   = 0x0001,
+	},
+	/* Greek and Coptic */
+	{
+		/* (index = 56, len = 3) */
+		.start = 0x037B,
+		.end   = 0x037E,
+		.value = 0x03FD,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 57, len = 1) */
+		.start = 0x03AC,
+		.end   = 0x03AD,
+		.value = 0x0386,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 58, len = 3) */
+		.start = 0x03AD,
+		.end   = 0x03B0,
+		.value = 0x0388,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 59, len = 17) */
+		.start = 0x03B1,
+		.end   = 0x03C2,
+		.value = 0x0391,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 60, len = 1) */
+		.start = 0x03C2,
+		.end   = 0x03C3,
+		.value = 0x03A3,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 61, len = 9) */
+		.start = 0x03C3,
+		.end   = 0x03CC,
+		.value = 0x03A3,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 62, len = 1) */
+		.start = 0x03CC,
+		.end   = 0x03CD,
+		.value = 0x038C,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 63, len = 2) */
+		.start = 0x03CD,
+		.end   = 0x03CF,
+		.value = 0x038E,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 64, len = 23) */
+		.start = 0x03D9,
+		.end   = 0x03F0,
+		.value = 0x03D8,
+		.inc   = 0x0002,
+	},
+	{
+		/* (index = 65, len = 1) */
+		.start = 0x03F2,
+		.end   = 0x03F3,
+		.value = 0x03F9,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 66, len = 4) */
+		.start = 0x03F8,
+		.end   = 0x03FC,
+		.value = 0x03F7,
+		.inc   = 0x0003,
+	},
+	/* Cyrillic */
+	{
+		/* (index = 67, len = 32) */
+		.start = 0x0430,
+		.end   = 0x0450,
+		.value = 0x0410,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 68, len = 16) */
+		.start = 0x0450,
+		.end   = 0x0460,
+		.value = 0x0400,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 69, len = 33) */
+		.start = 0x0461,
+		.end   = 0x0482,
+		.value = 0x0460,
+		.inc   = 0x0002,
+	},
+	{
+		/* (index = 70, len = 53) */
+		.start = 0x048B,
+		.end   = 0x04C0,
+		.value = 0x048A,
+		.inc   = 0x0002,
+	},
+	{
+		/* (index = 71, len = 13) */
+		.start = 0x04C2,
+		.end   = 0x04CF,
+		.value = 0x04C1,
+		.inc   = 0x0002,
+	},
+	{
+		/* (index = 72, len = 1) */
+		.start = 0x04CF,
+		.end   = 0x04D0,
+		.value = 0x04C0,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 73, len = 67) */
+		.start = 0x04D1,
+		.end   = 0x0514,
+		.value = 0x04D0,
+		.inc   = 0x0002,
+	},
+	/* Armenian */
+	{
+		/* (index = 74, len = 38) */
+		.start = 0x0561,
+		.end   = 0x0587,
+		.value = 0x0531,
+		.inc   = 0x0001,
+	},
+	/* Phonetic Extensions (LATIN SMALL LETTER P WITH STROKE) */
+	{
+		/* (index = 75, len = 1) */
+		.start = 0x1D7D,
+		.end   = 0x1D7E,
+		.value = 0x2C63,
+		.inc   = 0x0001,
+	},
+	/* Latin Extended Additional */
+	{
+		/* (index = 76, len = 149) */
+		.start = 0x1E01,
+		.end   = 0x1E96,
+		.value = 0x1E00,
+		.inc   = 0x0002,
+	},
+	{
+		/* (index = 77, len = 89) */
+		.start = 0x1EA1,
+		.end   = 0x1EFA,
+		.value = 0x1EA0,
+		.inc   = 0x0002,
+	},
+	/* Greek Extended */
+	{
+		/* (index = 78, len = 8) */
+		.start = 0x1F00,
+		.end   = 0x1F08,
+		.value = 0x1F08,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 79, len = 6) */
+		.start = 0x1F10,
+		.end   = 0x1F16,
+		.value = 0x1F18,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 80, len = 8) */
+		.start = 0x1F20,
+		.end   = 0x1F28,
+		.value = 0x1F28,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 81, len = 8) */
+		.start = 0x1F30,
+		.end   = 0x1F38,
+		.value = 0x1F38,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 82, len = 6) */
+		.start = 0x1F40,
+		.end   = 0x1F46,
+		.value = 0x1F48,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 83, len = 7) */
+		.start = 0x1F51,
+		.end   = 0x1F58,
+		.value = 0x1F59,
+		.inc   = 0x0002,
+	},
+	{
+		/* (index = 84, len = 8) */
+		.start = 0x1F60,
+		.end   = 0x1F68,
+		.value = 0x1F68,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 85, len = 2) */
+		.start = 0x1F70,
+		.end   = 0x1F72,
+		.value = 0x1FBA,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 86, len = 4) */
+		.start = 0x1F72,
+		.end   = 0x1F76,
+		.value = 0x1FC8,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 87, len = 2) */
+		.start = 0x1F76,
+		.end   = 0x1F78,
+		.value = 0x1FDA,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 88, len = 2) */
+		.start = 0x1F78,
+		.end   = 0x1F7A,
+		.value = 0x1FF8,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 89, len = 2) */
+		.start = 0x1F7A,
+		.end   = 0x1F7C,
+		.value = 0x1FEA,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 90, len = 2) */
+		.start = 0x1F7C,
+		.end   = 0x1F7E,
+		.value = 0x1FFA,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 91, len = 8) */
+		.start = 0x1F80,
+		.end   = 0x1F88,
+		.value = 0x1F88,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 92, len = 8) */
+		.start = 0x1F90,
+		.end   = 0x1F98,
+		.value = 0x1F98,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 93, len = 8) */
+		.start = 0x1FA0,
+		.end   = 0x1FA8,
+		.value = 0x1FA8,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 94, len = 2) */
+		.start = 0x1FB0,
+		.end   = 0x1FB2,
+		.value = 0x1FB8,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 95, len = 1) */
+		.start = 0x1FB3,
+		.end   = 0x1FB4,
+		.value = 0x1FBC,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 96, len = 1) */
+		.start = 0x1FCC,
+		.end   = 0x1FCD,
+		.value = 0x1FC3,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 97, len = 2) */
+		.start = 0x1FD0,
+		.end   = 0x1FD2,
+		.value = 0x1FD8,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 98, len = 2) */
+		.start = 0x1FE0,
+		.end   = 0x1FE2,
+		.value = 0x1FE8,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 99, len = 1) */
+		.start = 0x1FE5,
+		.end   = 0x1FE6,
+		.value = 0x1FEC,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 100, len = 1) */
+		.start = 0x1FFC,
+		.end   = 0x1FFD,
+		.value = 0x1FF3,
+		.inc   = 0x0001,
+	},
+	/* Letterlike Symbols (turned letter F) */
+	{
+		/* (index = 101, len = 1) */
+		.start = 0x214E,
+		.end   = 0x214F,
+		.value = 0x2132,
+		.inc   = 0x0001,
+	},
+	/* Number Forms */
+	{
+		/* (index = 102, len = 16) */
+		.start = 0x2170,
+		.end   = 0x2180,
+		.value = 0x2160,
+		.inc   = 0x0001,
+	},
+	{
+		/* (index = 103, len = 1) */
+		.start = 0x2184,
+		.end   = 0x2185,
+		.value = 0x2183,
+		.inc   = 0x0001,
+	},
+	/* Enclosed Alphanumerics */
+	{
+		/* (index = 104, len = 26) */
+		.start = 0x24D0,
+		.end   = 0x24EA,
+		.value = 0x24B6,
+		.inc   = 0x0001,
+	},
+	/* Glagolitic */
+	{
+		/* (index = 105, len = 47) */
+		.start = 0x2C30,
+		.end   = 0x2C5F,
+		.value = 0x2C00,
+		.inc   = 0x0001,
+	},
+	/* Latin Extended-C */
+	{
+		/* (index = 106, len = 9) */
+		.start = 0x2C61,
+		.end   = 0x2C6A,
+		.value = 0x2C60,
+		.inc   = 0x0007,
+	},
+	{
+		/* (index = 107, len = 3) */
+		.start = 0x2C6A,
+		.end   = 0x2C6D,
+		.value = 0x2C69,
+		.inc   = 0x0002,
+	},
+	{
+		/* (index = 108, len = 13) */
+		.start = 0x2C76,
+		.end   = 0x2C83,
+		.value = 0x2C75,
+		.inc   = 0x000B,
+	},
+	/* Coptic */
+	{
+		/* (index = 109, len = 97) */
+		.start = 0x2C83,
+		.end   = 0x2CE4,
+		.value = 0x2C82,
+		.inc   = 0x0002,
+	},
+	/* Georgian Supplement */
+	{
+		/* (index = 110, len = 38) */
+		.start = 0x2D00,
+		.end   = 0x2D26,
+		.value = 0x10A0,
+		.inc   = 0x0001,
+	},
+	/* Halfwidth and Fullwidth Forms */
+	{
+		/* (index = 111, len = 26) */
+		.start = 0xFF41,
+		.end   = 0xFF5B,
+		.value = 0xFF21,
+		.inc   = 0x0001,
+	},
 };
 
 /*
diff --git a/fs/exfat/upcase.c b/fs/exfat/upcase.c
new file mode 100644
index 000000000000..246f76b0f181
--- /dev/null
+++ b/fs/exfat/upcase.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/buffer_head.h>
+
+#include "exfat_raw.h"
+#include "exfat_fs.h"
+
+int exfat_set_upcase_ptable(struct exfat_upcase_ptable *ptbl,
+		const __u16 index, const __u16 value)
+{
+	const size_t page_idx = index / EXFAT_UPTBL_PAGESIZE;
+	const size_t idx_in_page = index % EXFAT_UPTBL_PAGESIZE;
+
+	if (value == 0)
+		return 0;
+
+	if (ptbl->pages[page_idx] == NULL) {
+		void *nm = kvcalloc(EXFAT_UPTBL_PAGESIZE, sizeof(__u16), GFP_KERNEL);
+
+		if (nm == NULL)
+			return -ENOMEM;
+		ptbl->pages[page_idx] = nm;
+		ptbl->cnt++;
+	}
+
+	ptbl->pages[page_idx][idx_in_page] = value;
+
+	return 0;
+}
+
+void exfat_free_upcase_ptable(struct exfat_upcase_ptable *ptbl)
+{
+	if (ptbl == NULL)
+		return;
+
+	for (size_t i = 0; i < ARRAY_SIZE(ptbl->pages); i++) {
+		kvfree(ptbl->pages[i]);
+		ptbl->pages[i] = NULL;
+	}
+	ptbl->cnt = 0;
+}
+
+int exfat_populate_upcase_ptable(struct exfat_upcase_ptable *ptbl,
+		const struct exfat_upcase_range_info *ri,
+		const size_t cnt)
+{
+	int err;
+
+	for (size_t i = 0; i < cnt; i++) {
+		/* Memory safety: allow the value to wrap around but not the index */
+		const unsigned int step = ri[i].inc;
+		unsigned int index = ri[i].start;
+		__u16 value = ri[i].value;
+
+		if (step == 0 || index >= ri[i].end)
+			/* Damaged .rodata */
+			return -EINVAL;
+
+		while (index < ri[i].end) {
+			err = exfat_set_upcase_ptable(ptbl, index, value);
+			if (err)
+				return err;
+
+			index += step;
+			value += step;
+		}
+	}
+
+	return 0;
+}
-- 
2.53.0.1.ga224b40d3f.dirty


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 3/4] exfat: add default_upcase option (read-only)
  2026-04-28 23:50 [PATCH v1 0/4] exfat: memory optimisations and stringent integrity checks for up-case table David Timber
  2026-04-28 23:50 ` [PATCH v1 1/4] exfat: refactor nls.c (tables) David Timber
  2026-04-28 23:50 ` [PATCH v1 2/4] exfat: use upcase_ptable and upcase_range_info to reduce memory footprint David Timber
@ 2026-04-28 23:50 ` David Timber
  2026-04-28 23:50 ` [PATCH v1 4/4] exfat: more pedantic upcase table validity check David Timber
  3 siblings, 0 replies; 16+ messages in thread
From: David Timber @ 2026-04-28 23:50 UTC (permalink / raw)
  To: Namjae Jeon, Sungjong Seo, Yuezhang Mo; +Cc: linux-fsdevel, David Timber

Show "default_upcase" in the mount options if the default upcase table
is being used for the volume for diagnostic purposes.

Signed-off-by: David Timber <dxdt@dev.snart.me>
---
 fs/exfat/super.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/fs/exfat/super.c b/fs/exfat/super.c
index 200d93bd5abd..7da6fcf8b41d 100644
--- a/fs/exfat/super.c
+++ b/fs/exfat/super.c
@@ -149,6 +149,9 @@ static int exfat_show_options(struct seq_file *m, struct dentry *root)
 		seq_printf(m, ",time_offset=%d", opts->time_offset);
 	if (opts->zero_size_dir)
 		seq_puts(m, ",zero_size_dir");
+	if (sbi->vol_utbl_own == NULL)
+		seq_puts(m, ",default_upcase");
+
 	return 0;
 }
 
-- 
2.53.0.1.ga224b40d3f.dirty


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 4/4] exfat: more pedantic upcase table validity check
  2026-04-28 23:50 [PATCH v1 0/4] exfat: memory optimisations and stringent integrity checks for up-case table David Timber
                   ` (2 preceding siblings ...)
  2026-04-28 23:50 ` [PATCH v1 3/4] exfat: add default_upcase option (read-only) David Timber
@ 2026-04-28 23:50 ` David Timber
  2026-04-30  7:58   ` Yuezhang.Mo
  3 siblings, 1 reply; 16+ messages in thread
From: David Timber @ 2026-04-28 23:50 UTC (permalink / raw)
  To: Namjae Jeon, Sungjong Seo, Yuezhang Mo; +Cc: linux-fsdevel, David Timber

It is observed that most exFAT implementations reject a volume with an
upcase table whose index of the last entry is not 0xFFFF and treat the
volume as damaged.

Upon encoutering an incomplete or malformed upcase table:

  - whose the index of last entry is not 0xFFFF
  - that has extra data after the end of the table

Raise exfat_fs_error() to mark the volume read-only.

Signed-off-by: David Timber <dxdt@dev.snart.me>
---
 fs/exfat/nls.c | 26 ++++++++++++++------------
 1 file changed, 14 insertions(+), 12 deletions(-)

diff --git a/fs/exfat/nls.c b/fs/exfat/nls.c
index c56d501659b3..3ce5165094bf 100644
--- a/fs/exfat/nls.c
+++ b/fs/exfat/nls.c
@@ -260,17 +260,16 @@ int exfat_nls_to_utf16(struct super_block *sb, const unsigned char *p_cstring,
 }
 
 static int exfat_load_upcase_table(struct super_block *sb,
-		sector_t sector, unsigned long long num_sectors,
-		unsigned int utbl_checksum)
+		unsigned long long tbl_size, sector_t sector,
+		unsigned long long num_sectors, unsigned int utbl_checksum)
 {
 	struct exfat_sb_info *sbi = EXFAT_SB(sb);
 	unsigned int sect_size = sb->s_blocksize;
 	unsigned int i, index = 0;
 	u32 chksum = 0;
-	unsigned char skip = false;
+	bool skip = false, is_default = true;
 	struct exfat_upcase_ptable *upcase_table;
 	unsigned short def_upcase;
-	bool is_default;
 	unsigned int entries = 0;
 	int ret = -EINVAL;
 
@@ -279,9 +278,8 @@ static int exfat_load_upcase_table(struct super_block *sb,
 		return -ENOMEM;
 
 	num_sectors += sector;
-	is_default = sector < num_sectors;
 
-	while (sector < num_sectors) {
+	while (tbl_size > 1 && sector < num_sectors) {
 		struct buffer_head *bh;
 
 		bh = sb_bread(sb, sector);
@@ -292,7 +290,8 @@ static int exfat_load_upcase_table(struct super_block *sb,
 			goto err;
 		}
 		sector++;
-		for (i = 0; i < sect_size && index <= 0xFFFF; i += 2) {
+		for (i = 0; i < sect_size && index <= 0xFFFF && tbl_size > 1;
+				i += 2, tbl_size -= 2) {
 			unsigned short uni = get_unaligned_le16(bh->b_data + i);
 
 			if (skip) {
@@ -319,9 +318,13 @@ static int exfat_load_upcase_table(struct super_block *sb,
 		}
 		chksum = exfat_calc_chksum32(bh->b_data, i, chksum, CS_DEFAULT);
 		brelse(bh);
+
+		/* the contents of the table went out of bounds with still more data to read */
+		if (tbl_size > 0 && index >= 0x10000)
+			goto err;
 	}
 
-	if (index >= 0xFFFF && utbl_checksum == chksum) {
+	if (index == 0x10000 && utbl_checksum == chksum) {
 		/*
 		 * is_default being set does not necessarily mean the contents are exact same as the
 		 * upcase table loaded from the volume may be missing some entries. The checksum
@@ -339,10 +342,9 @@ static int exfat_load_upcase_table(struct super_block *sb,
 		return 0;
 	}
 
-	exfat_err(sb, "failed to load upcase table (idx : 0x%08x, chksum : 0x%08x, utbl_chksum : 0x%08x)",
-		  index, chksum, utbl_checksum);
-
 err:
+	exfat_fs_error(sb, "damaged upcase table. Please run fsck (idx : 0x%08x, chksum : 0x%08x, utbl_chksum : 0x%08x)",
+		       index, chksum, utbl_checksum);
 	exfat_free_upcase_ptable(upcase_table);
 	kvfree(upcase_table);
 
@@ -390,7 +392,7 @@ int exfat_create_upcase_table(struct super_block *sb)
 			if (tbl_size) {
 				sector = exfat_cluster_to_sector(sbi, tbl_clu);
 				num_sectors = ((tbl_size - 1) >> blksize_bits) + 1;
-				ret = exfat_load_upcase_table(sb, sector, num_sectors,
+				ret = exfat_load_upcase_table(sb, tbl_size, sector, num_sectors,
 					le32_to_cpu(ep->dentry.upcase.checksum));
 			} else
 				exfat_fs_error(sb,
-- 
2.53.0.1.ga224b40d3f.dirty


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH v1 1/4] exfat: refactor nls.c (tables)
  2026-04-28 23:50 ` [PATCH v1 1/4] exfat: refactor nls.c (tables) David Timber
@ 2026-04-30  7:53   ` Yuezhang.Mo
  2026-04-30  9:22     ` David Timber
  0 siblings, 1 reply; 16+ messages in thread
From: Yuezhang.Mo @ 2026-04-30  7:53 UTC (permalink / raw)
  To: David Timber, Namjae Jeon, Sungjong Seo; +Cc: linux-fsdevel@vger.kernel.org

> +/* exfat/tables.c */
> +/* Upcase table macro */
> +#define EXFAT_NUM_UPCASE     (2918)
> +#define EXFAT_UTBL_COUNT     (0x10000)
> +
> +extern const unsigned short exfat_uni_def_upcase[EXFAT_NUM_UPCASE];
> +extern const unsigned short exfat_bad_uni_chars[];

The global variable exfat_bad_uni_chars[] is unnecessary, as is the 
generic function exfat_wstrchr().

We can implement a dedicated function `exfat_is_bad_uni_char()`, in
which we can define static `exfat_bad_uni_chars`, or we can use a
switch...case... statement.



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v1 2/4] exfat: use upcase_ptable and upcase_range_info to reduce memory footprint
  2026-04-28 23:50 ` [PATCH v1 2/4] exfat: use upcase_ptable and upcase_range_info to reduce memory footprint David Timber
@ 2026-04-30  7:58   ` Yuezhang.Mo
  2026-04-30  9:45     ` David Timber
  0 siblings, 1 reply; 16+ messages in thread
From: Yuezhang.Mo @ 2026-04-30  7:58 UTC (permalink / raw)
  To: David Timber, Namjae Jeon, Sungjong Seo; +Cc: linux-fsdevel@vger.kernel.org

> +static inline __u16 exfat_lookup_upcase_ptable(const struct exfat_upcase_ptable *ptbl,
> +             const __u16 index)
> +{
> +     const size_t page_idx = index / EXFAT_UPTBL_PAGESIZE;
> +     const size_t idx_in_page = index % EXFAT_UPTBL_PAGESIZE;
> +
> +     return ptbl->pages[page_idx] == NULL ? 0 : ptbl->pages[page_idx][idx_in_page];
 
How about this? Then we won't need to check it again in exfat_toupper().
 
return ptbl->pages[page_idx] == NULL ? index : ptbl->pages[page_idx][idx_in_page];
 
> +}
> +
> +void exfat_free_upcase_ptable(struct exfat_upcase_ptable *ptbl);
> +int exfat_populate_upcase_ptable(struct exfat_upcase_ptable *ptbl,
> +             const struct exfat_upcase_range_info *ri,
> +             const size_t cnt);
> +
>  /* exfat/tables.c */
>  /* Upcase table macro */
> -#define EXFAT_NUM_UPCASE     (2918)
> -#define EXFAT_UTBL_COUNT     (0x10000)
> +#define EXFAT_DEF_UTBL_RI_COUNT      (112)
> +#define EXFAT_DEF_UTBL_CHKSUM        (0xE619D30D)
>  
> -extern const unsigned short exfat_uni_def_upcase[EXFAT_NUM_UPCASE];
> +extern const struct exfat_upcase_range_info exfat_def_utbl_ri[EXFAT_DEF_UTBL_RI_COUNT];
 
Is there a way to reduce the size of `exfat_def_utbl_ri` so that we
can put it on the stack of `exfat_init_default_upcase_ptable()`?
 
If not, add `__initconst` for `exfat_def_utbl_ri`.
 
> +int exfat_init_default_upcase_ptable(void)
 
Add __init.
 
> +void exfat_free_default_upcase_ptable(void)
 
Add __exit.

> @@ -896,6 +896,12 @@ static int __init init_exfat_fs(void)
>  {
>       int err;
>  
> +     err = exfat_init_default_upcase_ptable();
> +     if (err) {
> +             WARN_ON(err == -EINVAL);
> +             return err;
> +     }
> +
>       err = exfat_cache_init();
>       if (err)
>               return err;
 
Please add error handling to release default_upcase_ptable.
 
> +
> +int exfat_populate_upcase_ptable(struct exfat_upcase_ptable *ptbl,
> +             const struct exfat_upcase_range_info *ri,
> +             const size_t cnt)
                                                                                                                                                                                        
This function is only called by exfat_init_default_upcase_ptable(),
how about merge this function to it?
 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v1 4/4] exfat: more pedantic upcase table validity check
  2026-04-28 23:50 ` [PATCH v1 4/4] exfat: more pedantic upcase table validity check David Timber
@ 2026-04-30  7:58   ` Yuezhang.Mo
  2026-04-30  9:24     ` David Timber
  0 siblings, 1 reply; 16+ messages in thread
From: Yuezhang.Mo @ 2026-04-30  7:58 UTC (permalink / raw)
  To: David Timber, Namjae Jeon, Sungjong Seo; +Cc: linux-fsdevel@vger.kernel.org

>  static int exfat_load_upcase_table(struct super_block *sb,
> -             sector_t sector, unsigned long long num_sectors,
> -             unsigned int utbl_checksum)
> +             unsigned long long tbl_size, sector_t sector,
> +             unsigned long long num_sectors, unsigned int utbl_checksum)

After adding the parameter tbl_size, the parameter num_sectors is no
longer needed.


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v1 1/4] exfat: refactor nls.c (tables)
  2026-04-30  7:53   ` Yuezhang.Mo
@ 2026-04-30  9:22     ` David Timber
  2026-05-03  8:03       ` David Timber
  0 siblings, 1 reply; 16+ messages in thread
From: David Timber @ 2026-04-30  9:22 UTC (permalink / raw)
  To: Yuezhang.Mo@sony.com, Namjae Jeon, Sungjong Seo
  Cc: linux-fsdevel@vger.kernel.org

On 4/30/26 16:53, Yuezhang.Mo@sony.com wrote:
>> +/* exfat/tables.c */
>> +/* Upcase table macro */
>> +#define EXFAT_NUM_UPCASE     (2918)
>> +#define EXFAT_UTBL_COUNT     (0x10000)
>> +
>> +extern const unsigned short exfat_uni_def_upcase[EXFAT_NUM_UPCASE];
>> +extern const unsigned short exfat_bad_uni_chars[];
> The global variable exfat_bad_uni_chars[] is unnecessary, as is the 
> generic function exfat_wstrchr().
>
> We can implement a dedicated function `exfat_is_bad_uni_char()`, in
> which we can define static `exfat_bad_uni_chars`, or we can use a
> switch...case... statement.
I agree.

I too think that switch a case statement is suitable here because
there's only a few __u16's to compare so there's really no need to leave
room for cache misses. Let the CPU prefetch the characters along with
the instructions.

Will make a separate commit if no objection.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v1 4/4] exfat: more pedantic upcase table validity check
  2026-04-30  7:58   ` Yuezhang.Mo
@ 2026-04-30  9:24     ` David Timber
  2026-05-01 11:27       ` Namjae Jeon
  0 siblings, 1 reply; 16+ messages in thread
From: David Timber @ 2026-04-30  9:24 UTC (permalink / raw)
  To: Yuezhang.Mo@sony.com, Namjae Jeon, Sungjong Seo
  Cc: linux-fsdevel@vger.kernel.org

On 4/30/26 16:58, Yuezhang.Mo@sony.com wrote:
>>  static int exfat_load_upcase_table(struct super_block *sb,
>> -             sector_t sector, unsigned long long num_sectors,
>> -             unsigned int utbl_checksum)
>> +             unsigned long long tbl_size, sector_t sector,
>> +             unsigned long long num_sectors, unsigned int utbl_checksum)
> After adding the parameter tbl_size, the parameter num_sectors is no
> longer needed.
Change the func signature? I'd like to hear Jeon's opinion on this.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v1 2/4] exfat: use upcase_ptable and upcase_range_info to reduce memory footprint
  2026-04-30  7:58   ` Yuezhang.Mo
@ 2026-04-30  9:45     ` David Timber
  2026-04-30 11:48       ` Yuezhang.Mo
  0 siblings, 1 reply; 16+ messages in thread
From: David Timber @ 2026-04-30  9:45 UTC (permalink / raw)
  To: Yuezhang.Mo@sony.com, Namjae Jeon, Sungjong Seo
  Cc: linux-fsdevel@vger.kernel.org

On 4/30/26 16:58, Yuezhang.Mo@sony.com wrote:
>> +static inline __u16 exfat_lookup_upcase_ptable(const struct exfat_upcase_ptable *ptbl,
>> +             const __u16 index)
>> +{
>> +     const size_t page_idx = index / EXFAT_UPTBL_PAGESIZE;
>> +     const size_t idx_in_page = index % EXFAT_UPTBL_PAGESIZE;
>> +
>> +     return ptbl->pages[page_idx] == NULL ? 0 : ptbl->pages[page_idx][idx_in_page];
>  
> How about this? Then we won't need to check it again in exfat_toupper().
>  
> return ptbl->pages[page_idx] == NULL ? index : ptbl->pages[page_idx][idx_in_page]; 

Zero(invalid character in exFAT spec) is used as identity within the
kernel. We'll have to be a bit careful. I'd have to change this logic in
exfat_load_upcase_table()

                ret = exfat_set_upcase_ptable(upcase_table, index, uni);
                if (ret) {
                    brelse(bh);
                    goto err;
                }

                def_upcase =
exfat_lookup_upcase_ptable(&default_upcase_table,
                                    index);
                is_default &= def_upcase == uni;

Some OOB data check will be ignored:

 - the character is "skipped": zero
 - the character is not skipped or specified as identity: non-zero

Subtle difference. If we change exfat_lookup_upcase_ptable() to return
identity on non-existent mapping, that information is lost, I think. But
again, the resulting contents would be the same regardless so I think
there isn't really a side effect.

>> +}
>> +
>> +void exfat_free_upcase_ptable(struct exfat_upcase_ptable *ptbl);
>> +int exfat_populate_upcase_ptable(struct exfat_upcase_ptable *ptbl,
>> +             const struct exfat_upcase_range_info *ri,
>> +             const size_t cnt);
>> +
>>  /* exfat/tables.c */
>>  /* Upcase table macro */
>> -#define EXFAT_NUM_UPCASE     (2918)
>> -#define EXFAT_UTBL_COUNT     (0x10000)
>> +#define EXFAT_DEF_UTBL_RI_COUNT      (112)
>> +#define EXFAT_DEF_UTBL_CHKSUM        (0xE619D30D)
>>  
>> -extern const unsigned short exfat_uni_def_upcase[EXFAT_NUM_UPCASE];
>> +extern const struct exfat_upcase_range_info exfat_def_utbl_ri[EXFAT_DEF_UTBL_RI_COUNT];
>  
> Is there a way to reduce the size of `exfat_def_utbl_ri` so that we
> can put it on the stack of `exfat_init_default_upcase_ptable()`?
>  
> If not, add `__initconst` for `exfat_def_utbl_ri`.
>  
Nah. It's as packed as it could get without breaking the alignment. We
could pack it down to 784 bytes, but that's still a lot of stack usage IMO.

>> +int exfat_init_default_upcase_ptable(void)
>  
> Add __init.
>  
>> +void exfat_free_default_upcase_ptable(void)
>  
> Add __exit.
Thanks.

>> @@ -896,6 +896,12 @@ static int __init init_exfat_fs(void)
>>  {
>>       int err;
>>  
>> +     err = exfat_init_default_upcase_ptable();
>> +     if (err) {
>> +             WARN_ON(err == -EINVAL);
>> +             return err;
>> +     }
>> +
>>       err = exfat_cache_init();
>>       if (err)
>>               return err;
>  
> Please add error handling to release default_upcase_ptable.
>  
>> +
>> +int exfat_populate_upcase_ptable(struct exfat_upcase_ptable *ptbl,
>> +             const struct exfat_upcase_range_info *ri,
>> +             const size_t cnt)
>                                                                                                                                                                                         
> This function is only called by exfat_init_default_upcase_ptable(),
> how about merge this function to it?
>  
default_upcase_ptable is already freed up before returning from
exfat_init_default_upcase_ptable() upon error. I made
exfat_populate_upcase_ptable() as a separate function to use it on my
test cases. The compiler inlines the function anyway(although
appropriate attributes are missing in the original patch, my bad) so
there's no ELF overhead either. I'd like to leave it as it is, if that's
okay.


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v1 2/4] exfat: use upcase_ptable and upcase_range_info to reduce memory footprint
  2026-04-30  9:45     ` David Timber
@ 2026-04-30 11:48       ` Yuezhang.Mo
  2026-04-30 17:13         ` David Timber
  0 siblings, 1 reply; 16+ messages in thread
From: Yuezhang.Mo @ 2026-04-30 11:48 UTC (permalink / raw)
  To: David Timber, Namjae Jeon, Sungjong Seo; +Cc: linux-fsdevel@vger.kernel.org

> On 4/30/26 16:58, Yuezhang.Mo@sony.com wrote:
> >> +static inline __u16 exfat_lookup_upcase_ptable(const struct exfat_upcase_ptable *ptbl,
> >> +      a       const __u16 index)
> >> +{
> >> +     const size_t page_idx = index / EXFAT_UPTBL_PAGESIZE;
> >> +     const size_t idx_in_page = index % EXFAT_UPTBL_PAGESIZE;
> >> +
> >> +     return ptbl->pages[page_idx] == NULL ? 0 : ptbl->pages[page_idx][idx_in_page];
> >
> > How about this? Then we won't need to check it again in exfat_toupper().
> >
> > return ptbl->pages[page_idx] == NULL ? index : ptbl->pages[page_idx][idx_in_page];
> 
> Zero(invalid character in exFAT spec) is used as identity within the
> kernel. We'll have to be a bit careful. I'd have to change this logic in
> exfat_load_upcase_table()
> 
>                 ret = exfat_set_upcase_ptable(upcase_table, index, uni);
>                 if (ret) {
>                     brelse(bh);
>                     goto err;
>                 }
> 
>                 def_upcase =
> exfat_lookup_upcase_ptable(&default_upcase_table,
>                                     index);
>                 is_default &= def_upcase == uni;
> 
> Some OOB data check will be ignored:
> 
>  - the character is "skipped": zero
>  - the character is not skipped or specified as identity: non-zero
> 
> Subtle difference. If we change exfat_lookup_upcase_ptable() to return
> identity on non-existent mapping, that information is lost, I think. But
> again, the resulting contents would be the same regardless so I think
> there isn't really a side effect.
> 
> >> +}
> >> +
> >> +void exfat_free_upcase_ptable(struct exfat_upcase_ptable *ptbl);
> >> +int exfat_populate_upcase_ptable(struct exfat_upcase_ptable *ptbl,
> >> +             const struct exfat_upcase_range_info *ri,
> >> +             const size_t cnt);
> >> +
> >>  /* exfat/tables.c */
> >>  /* Upcase table macro */
> >> -#define EXFAT_NUM_UPCASE     (2918)
> >> -#define EXFAT_UTBL_COUNT     (0x10000)
> >> +#define EXFAT_DEF_UTBL_RI_COUNT      (112)
> >> +#define EXFAT_DEF_UTBL_CHKSUM        (0xE619D30D)
> >>
> >> -extern const unsigned short exfat_uni_def_upcase[EXFAT_NUM_UPCASE];
> >> +extern const struct exfat_upcase_range_info exfat_def_utbl_ri[EXFAT_DEF_UTBL_RI_COUNT];
> >
> > Is there a way to reduce the size of `exfat_def_utbl_ri` so that we
> > can put it on the stack of `exfat_init_default_upcase_ptable()`?
> >
> > If not, add `__initconst` for `exfat_def_utbl_ri`.
> >
> Nah. It's as packed as it could get without breaking the alignment. We
> could pack it down to 784 bytes, but that's still a lot of stack usage IMO.
> 
> >> +int exfat_init_default_upcase_ptable(void)
> >
> > Add __init.
> >
> >> +void exfat_free_default_upcase_ptable(void)
> >
> > Add __exit.
> Thanks.
> 
> >> @@ -896,6 +896,12 @@ static int __init init_exfat_fs(void)
> >>  {
> >>       int err;
> >>
> >> +     err = exfat_init_default_upcase_ptable();
> >> +     if (err) {
> >> +             WARN_ON(err == -EINVAL);
> >> +             return err;
> >> +     }
> >> +
> >>       err = exfat_cache_init();
> >>       if (err)
> >>               return err;
> >
> > Please add error handling to release default_upcase_ptable.
> >
> >> +
> >> +int exfat_populate_upcase_ptable(struct exfat_upcase_ptable *ptbl,
> >> +             const struct exfat_upcase_range_info *ri,
> >> +             const size_t cnt)
> >
> > This function is only called by exfat_init_default_upcase_ptable(),
> > how about merge this function to it?
> >
> default_upcase_ptable is already freed up before returning from
> exfat_init_default_upcase_ptable() upon error. I made

What I mean is that if the subsequent operations fail, such as
exfat_cache_init() returning an error, exfat_free_default_upcase_ptable()
needs to be called to release default_upcase_ptable.

> exfat_populate_upcase_ptable() as a separate function to use it on my
> test cases. The compiler inlines the function anyway(although
> appropriate attributes are missing in the original patch, my bad) so
> there's no ELF overhead either. I'd like to leave it as it is, if that's
> okay.

Your test cases are only for the current development phase and will not
be needed later, right?

If not, keeping it is fine. Otherwise, we should try to keep the code as
concise as possible.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v1 2/4] exfat: use upcase_ptable and upcase_range_info to reduce memory footprint
  2026-04-30 11:48       ` Yuezhang.Mo
@ 2026-04-30 17:13         ` David Timber
  0 siblings, 0 replies; 16+ messages in thread
From: David Timber @ 2026-04-30 17:13 UTC (permalink / raw)
  To: Yuezhang.Mo@sony.com, Namjae Jeon, Sungjong Seo
  Cc: linux-fsdevel@vger.kernel.org

On 4/30/26 20:48, Yuezhang.Mo@sony.com wrote:
> What I mean is that if the subsequent operations fail, such as
> exfat_cache_init() returning an error, exfat_free_default_upcase_ptable()
> needs to be called to release default_upcase_ptable.
Oh, you're right. Totally missed that. Thank you for being patient and
clearing that out. Disaster averted.

>
>> exfat_populate_upcase_ptable() as a separate function to use it on my
>> test cases. The compiler inlines the function anyway(although
>> appropriate attributes are missing in the original patch, my bad) so
>> there's no ELF overhead either. I'd like to leave it as it is, if that's
>> okay.
> Your test cases are only for the current development phase and will not
> be needed later, right?
>
> If not, keeping it is fine. Otherwise, we should try to keep the code as
> concise as possible.
I'll just get rid of exfat_init_default_upcase_ptable() and see if I can
just open code it in init_exfat_fs(). That should get rid of the problem
above as well.

Thanks,
Davo

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v1 4/4] exfat: more pedantic upcase table validity check
  2026-04-30  9:24     ` David Timber
@ 2026-05-01 11:27       ` Namjae Jeon
  2026-05-01 12:20         ` David Timber
  0 siblings, 1 reply; 16+ messages in thread
From: Namjae Jeon @ 2026-05-01 11:27 UTC (permalink / raw)
  To: David Timber
  Cc: Yuezhang.Mo@sony.com, Sungjong Seo, linux-fsdevel@vger.kernel.org

On Thu, Apr 30, 2026 at 6:24 PM David Timber <dxdt@dev.snart.me> wrote:
>
> On 4/30/26 16:58, Yuezhang.Mo@sony.com wrote:
> >>  static int exfat_load_upcase_table(struct super_block *sb,
> >> -             sector_t sector, unsigned long long num_sectors,
> >> -             unsigned int utbl_checksum)
> >> +             unsigned long long tbl_size, sector_t sector,
> >> +             unsigned long long num_sectors, unsigned int utbl_checksum)
> > After adding the parameter tbl_size, the parameter num_sectors is no
> > longer needed.
> Change the func signature? I'd like to hear Jeon's opinion on this.
I agree. It is cleaner to remove num_sectors from the arguments and
calculate it inside exfat_load_upcase_table() using tbl_size and
sb->s_blocksize_bits.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v1 4/4] exfat: more pedantic upcase table validity check
  2026-05-01 11:27       ` Namjae Jeon
@ 2026-05-01 12:20         ` David Timber
  0 siblings, 0 replies; 16+ messages in thread
From: David Timber @ 2026-05-01 12:20 UTC (permalink / raw)
  To: Namjae Jeon
  Cc: Yuezhang.Mo@sony.com, Sungjong Seo, linux-fsdevel@vger.kernel.org

On 5/1/26 20:27, Namjae Jeon wrote:
> On Thu, Apr 30, 2026 at 6:24 PM David Timber <dxdt@dev.snart.me> wrote:
>> On 4/30/26 16:58, Yuezhang.Mo@sony.com wrote:
>>>>  static int exfat_load_upcase_table(struct super_block *sb,
>>>> -             sector_t sector, unsigned long long num_sectors,
>>>> -             unsigned int utbl_checksum)
>>>> +             unsigned long long tbl_size, sector_t sector,
>>>> +             unsigned long long num_sectors, unsigned int utbl_checksum)
>>> After adding the parameter tbl_size, the parameter num_sectors is no
>>> longer needed.
>> Change the func signature? I'd like to hear Jeon's opinion on this.
> I agree. It is cleaner to remove num_sectors from the arguments and
> calculate it inside exfat_load_upcase_table() using tbl_size and
> sb->s_blocksize_bits.
Ack'd


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v1 1/4] exfat: refactor nls.c (tables)
  2026-04-30  9:22     ` David Timber
@ 2026-05-03  8:03       ` David Timber
  0 siblings, 0 replies; 16+ messages in thread
From: David Timber @ 2026-05-03  8:03 UTC (permalink / raw)
  To: Yuezhang.Mo@sony.com, Namjae Jeon, Sungjong Seo
  Cc: linux-fsdevel@vger.kernel.org

On 4/30/26 18:22, David Timber wrote:
> On 4/30/26 16:53, Yuezhang.Mo@sony.com wrote:
>>> +/* exfat/tables.c */
>>> +/* Upcase table macro */
>>> +#define EXFAT_NUM_UPCASE     (2918)
>>> +#define EXFAT_UTBL_COUNT     (0x10000)
>>> +
>>> +extern const unsigned short exfat_uni_def_upcase[EXFAT_NUM_UPCASE];
>>> +extern const unsigned short exfat_bad_uni_chars[];
>> The global variable exfat_bad_uni_chars[] is unnecessary, as is the 
>> generic function exfat_wstrchr().
>>
>> We can implement a dedicated function `exfat_is_bad_uni_char()`, in
>> which we can define static `exfat_bad_uni_chars`, or we can use a
>> switch...case... statement.
> I agree.
>
> I too think that switch a case statement is suitable here because
> there's only a few __u16's to compare so there's really no need to leave
> room for cache misses. Let the CPU prefetch the characters along with
> the instructions.
>
> Will make a separate commit if no objection.
Did some fiddling and found out that that could risk performance
regression. But it's a problem specific to Clang, which is not so
popular choice of compiler in the embedded world where it could have a
significant impact.

If it doesn't bother anyone, I'll send out the reroll.

https://github.com/llvm/llvm-project/issues/127365#issuecomment-4362781091

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2026-05-03  8:03 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-28 23:50 [PATCH v1 0/4] exfat: memory optimisations and stringent integrity checks for up-case table David Timber
2026-04-28 23:50 ` [PATCH v1 1/4] exfat: refactor nls.c (tables) David Timber
2026-04-30  7:53   ` Yuezhang.Mo
2026-04-30  9:22     ` David Timber
2026-05-03  8:03       ` David Timber
2026-04-28 23:50 ` [PATCH v1 2/4] exfat: use upcase_ptable and upcase_range_info to reduce memory footprint David Timber
2026-04-30  7:58   ` Yuezhang.Mo
2026-04-30  9:45     ` David Timber
2026-04-30 11:48       ` Yuezhang.Mo
2026-04-30 17:13         ` David Timber
2026-04-28 23:50 ` [PATCH v1 3/4] exfat: add default_upcase option (read-only) David Timber
2026-04-28 23:50 ` [PATCH v1 4/4] exfat: more pedantic upcase table validity check David Timber
2026-04-30  7:58   ` Yuezhang.Mo
2026-04-30  9:24     ` David Timber
2026-05-01 11:27       ` Namjae Jeon
2026-05-01 12:20         ` David Timber

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox