texlive[63353] Build/source/libs: harfbuzz 4.3.0

commits+kakuto at tug.org commits+kakuto at tug.org
Sat May 21 04:44:02 CEST 2022


Revision: 63353
          http://tug.org/svn/texlive?view=revision&revision=63353
Author:   kakuto
Date:     2022-05-21 04:44:01 +0200 (Sat, 21 May 2022)
Log Message:
-----------
harfbuzz 4.3.0

Modified Paths:
--------------
    trunk/Build/source/libs/README
    trunk/Build/source/libs/harfbuzz/ChangeLog
    trunk/Build/source/libs/harfbuzz/TLpatches/ChangeLog
    trunk/Build/source/libs/harfbuzz/TLpatches/TL-Changes
    trunk/Build/source/libs/harfbuzz/configure
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/BUILD.md
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/ChangeLog
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/Makefile.am
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/NEWS
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/configure.ac
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/meson.build
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/gen-tag-table.py
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/gen-use-table.py
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-algs.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-array.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-bimap.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-page.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-set-invertible.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-set.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-common.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-cs-common.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-dict-common.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-cff1-interp-cs.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-cff2-interp-cs.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-font.cc
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ft.cc
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-map.cc
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-map.h
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-map.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-meta.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-open-type.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff-common.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff1-table.cc
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff1-table.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff2-table.cc
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff2-table.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cmap-table.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-color-cpal-table.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-glyf-table.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-common.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-gsubgpos.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-use-table.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-tag-table.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-tag.cc
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-gvar-table.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-priority-queue.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-repacker.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-serialize.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-set.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff-common.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff1.cc
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff2.cc
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-plan.cc
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-plan.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset.cc
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-vector.hh
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/test-map.cc
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/test-priority-queue.cc
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/test-repacker.cc
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/test-set.cc
    trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/test-vector.cc
    trunk/Build/source/libs/harfbuzz/version.ac

Modified: trunk/Build/source/libs/README
===================================================================
--- trunk/Build/source/libs/README	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/README	2022-05-21 02:44:01 UTC (rev 63353)
@@ -12,7 +12,7 @@
 cairo 1.16.0 - checked 20oct18
   http://cairographics.org/releases/
 
-freetype2 2.11.1 - checked 03dec21
+freetype2 2.12.1 - checked 07may22
   http://download.savannah.gnu.org/releases/freetype/
 
 gd 2.3.3 - checked 13sep21
@@ -25,7 +25,7 @@
   http://sourceforge.net/projects/silgraphite/files/graphite2/
   (requires C++11)
 
-harfbuzz 4.2.1 - checked 25apr22
+harfbuzz 4.3.0 - checked 21may22
   https://github.com/harfbuzz/harfbuzz/releases/download/4.2.1/
 
 icu 70.1 - checked 16jan22

Modified: trunk/Build/source/libs/harfbuzz/ChangeLog
===================================================================
--- trunk/Build/source/libs/harfbuzz/ChangeLog	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/ChangeLog	2022-05-21 02:44:01 UTC (rev 63353)
@@ -1,3 +1,8 @@
+2022-05-21  Akira Kakuto  <kakuto at jcom.zaq.ne.jp>
+
+	Import harfbuzz-4.3.0.
+	* version.ac: Adjusted.
+
 2022-04-25  Akira Kakuto  <kakuto at jcom.zaq.ne.jp>
 
 	Import harfbuzz-4.2.1.

Modified: trunk/Build/source/libs/harfbuzz/TLpatches/ChangeLog
===================================================================
--- trunk/Build/source/libs/harfbuzz/TLpatches/ChangeLog	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/TLpatches/ChangeLog	2022-05-21 02:44:01 UTC (rev 63353)
@@ -1,3 +1,8 @@
+2022-05-21  Akira Kakuto  <kakuto at jcom.zaq.ne.jp>
+
+	Imported harfbuzz-4.3.0 source tree from:
+	https://github.com/harfbuzz/harfbuzz/releases/download/4.3.0/
+
 2022-04-25  Akira Kakuto  <kakuto at jcom.zaq.ne.jp>
 
 	Imported harfbuzz-4.2.1 source tree from:

Modified: trunk/Build/source/libs/harfbuzz/TLpatches/TL-Changes
===================================================================
--- trunk/Build/source/libs/harfbuzz/TLpatches/TL-Changes	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/TLpatches/TL-Changes	2022-05-21 02:44:01 UTC (rev 63353)
@@ -1,5 +1,5 @@
-Changes applied to the harfbuzz-4.2.1/ tree as obtained from:
-	https://github.com/harfbuzz/harfbuzz/releases/download/4.2.1/
+Changes applied to the harfbuzz-4.3.0/ tree as obtained from:
+	https://github.com/harfbuzz/harfbuzz/releases/download/4.3.0/
 
 Removed:
 	COPYING

Modified: trunk/Build/source/libs/harfbuzz/configure
===================================================================
--- trunk/Build/source/libs/harfbuzz/configure	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/configure	2022-05-21 02:44:01 UTC (rev 63353)
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.71 for harfbuzz (TeX Live) 4.2.1.
+# Generated by GNU Autoconf 2.71 for harfbuzz (TeX Live) 4.3.0.
 #
 # Report bugs to <tex-k at tug.org>.
 #
@@ -611,8 +611,8 @@
 # Identity of this package.
 PACKAGE_NAME='harfbuzz (TeX Live)'
 PACKAGE_TARNAME='harfbuzz--tex-live-'
-PACKAGE_VERSION='4.2.1'
-PACKAGE_STRING='harfbuzz (TeX Live) 4.2.1'
+PACKAGE_VERSION='4.3.0'
+PACKAGE_STRING='harfbuzz (TeX Live) 4.3.0'
 PACKAGE_BUGREPORT='tex-k at tug.org'
 PACKAGE_URL=''
 
@@ -1346,7 +1346,7 @@
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures harfbuzz (TeX Live) 4.2.1 to adapt to many kinds of systems.
+\`configure' configures harfbuzz (TeX Live) 4.3.0 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1418,7 +1418,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of harfbuzz (TeX Live) 4.2.1:";;
+     short | recursive ) echo "Configuration of harfbuzz (TeX Live) 4.3.0:";;
    esac
   cat <<\_ACEOF
 
@@ -1523,7 +1523,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-harfbuzz (TeX Live) configure 4.2.1
+harfbuzz (TeX Live) configure 4.3.0
 generated by GNU Autoconf 2.71
 
 Copyright (C) 2021 Free Software Foundation, Inc.
@@ -2064,7 +2064,7 @@
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by harfbuzz (TeX Live) $as_me 4.2.1, which was
+It was created by harfbuzz (TeX Live) $as_me 4.3.0, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -4823,7 +4823,7 @@
 
 # Define the identity of the package.
  PACKAGE='harfbuzz--tex-live-'
- VERSION='4.2.1'
+ VERSION='4.3.0'
 
 
 # Some tools Automake needs.
@@ -5034,9 +5034,9 @@
 
 
 HB_VERSION_MAJOR=4
-HB_VERSION_MINOR=2
-HB_VERSION_MICRO=1
-HB_VERSION=4.2.1
+HB_VERSION_MINOR=3
+HB_VERSION_MICRO=0
+HB_VERSION=4.3.0
 
 ac_ext=c
 ac_cpp='$CPP $CPPFLAGS'
@@ -8817,7 +8817,7 @@
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by harfbuzz (TeX Live) $as_me 4.2.1, which was
+This file was extended by harfbuzz (TeX Live) $as_me 4.3.0, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -8885,7 +8885,7 @@
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
-harfbuzz (TeX Live) config.status 4.2.1
+harfbuzz (TeX Live) config.status 4.3.0
 configured by $0, generated by GNU Autoconf 2.71,
   with options \\"\$ac_cs_config\\"
 

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/BUILD.md
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/BUILD.md	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/BUILD.md	2022-05-21 02:44:01 UTC (rev 63353)
@@ -5,7 +5,7 @@
 
 whereas on Fedora, RHEL, CentOS, and other Red Hat based systems you would do:
 
-    sudo dnf install meson pkgconfig gtk-doc gcc gcc-c++ freetype-devel glib2-devel cairo-dev
+    sudo dnf install meson pkgconfig gtk-doc gcc gcc-c++ freetype-devel glib2-devel cairo-devel
 
 and on ArchLinux and Manjaro:
 

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/ChangeLog
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/ChangeLog	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/ChangeLog	2022-05-21 02:44:01 UTC (rev 63353)
@@ -1,3 +1,2554 @@
+commit aee123fc83388b8f5acfb301d87bd92eccc5b843
+Author: Khaled Hosny <khaled at aliftype.com>
+Date:   Fri May 20 21:07:25 2022 +0200
+
+    4.3.0
+
+ NEWS                   | 17 +++++++++++++++++
+ configure.ac           |  2 +-
+ docs/harfbuzz-docs.xml |  1 +
+ meson.build            |  2 +-
+ src/hb-map.cc          |  2 +-
+ src/hb-version.h       |  6 +++---
+ 6 files changed, 24 insertions(+), 6 deletions(-)
+
+commit 975a5f919467c9bc4cad1340ebf07ae32bf07e14
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri May 20 12:34:49 2022 -0600
+
+    [array] Use hb_memcmp instead of memcmp
+    
+    Fixes ubsan error.
+
+ src/hb-array.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 55804e8d68af0685867d20a1796b952c6ff8db60
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri May 20 11:40:44 2022 -0600
+
+    [hb-ft] Minor rearrange of struct members
+    
+    To make clear what members the lock protects.
+
+ src/hb-ft.cc | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 4e11da054d2c527fa64b33d49b33e3aa6b49077c
+Author: Garret Rieger <grieger at google.com>
+Date:   Fri May 20 01:42:34 2022 +0000
+
+    [repacker] update repacker test golden file.
+    
+    Changed due to removal of Kahn sorting.
+
+ test/api/fonts/repacker_expected.otf | Bin 1400 -> 1400 bytes
+ 1 file changed, 0 insertions(+), 0 deletions(-)
+
+commit cbf8f44c9b6de43387c61fdd43cf6bf0b89c3c08
+Author: Garret Rieger <grieger at google.com>
+Date:   Thu May 19 21:25:21 2022 +0000
+
+    [subset-perf] swap instead of copying vertice's when reordering during sort.
+
+ src/hb-repacker.hh  | 22 ++++++++++++++++------
+ src/hb-serialize.hh | 11 ++++++++++-
+ 2 files changed, 26 insertions(+), 7 deletions(-)
+
+commit b32ca2a292f256a40e445990f104f09c5920d0bd
+Author: Garret Rieger <grieger at google.com>
+Date:   Thu May 19 20:45:39 2022 +0000
+
+    [subset-perf] remove sort_kahn from repacker.
+    
+    Without an optimized FIFO queue implementation it's nearly as slow as the now optimized sort_shortest_distance.
+
+ src/hb-repacker.hh   | 53 ---------------------------------
+ src/test-repacker.cc | 84 ----------------------------------------------------
+ 2 files changed, 137 deletions(-)
+
+commit 4266cf3be266aef27a5d60530860915c68ba03e1
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 18:15:46 2022 -0600
+
+    [array] Specialize operator== for bytes_t and ubytes_t
+
+ src/hb-array.hh | 17 +++++++++++++----
+ 1 file changed, 13 insertions(+), 4 deletions(-)
+
+commit 6eaa22e9d71d2b09d4bd211026194d618dcc8aad
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 18:00:58 2022 -0600
+
+    [serialize] Reduce link_t size from 16 to 12
+
+ src/hb-serialize.hh | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+commit 30ba9a39e2249b86310c36564373f4f0347012e1
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 17:34:58 2022 -0600
+
+    [vector] Add emplacing push implementation
+
+ src/hb-vector.hh | 24 +++++++++++++++++++++---
+ 1 file changed, 21 insertions(+), 3 deletions(-)
+
+commit 25393288f0d6b98355bc4b72bce15ab6f77a5b0e
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 17:19:21 2022 -0600
+
+    [test] Fix compiler warning
+
+ test/api/test-set.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+commit 73b8360dcfb57eaa9acffc7967015a113421eeda
+Author: Garret Rieger <grieger at google.com>
+Date:   Thu May 19 22:59:51 2022 +0000
+
+    [subset] fix fuzzer found underflow when heap push fails.
+    
+    Fixes https://oss-fuzz.com/testcase-detail/5148625505746944.
+
+ src/hb-priority-queue.hh | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit f1bf14ea89ea082e5edd4e9c90738370bffcab1c
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 16:42:35 2022 -0600
+
+    Revert "[set] Cache hash value"
+    
+    This reverts commit 44952bcc259a906b8875ed62dc40de96ade8b95c.
+    
+    While we investivate https://github.com/harfbuzz/harfbuzz/issues/3599
+
+ src/hb-bit-set.hh | 22 +---------------------
+ 1 file changed, 1 insertion(+), 21 deletions(-)
+
+commit b4d1ec310cd2c8a6e250c71f865c45fe7cadd5fa
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 16:06:21 2022 -0600
+
+    [algs] Declare coerce() as constexpr
+
+ src/hb-algs.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 2fdb7616f589ebb9fc060fdb88745e0219a78a14
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 16:00:43 2022 -0600
+
+    [map Further adjust hash function
+
+ src/hb-map.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 01fc90b68c023d380f3cd44e13b21972b3a41dcf
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 16:00:06 2022 -0600
+
+    [map] Adjust hash function
+
+ src/hb-map.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit a47b0aebf5f8d56dd78ddd651d40727b729a7577
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 15:52:00 2022 -0600
+
+    [vector] Fix remove() implementation
+    
+    test-vector under valgrind happy now.
+
+ src/hb-vector.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 3bd755c32dc7c6ba189783daf89e4cde81715483
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 15:51:18 2022 -0600
+
+    [test-vector] Test remove()
+    
+    Currently buggy. Valgrind confirms.
+
+ src/test-vector.cc | 2 ++
+ 1 file changed, 2 insertions(+)
+
+commit 58f848daa8f596007a8dadee3fcb462548def980
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 15:42:54 2022 -0600
+
+    [set/map] Adjust hash function return type
+
+ src/hb-bit-page.hh           | 8 ++++----
+ src/hb-bit-set-invertible.hh | 2 +-
+ src/hb-bit-set.hh            | 6 +++---
+ src/hb-map.hh                | 4 ++--
+ src/hb-set.hh                | 2 +-
+ 5 files changed, 11 insertions(+), 11 deletions(-)
+
+commit 6544fc284f55ec1d3199bc610eeac39af935df9c
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 15:28:09 2022 -0600
+
+    [vector] Add further copy implementation
+
+ src/hb-vector.hh | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+commit c19f1169521c6fa95c690285a3d24123f387a98e
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 15:27:52 2022 -0600
+
+    [meta] Remove non-existing gcc4 trait implementation
+
+ src/hb-meta.hh   | 2 --
+ src/hb-vector.hh | 2 +-
+ 2 files changed, 1 insertion(+), 3 deletions(-)
+
+commit 679b900e9b27fdecb9a694c58f71e7bc9e2cd125
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 15:27:32 2022 -0600
+
+    [meta] Fix gcc4 trait implementation
+
+ src/hb-meta.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit fb77f48ffd3fe7fcd17843b9cdc6ca677d36602c
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 15:02:10 2022 -0600
+
+    [vector] Optimize vector copy
+
+ src/hb-meta.hh   |  4 ++++
+ src/hb-vector.hh | 30 ++++++++++++++++++++++++++++--
+ 2 files changed, 32 insertions(+), 2 deletions(-)
+
+commit 28b44ac46a24f6987d2c2565e0ac72d5b2763d81
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 15:01:56 2022 -0600
+
+    [set] Switch set copy to vector operator =
+    
+    Slows it down currently.
+
+ src/hb-bit-set.hh | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+commit 37d3275dec01edfafe2cc744ed85a3febb964594
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 15:01:23 2022 -0600
+
+    [test-vector] Enable disabled test
+    
+    This seems to work already.
+
+ src/test-vector.cc | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+commit 544ffb913ea515fae77f26714a1c7c620cdab0ed
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 14:50:12 2022 -0600
+
+    [set] Adjust grow_vector condition
+
+ src/hb-vector.hh | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+commit 0623aa598ba6a7cc14d00091935bc8811b3c6aac
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 14:12:42 2022 -0600
+
+    [benchmark-set] Add benchmark for set copy
+
+ perf/benchmark-set.cc | 23 +++++++++++++++++++++++
+ src/hb-set.hh         |  8 ++++----
+ 2 files changed, 27 insertions(+), 4 deletions(-)
+
+commit 44952bcc259a906b8875ed62dc40de96ade8b95c
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 14:02:48 2022 -0600
+
+    [set] Cache hash value
+
+ src/hb-bit-set.hh | 26 +++++++++++++++++++++++---
+ 1 file changed, 23 insertions(+), 3 deletions(-)
+
+commit 844ac328e46f9bfcc5481f2dd525603c9a448ffe
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 13:54:31 2022 -0600
+
+    [set] Fix hb_set_t hash stability
+
+ src/hb-bit-page.hh | 2 +-
+ src/hb-bit-set.hh  | 6 ++++--
+ 2 files changed, 5 insertions(+), 3 deletions(-)
+
+commit 2d0b1248b23c9eb931c013a35daec62c48ee293f
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 13:53:53 2022 -0600
+
+    [test-map] Test hb_set_t hash stability
+    
+    Fails currently.
+
+ src/test-map.cc | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+commit 561e02fefb72be902482fc49dcec66b4c585b798
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 13:38:52 2022 -0600
+
+    [map] Make hb_map_t hashable
+
+ src/hb-map.hh   |  8 ++++++++
+ src/test-map.cc | 21 +++++++++++----------
+ 2 files changed, 19 insertions(+), 10 deletions(-)
+
+commit ad176990895963c1b83274d0ef3c5ae148a4f760
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 13:36:12 2022 -0600
+
+    [map] Add is_equal() / towards making hb_map_t hashable
+    
+    New API:
+    + hb_map_is_equal()
+
+ docs/harfbuzz-sections.txt |  1 +
+ src/hb-map.cc              | 20 ++++++++++++++++++++
+ src/hb-map.h               |  4 ++++
+ src/hb-map.hh              | 15 +++++++++++++++
+ src/test-map.cc            | 24 ++++++++++++++++++++++++
+ 5 files changed, 64 insertions(+)
+
+commit 14a24d8e3f7d9b8379452b1596e4aff6603e1f25
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 13:03:50 2022 -0600
+
+    [vector] Make hb_vector_t hashable
+
+ src/hb-vector.hh |  1 +
+ src/test-map.cc  | 26 ++++++++++++++++++++++++++
+ 2 files changed, 27 insertions(+)
+
+commit 124f9aeb9b4c77fe1e2a733c5aceb9172d169f9f
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 19 12:58:02 2022 -0600
+
+    [set] Make hb_set_t hashable
+
+ src/hb-bit-page.hh           |  7 +++++++
+ src/hb-bit-set-invertible.hh |  2 ++
+ src/hb-bit-set.hh            |  6 ++++++
+ src/hb-set.hh                |  4 ++++
+ src/test-map.cc              | 23 +++++++++++++++++++++++
+ 5 files changed, 42 insertions(+)
+
+commit 3ab2c7935f5b9706e4767a6e28ff1dcd739ac271
+Author: Garret Rieger <grieger at google.com>
+Date:   Thu May 19 17:23:36 2022 +0000
+
+    [subset-perf] Signficiantly speed up ClassDef*::subset.
+    
+    Eliminates the usage of a glyph -> klass hash map and replaces it with a vector storing the mapping. This allows us to use the vector directly as the iterator driving the serialize. Approximately 1% speedup for Noto Nastaliq.
+
+ src/hb-ot-layout-common.hh | 72 ++++++++++++++++++++++------------------------
+ 1 file changed, 35 insertions(+), 37 deletions(-)
+
+commit e3e685e5eec1cb400e0b4bd371872cb9394c79bc
+Author: David Corbett <corbett.dav at northeastern.edu>
+Date:   Wed May 18 15:05:55 2022 -0400
+
+    [ot-tags] Fix `min_subtag_len` calculations
+
+ src/gen-tag-table.py   | 12 +++++-------
+ src/hb-ot-tag-table.hh |  3 +--
+ 2 files changed, 6 insertions(+), 9 deletions(-)
+
+commit 0b1c2ff96a333a3e78eeefe54cb9e9509120990a
+Author: Garret Rieger <grieger at google.com>
+Date:   Wed May 18 23:32:03 2022 +0000
+
+    [subset-perf] Remove extra map lookup in ClassDef subset methods.
+
+ src/hb-ot-layout-common.hh | 15 ++++++++-------
+ 1 file changed, 8 insertions(+), 7 deletions(-)
+
+commit 13ace77f1daaf94d79ad400e3943f71fa5139e70
+Author: Garret Rieger <grieger at google.com>
+Date:   Wed May 18 22:38:43 2022 +0000
+
+    [subset-perf] Use glyph_map instead of set in ClassDefFormat.
+
+ src/hb-ot-layout-common.hh | 29 +++++++++++++++--------------
+ 1 file changed, 15 insertions(+), 14 deletions(-)
+
+commit adae2f2272bf51c6b4df2ba5d0e10eb25386e58c
+Author: Garret Rieger <grieger at google.com>
+Date:   Wed May 18 21:42:28 2022 +0000
+
+    [subset-perf] Cache a glyph map for gsub.
+    
+    This allows us in some cases to avoid using glyph_set_gsub as a filter.
+
+ src/hb-ot-layout-common.hh |  8 +++-----
+ src/hb-subset-plan.cc      | 21 +++++++++++++++++++++
+ src/hb-subset-plan.hh      |  1 +
+ 3 files changed, 25 insertions(+), 5 deletions(-)
+
+commit 202e6c469963fe76f3320a956be8b194adb9089d
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 17:12:43 2022 -0600
+
+    [subset] Remove unnecessary test
+
+ src/hb-ot-layout-gsubgpos.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit cedf739646d67e73c06e2569d4be2d7f32e39fd8
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 16:52:35 2022 -0600
+
+    Add some commented-out code
+
+ src/hb-ot-layout-common.hh | 14 +++++++++++++-
+ 1 file changed, 13 insertions(+), 1 deletion(-)
+
+commit 6b62c10f0228d011526ef41772a65e6f12022ddb
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 16:27:54 2022 -0600
+
+    [priority-queue] Remove old init/fini
+
+ src/hb-priority-queue.hh | 7 -------
+ 1 file changed, 7 deletions(-)
+
+commit bff8248a9d44654d7901150e86e684af0cfa8681
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 16:25:03 2022 -0600
+
+    [repacker] Pre-alloc vertices
+
+ src/hb-repacker.hh | 2 ++
+ 1 file changed, 2 insertions(+)
+
+commit 39a424caf04392702b62950c832fd1f67204ba62
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 16:17:16 2022 -0600
+
+    [priority-queue] Optimize heap access
+
+ src/hb-priority-queue.hh   | 18 ++++++++++++------
+ src/test-priority-queue.cc |  8 --------
+ 2 files changed, 12 insertions(+), 14 deletions(-)
+
+commit 9308659fd76bb400da2c869ca2f945760adfaf56
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 16:14:25 2022 -0600
+
+    [priority-queue] Optimize swap()
+
+ src/hb-priority-queue.hh | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+commit c7317ef7617a1387c88db19582f1b9879a722d7a
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 16:03:41 2022 -0600
+
+    [repacker] Avoid destroying and recreating objects
+
+ src/hb-repacker.hh | 2 --
+ 1 file changed, 2 deletions(-)
+
+commit 864e09a0c460916c06d8becbc3480d06692cd634
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 15:59:29 2022 -0600
+
+    [repacker] Reuse allocated vector
+
+ src/hb-repacker.hh | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+commit ca77f164704c6463b09d973251f6f9995172d8c1
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 15:55:49 2022 -0600
+
+    [repacker] Remove unnecessary vector .fini() calls
+
+ src/hb-repacker.hh | 2 --
+ 1 file changed, 2 deletions(-)
+
+commit 4cfc2d668e3df53a6564cef1be65ad0239470123
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 15:32:19 2022 -0600
+
+    [subset] Use a std::move on set_t when feasible
+
+ src/hb-ot-layout-gsubgpos.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 1f578b5a32337011766e078331c0ba8ce4ce8af8
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 15:24:40 2022 -0600
+
+    [set] Add pre-allocation internal API
+
+ src/hb-bit-set-invertible.hh | 1 +
+ src/hb-bit-set.hh            | 7 +++++++
+ src/hb-set.hh                | 1 +
+ 3 files changed, 9 insertions(+)
+
+commit 48dfbd54a3c9876e86bcdbeb47ae42300ee9f08f
+Author: Garret Rieger <grieger at google.com>
+Date:   Wed May 18 21:03:56 2022 +0000
+
+    [subset] minor cleanup.
+
+ src/hb-ot-layout-common.hh | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+commit 482c6e5dc41402e60acf609fca0d9d8e8fbc4d9d
+Author: Garret Rieger <grieger at google.com>
+Date:   Wed May 18 19:58:55 2022 +0000
+
+    [subset-perf] Speed up Coverage::serialize by caching iterator.
+
+ src/hb-ot-layout-common.hh | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+commit 14b18725f04bba0dac6da943244230c65d3879d4
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 15:14:32 2022 -0600
+
+    In Coverage::iter_t, assume iterators are from same Coverage object
+    
+    No need to support otherwise.
+
+ src/hb-ot-layout-common.hh | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+commit 27141735c3e8caa807c3528ce9793b8c8f05a556
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 15:12:49 2022 -0600
+
+    [subset] Add Coverage::__end__ implementation
+
+ src/hb-ot-layout-common.hh | 23 +++++++++++++++++++++++
+ 1 file changed, 23 insertions(+)
+
+commit c476f58adba6680c655cb7bcbdd28d3bd4b7ad37
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 14:20:23 2022 -0600
+
+    [subset] Write CoverageFormat2::intersects_coverage() as bsearch()
+
+ src/hb-ot-layout-common.hh | 23 ++++++++++++++---------
+ 1 file changed, 14 insertions(+), 9 deletions(-)
+
+commit 63c6695108ceb19b4b79e00782c3106801d7dc01
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 13:53:52 2022 -0600
+
+    [ot-layout] Cosmetic
+    
+    The implementation of HBUINT16 operator == is slower than just
+    comparing to ints.
+
+ src/hb-ot-layout-common.hh | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+commit 777debd748dfd803bbd16bcc1bbf2afd7db2fc82
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 13:46:06 2022 -0600
+
+    [subset] Rewrite CoverageFormat2::intersects as dagger
+
+ src/hb-ot-layout-common.hh | 10 +++-------
+ 1 file changed, 3 insertions(+), 7 deletions(-)
+
+commit cf5001fac7770286082ced9d3c5c5fefa3b19d79
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 13:38:29 2022 -0600
+
+    [subset] Optimize CoverageFormat2::intersected_coverage_glyphs
+
+ src/hb-ot-layout-common.hh | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+commit 6f37c2079815bc0ac9193c8e9028da4872374402
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 13:25:42 2022 -0600
+
+    [subset] Minor rewrite in CoverageFormat2::serialize()
+
+ src/hb-ot-layout-common.hh | 7 +------
+ 1 file changed, 1 insertion(+), 6 deletions(-)
+
+commit e91863b7173543b850e1758873f96c76c67f8ce8
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 12:39:55 2022 -0600
+
+    [subset-cff] Pre-size map in subr_remap_t::create()
+
+ src/hb-bimap.hh             | 6 ++++++
+ src/hb-subset-cff-common.hh | 1 +
+ 2 files changed, 7 insertions(+)
+
+commit ce60462173c7d22f9bad8531a2490a551f004197
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 12:34:27 2022 -0600
+
+    [subset-plan] Pre-size maps in _create_old_gid_to_new_gid_map()
+
+ src/hb-subset-plan.cc | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+commit f82ee17a75dda53ac5c506136221b93eed53aee1
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 12:17:43 2022 -0600
+
+    [map] Pre-size map in constructor if we can
+
+ src/hb-map.hh           | 13 ++++++++-----
+ src/hb-ot-cmap-table.hh |  4 ++--
+ 2 files changed, 10 insertions(+), 7 deletions(-)
+
+commit b5aa8a27eac851503eaee086912f269b51e68724
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 11:58:58 2022 -0600
+
+    [subset-cff] Cosmetic
+
+ src/hb-subset-cff-common.hh | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+commit 0b201623f54420a898d3538c8673b450923bcac1
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 11:58:22 2022 -0600
+
+    [subset-cff] Fix previous commit
+    
+    Oops!
+
+ src/hb-subset-cff-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 4792309265ea17aea0c5fd6821ed453fe8427ab4
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 11:54:08 2022 -0600
+
+    [subset-cff] Access vector directly
+
+ src/hb-subset-cff-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 7c86f2e763e44b4c96cd26f1ce06225b4aba977f
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 11:45:27 2022 -0600
+
+    [subset-cff] Pre-alloc out buffer
+
+ src/hb-subset-cff-common.hh | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit 0761e7cdfd00d5347657bdf009c3035be4ebab44
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 11:37:57 2022 -0600
+
+    [subset-cff] Avoid resetting buffer as encoder does
+
+ src/hb-subset-cff-common.hh | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+commit 71aa10a3942081f2c4ce0c2c5d4c3897d13d887c
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 11:37:24 2022 -0600
+
+    [subset-cff] Manually grow vector to avoid memset overhead
+
+ src/hb-subset-cff-common.hh | 10 ++++------
+ 1 file changed, 4 insertions(+), 6 deletions(-)
+
+commit f455cc53fd4a30682c549fcad6d4112b98688aca
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 11:31:55 2022 -0600
+
+    [subset-cff] Reuse buffer allocation
+
+ src/hb-subset-cff-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 3e4ab2ad9c2de17218c16787b59d63c236262d8f
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 11:16:46 2022 -0600
+
+    [perf/benchmark-ot] Add zh-hans
+
+ perf/benchmark-ot.cc | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit 6e668a2adefdc186dcd300136b3535c43d6fdffd
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 11:16:11 2022 -0600
+
+    [perf/benchmark-ot] Rename test
+
+ perf/benchmark-ot.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit e24797aeac65aaa1edd836bf9708f488044d3939
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 11:10:10 2022 -0600
+
+    [ot-tags] Follow-up to previous commit
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3591
+
+ src/gen-tag-table.py   | 16 ++++++++--------
+ src/hb-ot-tag-table.hh | 18 +++++++++---------
+ 2 files changed, 17 insertions(+), 17 deletions(-)
+
+commit f5d619be79e9f23f67f23513e60c546fc498f1b8
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 18 11:04:52 2022 -0600
+
+    [ot-tags] Further gate the slow complex case, and add more tests
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3591
+    
+    Still 'zh-trad' is the slowest case.
+    
+    --------------------------------------------------------------------------------------------------
+    Benchmark                                                        Time             CPU   Iterations
+    --------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_trad          136 ns          136 ns      5107838
+    BM_hb_ot_tags_from_script_and_language/COMMON ab_abcd          115 ns          115 ns      6103104
+    BM_hb_ot_tags_from_script_and_language/COMMON ab_abc          25.4 ns         25.3 ns     27674482
+    BM_hb_ot_tags_from_script_and_language/COMMON abcdef_XY       20.2 ns         20.1 ns     34795719
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY         19.4 ns         19.3 ns     36390401
+    BM_hb_ot_tags_from_script_and_language/COMMON cxy_CN          33.5 ns         33.4 ns     20998939
+    BM_hb_ot_tags_from_script_and_language/COMMON exy_CN          25.1 ns         25.0 ns     27705832
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN           34.2 ns         34.1 ns     20564356
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US           15.5 ns         15.5 ns     45032204
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US            15.9 ns         15.8 ns     44412379
+    BM_hb_ot_tags_from_script_and_language/COMMON none            4.72 ns         4.71 ns    149101665
+    BM_hb_ot_tags_from_script_and_language/LATIN none             4.72 ns         4.70 ns    149254498
+
+ perf/benchmark-ot.cc   | 3 +++
+ src/gen-tag-table.py   | 3 +++
+ src/hb-ot-tag-table.hh | 3 +++
+ 3 files changed, 9 insertions(+)
+
+commit 9c64bda21dd4215a3caa32e4127d9f2017e50de2
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 17 17:31:18 2022 -0600
+
+    [ot-tag] Whitespace
+
+ src/hb-ot-tag.cc | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+commit 3df8017e9b7ea2b72477294133563b4ff304a007
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 17 17:29:39 2022 -0600
+
+    [ot-tag] Optimize subtag_matches() more
+
+ src/gen-tag-table.py   |   2 +-
+ src/hb-ot-tag-table.hh | 112 ++++++++++++++++++++++++-------------------------
+ src/hb-ot-tag.cc       |   5 +--
+ 3 files changed, 59 insertions(+), 60 deletions(-)
+
+commit b231fc2dbcee402d1cff578371f9ad89ff594bb2
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 17 17:02:48 2022 -0600
+
+    [perf/benchmark-ot] Add a couple more test cases
+
+ perf/benchmark-ot.cc | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit 3524b14fa06dbf9caddef1d2f598e2f4f46315c1
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 17 17:02:48 2022 -0600
+
+    [perf/benchmark-ot] Add a couple more test cases
+
+ perf/benchmark-ot.cc | 2 ++
+ 1 file changed, 2 insertions(+)
+
+commit 7f6e8c5536fd13a56b4bd030233960aa1af38d05
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 17 16:58:35 2022 -0600
+
+    [ot-tags] Optimize subtag_matches() further
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3591
+    
+    Comparing before to after
+    Benchmark                                                               Time             CPU      Time Old      Time New       CPU Old       CPU New
+    ----------------------------------------------------------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY                -0.3371         -0.3371            71            47            71            47
+
+ src/hb-ot-tag.cc | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+commit 27c11405a263ad43d24e2ed460e15f247ac06d17
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 17 16:51:51 2022 -0600
+
+    [ot-tag] Optimize subtag_matches
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3591
+
+ src/hb-ot-tag.cc | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+commit a07d818597385c6d83265f8320b9c1334c539758
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 17 16:46:10 2022 -0600
+
+    [ot-tag] Add a likely() to the cache hit case
+
+ src/hb-ot-tag.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 0ff5d36cd451dffe51a8c0637b4a544882663a1d
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 17 16:34:52 2022 -0600
+
+    [perf/benchmark-ot] Fix benchmark
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3591
+    
+    Ouch!
+    
+    These are the current numbers:
+    
+    ------------------------------------------------------------------------------------------------
+    Benchmark                                                      Time             CPU   Iterations
+    ------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY       78.0 ns         77.7 ns      8917912
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN         44.9 ns         44.8 ns     15475318
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US         17.6 ns         17.5 ns     39812340
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US          18.2 ns         18.1 ns     38356204
+    BM_hb_ot_tags_from_script_and_language/COMMON none          4.76 ns         4.74 ns    148746131
+    BM_hb_ot_tags_from_script_and_language/LATIN none           4.73 ns         4.71 ns    148421349
+
+ perf/benchmark-ot.cc | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+commit dfca47f419b6ef5c6df813822e4b10a7cec92434
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 17 16:21:02 2022 -0600
+
+    [ot-tag] Cache last bsearch result
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3591
+    
+    Humm. Looks like not all of the fat is bsearch overhead now. I cached
+    the last bsearch result, but most of the time is still there. I'm
+    baffled.
+    
+    Before:
+    ------------------------------------------------------------------------------------------------
+    Benchmark                                                      Time             CPU   Iterations
+    ------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY       8.08 ns         8.05 ns     84500482
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN         42.2 ns         42.1 ns     16722006
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US         16.1 ns         16.0 ns     43461527
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US          16.5 ns         16.5 ns     42448505
+    BM_hb_ot_tags_from_script_and_language/COMMON none          4.34 ns         4.33 ns    161290530
+    BM_hb_ot_tags_from_script_and_language/LATIN none           4.34 ns         4.33 ns    162339799
+    
+    After:
+    ------------------------------------------------------------------------------------------------
+    Benchmark                                                      Time             CPU   Iterations
+    ------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY       8.13 ns         8.11 ns     80438134
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN         40.0 ns         39.9 ns     17487939
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US         12.7 ns         12.7 ns     55124394
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US          13.1 ns         13.0 ns     53660125
+    BM_hb_ot_tags_from_script_and_language/COMMON none          4.61 ns         4.60 ns    151394104
+    BM_hb_ot_tags_from_script_and_language/LATIN none           4.70 ns         4.68 ns    150402847
+
+ src/hb-ot-tag.cc | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+commit 909f00ac6e6b3eb459f0553b84fe508bb697e9af
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 17 15:51:41 2022 -0600
+
+    [ot-tags] Further speed up language bsearch()
+    
+    Using an integer tag to bsearch, instead of string.
+    
+    Part of: https://github.com/harfbuzz/harfbuzz/issues/3591
+    
+    Before:
+    ------------------------------------------------------------------------------------------------
+    Benchmark                                                      Time             CPU   Iterations
+    ------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY       8.11 ns         8.08 ns     87067795
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN         53.6 ns         53.5 ns     13042418
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US         24.2 ns         24.1 ns     29052731
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US          24.4 ns         24.3 ns     28736769
+    BM_hb_ot_tags_from_script_and_language/COMMON none          4.43 ns         4.41 ns    160370413
+    BM_hb_ot_tags_from_script_and_language/LATIN none           4.35 ns         4.34 ns    160578191
+    
+    After:
+    ------------------------------------------------------------------------------------------------
+    Benchmark                                                      Time             CPU   Iterations
+    ------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY       7.97 ns         7.95 ns     85208363
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN         41.7 ns         41.6 ns     16945817
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US         16.1 ns         16.0 ns     43613523
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US          16.5 ns         16.4 ns     42568107
+    BM_hb_ot_tags_from_script_and_language/COMMON none          4.30 ns         4.29 ns    164055469
+    BM_hb_ot_tags_from_script_and_language/LATIN none           4.29 ns         4.27 ns    163793591
+
+ src/gen-tag-table.py   |    2 +-
+ src/hb-ot-tag-table.hh | 3206 ++++++++++++++++++++++++------------------------
+ src/hb-ot-tag.cc       |   29 +-
+ 3 files changed, 1622 insertions(+), 1615 deletions(-)
+
+commit c460cf74ce2a3ebe5d285e03dc122fb60ff70e01
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 17 15:30:11 2022 -0600
+
+    [ot-tags] Cosmetic
+
+ src/hb-ot-tag.cc | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+commit 1c8226ed14c1ac7d82ea5482bdf2a7d019dd38a2
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 17 15:28:50 2022 -0600
+
+    Fix compiler warning
+    
+    On Mac compiler:
+    
+    FAILED: src/libharfbuzz.0.dylib.p/hb-ot-tag.cc.o
+    c++ -Isrc/libharfbuzz.0.dylib.p -Isrc -I../src -I. -I.. -I/usr/local/opt/freetype/include/freetype2 -I/usr/local/Cellar/graphite2/1.3.14/include -I/usr/local/Cellar/glib/2.72.1/include/glib-2.0 -I/usr/local/Cellar/glib/2.72.1/lib/glib-2.0/include -I/usr/local/opt/gettext/include -I/usr/local/Cellar/pcre/8.45/include -Xclang -fcolor-diagnostics --coverage -pipe -Wall -Winvalid-pch -Wnon-virtual-dtor -std=c++11 -fno-rtti -O2 -g -fno-exceptions -fno-rtti -fno-threadsafe-statics -fvisibility-inlines-hidden -DHAVE_CONFIG_H -Wno-non-virtual-dtor -MD -MQ src/libharfbuzz.0.dylib.p/hb-ot-tag.cc.o -MF src/libharfbuzz.0.dylib.p/hb-ot-tag.cc.o.d -o src/libharfbuzz.0.dylib.p/hb-ot-tag.cc.o -c ../src/hb-ot-tag.cc
+    In file included from ../src/hb-ot-tag.cc:29:
+    In file included from ../src/hb.hh:481:
+    ../src/hb-array.hh:359:14: error: missing default argument on parameter 'ds'
+                  Ts... ds) const
+                        ^
+    ../src/hb-ot-tag.cc:292:58: note: in instantiation of function template specialization 'hb_sorted_array_t<const LangTag>::bfind<const char *, unsigned int>' requested here
+        if (hb_sorted_array (ot_languages, ot_languages_len).bfind (lang_str, &tag_idx,
+                                                             ^
+    1 error generated.
+
+ src/hb-array.hh  | 9 ++++-----
+ src/hb-ot-tag.cc | 4 +---
+ 2 files changed, 5 insertions(+), 8 deletions(-)
+
+commit c1f4b57c064d41a291976e6d126f7bf0f6e66bc9
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 17 15:19:40 2022 -0600
+
+    [ot-tags] Optimize language comparison
+    
+    Now that we know both strings are of equal len of 2 or 3, optimize.
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3591
+    
+    Before:
+    ------------------------------------------------------------------------------------------------
+    Benchmark                                                      Time             CPU   Iterations
+    ------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY       8.50 ns         8.47 ns     81221549
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN         79.6 ns         79.3 ns      8785804
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US         40.0 ns         39.9 ns     17462768
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US          39.2 ns         39.1 ns     17886793
+    BM_hb_ot_tags_from_script_and_language/COMMON none          4.31 ns         4.30 ns    162805417
+    BM_hb_ot_tags_from_script_and_language/LATIN none           4.32 ns         4.31 ns    162656688
+    
+    After:
+    ------------------------------------------------------------------------------------------------
+    Benchmark                                                      Time             CPU   Iterations
+    ------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY       8.27 ns         8.24 ns     81868701
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN         56.1 ns         56.0 ns     12353284
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US         24.3 ns         24.2 ns     28955030
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US          24.5 ns         24.4 ns     28664868
+    BM_hb_ot_tags_from_script_and_language/COMMON none          4.35 ns         4.34 ns    161190014
+    BM_hb_ot_tags_from_script_and_language/LATIN none           4.36 ns         4.34 ns    161319000
+
+ src/hb-array.hh  | 14 ++++++++------
+ src/hb-ot-tag.cc | 19 ++++++-------------
+ 2 files changed, 14 insertions(+), 19 deletions(-)
+
+commit dde48d78c1e1c11f3c770491a1d618386b3d92f8
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 17 15:07:49 2022 -0600
+
+    Fix compiler warning
+
+ src/hb-ot-tag.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 15be0deda03f68c0260d07bdc67c8952aa6ccfa7
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 17 14:57:08 2022 -0600
+
+    [ot-tags] Optimize lang_matches()
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3591
+    
+    Before:
+    ------------------------------------------------------------------------------------------------
+    Benchmark                                                      Time             CPU   Iterations
+    ------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY       8.67 ns         8.64 ns     80324382
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN         91.2 ns         90.9 ns      7674131
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US         41.1 ns         41.0 ns     17174093
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US          41.3 ns         41.2 ns     17000876
+    BM_hb_ot_tags_from_script_and_language/COMMON none          4.56 ns         4.55 ns    153914130
+    BM_hb_ot_tags_from_script_and_language/LATIN none           4.53 ns         4.52 ns    153830303
+    
+    After:
+    ------------------------------------------------------------------------------------------------
+    Benchmark                                                      Time             CPU   Iterations
+    ------------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON abcd_XY       8.24 ns         8.21 ns     84078465
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN         77.5 ns         77.2 ns      9059230
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US         38.8 ns         38.7 ns     17790692
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US          37.6 ns         37.5 ns     18648293
+    BM_hb_ot_tags_from_script_and_language/COMMON none          4.50 ns         4.49 ns    155573267
+    BM_hb_ot_tags_from_script_and_language/LATIN none           4.49 ns         4.47 ns    156456653
+
+ src/gen-tag-table.py   |   2 +-
+ src/hb-ot-tag-table.hh | 126 ++++++++++++++++++++++++-------------------------
+ src/hb-ot-tag.cc       |  11 +++--
+ 3 files changed, 70 insertions(+), 69 deletions(-)
+
+commit 407a135baf8ccd53cf1bc3502f3216f3dbcf3328
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 17 14:45:45 2022 -0600
+
+    [perf/benchmark-ot] Add one more test
+
+ perf/benchmark-ot.cc | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit dd3c858f84105021cf1e427399b971bf26dde8b3
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 17 14:28:28 2022 -0600
+
+    [ot-tags] Speed up hb_ot_tags_from_language()
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3591
+    
+    "After that, bulk of the time I suppose is spent in binary-searching the
+    language table. I suggest we split the language table in 2-letter and
+    3-letter tags, to speed-up the vast majority of cases that are
+    2-letter."
+    
+    benchmark-ot, before:
+    
+    ----------------------------------------------------------------------------------------------
+    Benchmark                                                    Time             CPU   Iterations
+    ----------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN        112 ns          111 ns      6286271
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US       60.6 ns         60.4 ns     11671176
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US        61.3 ns         61.1 ns     11442645
+    BM_hb_ot_tags_from_script_and_language/COMMON none        4.75 ns         4.74 ns    146997235
+    BM_hb_ot_tags_from_script_and_language/LATIN none         4.65 ns         4.64 ns    150938747
+    
+    After:
+    
+    ----------------------------------------------------------------------------------------------
+    Benchmark                                                    Time             CPU   Iterations
+    ----------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN       89.5 ns         89.2 ns      7747649
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US       38.5 ns         38.4 ns     18199432
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US        39.0 ns         38.9 ns     18049238
+    BM_hb_ot_tags_from_script_and_language/COMMON none        4.53 ns         4.52 ns    154895110
+    BM_hb_ot_tags_from_script_and_language/LATIN none         4.54 ns         4.52 ns    154762105
+
+ src/gen-tag-table.py   |  55 +++----
+ src/hb-ot-tag-table.hh | 409 +++++++++++++++++++++++++------------------------
+ src/hb-ot-tag.cc       |  45 ++++--
+ 3 files changed, 270 insertions(+), 239 deletions(-)
+
+commit 9baccb986087319ae56e77624082036063d67d90
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 17 13:34:34 2022 -0600
+
+    [ot-tags] Speed up hb_ot_tags_from_complex_language()
+    
+    Part of https://github.com/harfbuzz/harfbuzz/issues/3591
+    
+    2. All the subtag_matches outside the switch match long strings (>= 6 or so).
+       As such, check the tag for such length before going into any of them.
+    
+    benchmark-ot, before:
+    
+    ----------------------------------------------------------------------------------------------
+    Benchmark                                                    Time             CPU   Iterations
+    ----------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN        172 ns          171 ns      4083155
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US        120 ns          119 ns      5849947
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US         113 ns          112 ns      5840326
+    BM_hb_ot_tags_from_script_and_language/COMMON none        4.66 ns         4.64 ns    151396224
+    BM_hb_ot_tags_from_script_and_language/LATIN none         4.66 ns         4.64 ns    149019593
+    
+    After:
+    
+    ----------------------------------------------------------------------------------------------
+    Benchmark                                                    Time             CPU   Iterations
+    ----------------------------------------------------------------------------------------------
+    BM_hb_ot_tags_from_script_and_language/COMMON zh_CN        112 ns          112 ns      6357763
+    BM_hb_ot_tags_from_script_and_language/COMMON en_US       60.5 ns         60.3 ns     11475091
+    BM_hb_ot_tags_from_script_and_language/LATIN en_US        54.9 ns         54.8 ns     12575690
+    BM_hb_ot_tags_from_script_and_language/COMMON none        4.61 ns         4.59 ns    152388450
+    BM_hb_ot_tags_from_script_and_language/LATIN none         4.66 ns         4.64 ns    151497600
+
+ src/gen-tag-table.py   |  41 +++++++++++-----
+ src/hb-ot-tag-table.hh | 126 +++++++++++++++++++++++++------------------------
+ 2 files changed, 95 insertions(+), 72 deletions(-)
+
+commit 26d906b88b324ea953f42acf1b82086cc4ad3642
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 17 13:12:17 2022 -0600
+
+    [perf] Add benchmark-ot
+
+ perf/Makefile.am     |  1 +
+ perf/benchmark-ot.cc | 35 +++++++++++++++++++++++++++++++++++
+ perf/meson.build     | 10 ++++++++++
+ 3 files changed, 46 insertions(+)
+
+commit 629fa8ee87a419c3a2f6b1477d7ecd81571f0d7e
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 16 17:44:50 2022 -0600
+
+    [perf/benchmark-font] Test Roboto as variable even though it's not
+
+ perf/benchmark-font.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 71a0cda869f55c00727fdbbf079b671f7fe374ff
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 16 17:43:48 2022 -0600
+
+    [perf/benchmark-font] Only certain fonts are variable
+    
+    Don't test every font as variable.
+
+ perf/benchmark-font.cc | 17 +++++++++--------
+ 1 file changed, 9 insertions(+), 8 deletions(-)
+
+commit fb413f52022aa2edb8a5b64e9d28f826a161d0aa
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 16 17:08:43 2022 -0600
+
+    [subset/cff] Don't use bitfields for hot bools
+    
+    The struct has room because of alignment, and these bools are hot.
+
+ src/hb-subset-cff-common.hh | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+commit a4d98b63ea59f17ef5e4795f6048f9cd6baa4340
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 16 17:02:40 2022 -0600
+
+    [subset/cff1] Collect glyph-to-sid map to avoid an O(n^2) algorithm
+    
+    Saves 13 for largest benchmark:
+    
+    BM_subset/subset_glyphs/SourceHanSans-Regular_subset.otf/10000                    -0.1313         -0.1308            75            65            75            65
+    
+    BM_subset/subset_codepoints/SourceHanSans-Regular_subset.otf/4096                 -0.1009         -0.1004            54            48            54            48
+    BM_subset/subset_codepoints/SourceHanSans-Regular_subset.otf/10000                -0.1067         -0.1066            70            62            69            62
+
+ src/hb-ot-cff1-table.hh | 47 ++++++++++++++++++++++++++++++++++++++++++++++-
+ src/hb-subset-cff1.cc   |  8 +++++++-
+ 2 files changed, 53 insertions(+), 2 deletions(-)
+
+commit b87f48e948077297b823ac929e950d4188ec627d
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 16 16:33:31 2022 -0600
+
+    [cff1] get_sid() move bounds check into each implementation
+
+ src/hb-ot-cff1-table.hh | 13 +++++++------
+ 1 file changed, 7 insertions(+), 6 deletions(-)
+
+commit e1e359b4daac86cea0a4f02f7f175e93ea9440d7
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 16 15:53:28 2022 -0600
+
+    [cff1] Tighten up range_list_t a bit
+
+ src/hb-subset-cff1.cc | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+commit 3fbac0942da80457f8c226105f5a4a1bdfe502f5
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 16 15:41:11 2022 -0600
+
+    [cff1] Lazy-load & sort glyph names
+    
+    Improves subset benchmarks by up to 70% for small CFF1 subset of
+    non-CID fonts!
+    
+    BM_subset/subset_glyphs/SourceSansPro-Regular.otf/10                              -0.7067         -0.7071             1             0             1             0
+    BM_subset/subset_glyphs/SourceSansPro-Regular.otf/64                              -0.4817         -0.4824             1             0             1             0
+    BM_subset/subset_glyphs/SourceSansPro-Regular.otf/512                             -0.1948         -0.1956             2             2             2             2
+    BM_subset/subset_glyphs/SourceSansPro-Regular.otf/2000                            -0.0767         -0.0761             6             6             6             6
+
+ src/hb-ot-cff1-table.hh | 74 +++++++++++++++++++++++++++++++++++--------------
+ 1 file changed, 53 insertions(+), 21 deletions(-)
+
+commit b58bfd9818243fc1178ecad0731fa24a5aa3f235
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 16 11:21:45 2022 -0600
+
+    [font] Minor move of code to silence gcc-12 warning
+    
+    See mailing list discussion.
+
+ src/hb-font.cc | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+commit 602e0ca79d1a651fee0cd23d2fa3580621006c87
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 16 10:14:34 2022 -0600
+
+    [cff] Minor restructure of struct
+    
+    Surprisingly this shows tiny benchmark improvement consistently.
+
+ src/hb-cff-interp-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit acdab17ed3507bc9524cb57bef703a983e1031cf
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri May 13 14:14:36 2022 -0600
+
+    [cff] Cosmetic in parsed_values_t
+
+ src/hb-cff-interp-common.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit b46c7faa9c77e288d16869b9ac609524e0f89468
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri May 13 14:02:54 2022 -0600
+
+    [cff] Check buf_len, not buf
+    
+    Ouch!
+
+ src/hb-ot-cff1-table.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 19a8db85458f02f5be268747b85a2c4fab1319b9
+Author: Garret Rieger <grieger at google.com>
+Date:   Fri May 13 18:05:05 2022 +0000
+
+    [subset] fix potential integer overflow in gname_t::cmp.
+
+ src/hb-ot-cff-common.hh | 3 ++-
+ src/hb-ot-cff1-table.hh | 2 +-
+ 2 files changed, 3 insertions(+), 2 deletions(-)
+
+commit 2d2f66e1a300e14aac16120f2dc193717502129e
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri May 13 13:53:17 2022 -0600
+
+    [cff-common] In INDEX, return empty bytes if length is zero
+    
+    Before it was possible to return non-null arrayZ.
+
+ src/hb-ot-cff-common.hh | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+commit a2f132f1fc5ed1c8426dea1b1e27aa1eaf8eeb04
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri May 13 13:49:17 2022 -0600
+
+    [cff] Check glyph-name's length, not arrayZ
+    
+    As the latter can be non-null while still zero-length.
+
+ src/hb-ot-cff1-table.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit dc09053f1924a486058a8737fda22567e6d95213
+Author: jeremiazhao <jeremiazhao at tencent.com>
+Date:   Fri May 13 17:54:11 2022 +0800
+
+    fix build requirements for fedora/centos in buiding document
+
+ BUILD.md | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit c657c4e1f8e6f23828fefbc441b01f7bee685c79
+Author: Thomas Devoogdt <thomas.devoogdt at barco.com>
+Date:   Tue May 10 10:00:06 2022 +0200
+
+    [meta] fix type traits on gcc 4.9 #3526
+    
+    Signed-off-by: Thomas Devoogdt <thomas.devoogdt at barco.com>
+
+ src/hb-meta.hh      | 11 +++++++++++
+ src/hb-open-type.hh | 11 ++++++-----
+ src/hb-vector.hh    | 17 +++++++++--------
+ 3 files changed, 26 insertions(+), 13 deletions(-)
+
+commit e4e053c8b3a72295c7f414726085aaa01c137c6f
+Author: Garret Rieger <grieger at google.com>
+Date:   Fri May 13 17:00:57 2022 +0000
+
+    [perf] fix typo in perf Makefile.
+
+ perf/Makefile.am | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit e61234c5f75e21901a81df08945daddca5cbfde3
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 12 13:20:10 2022 -0600
+
+    [vector] Add tests for move constructor/assignment
+
+ src/test-vector.cc | 17 +++++++++++++----
+ 1 file changed, 13 insertions(+), 4 deletions(-)
+
+commit 7fa580bc4f83f5440b23531f53b546d52af0f69b
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 12 13:05:32 2022 -0600
+
+    [map] Fix map copy/move constructors to actually work
+    
+    Ouch!
+
+ src/hb-map.hh   |  5 +++--
+ src/hb-set.hh   |  3 +--
+ src/test-map.cc | 22 ++++++++++++++++++----
+ src/test-set.cc |  4 +++-
+ 4 files changed, 25 insertions(+), 9 deletions(-)
+
+commit a09dd87ca373c1629c05803e3b8611274cb15a6c
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 12 12:58:07 2022 -0600
+
+    [set] Fix set copy/move constructors to actually work
+    
+    Ouch!
+
+ src/hb-set.hh   | 16 ++++++++++------
+ src/test-set.cc | 19 ++++++++++++++-----
+ 2 files changed, 24 insertions(+), 11 deletions(-)
+
+commit 76fc27713f52cc338f0325650c2c7798f5cfa2ce
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 12 12:14:07 2022 -0600
+
+    [vector] Remove explicit std::move
+    
+    Was confusing compilers. Let them figure it out themselves.
+    
+    Makes NotoNastaliqu subsetting/1000 benchmark more than twice faster:
+    
+    Benchmark                                                                       Time             CPU      Time Old      Time New       CPU Old       CPU New
+    ------------------------------------------------------------------------------------------------------------------------------------------------------------
+    BM_subset/subset_glyphs/NotoNastaliqUrdu-Regular.ttf/1000                    -0.5064         -0.5065           111            55           110            55
+    BM_subset/subset_codepoints/NotoNastaliqUrdu-Regular.ttf/1000                -0.5494         -0.5493           132            59           131            59
+
+ src/hb-vector.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit c81198b5bc7d5d0990752b36ad2b1fcdec963abf
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 12 11:58:37 2022 -0600
+
+    [set] Tweak move operators a bit
+    
+    Should be equivalent.
+
+ src/hb-bit-set-invertible.hh | 6 +++---
+ src/hb-set.hh                | 4 ++--
+ 2 files changed, 5 insertions(+), 5 deletions(-)
+
+commit 8dc072d20d87d2986cd58797bc1993c372e5d4d6
+Merge: bff78e651 175319cd8
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 11 16:45:40 2022 -0600
+
+    Merge pull request #3579 from harfbuzz/subset-retain-buffer
+    
+    Subset retain buffer
+
+commit 175319cd89fbab431616eb83d4d7c569fe4e8800
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 11 13:47:17 2022 -0600
+
+    [gsubgpos] Clean up OT::ClassDefFormat2::intersected_class_glyphs 0 case
+
+ src/hb-ot-layout-common.hh | 18 ++++++++++--------
+ 1 file changed, 10 insertions(+), 8 deletions(-)
+
+commit 137af3612bcf038103bfc315f445d6574cba8d2c
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 11 13:39:30 2022 -0600
+
+    [gsubgpos] Simplify OT::ClassDefFormat2::intersected_class_glyphs()
+
+ src/hb-ot-layout-common.hh | 39 +++++++++++++++++++--------------------
+ 1 file changed, 19 insertions(+), 20 deletions(-)
+
+commit 3261e05bdbb067cb9411a38a585bb04be1fb2542
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 11 13:16:31 2022 -0600
+
+    [subset] Optimize ClassDef1::intersected_class_glyphs() for class0
+
+ src/hb-ot-layout-common.hh | 11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+commit c78d8ba60b49013e3ca98a2d7b030dc5d8c569d8
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 11 13:05:41 2022 -0600
+
+    [subset] Allocate same size as source table for GSUB/GPOS/name
+
+ src/hb-subset.cc | 16 +++++++++++-----
+ 1 file changed, 11 insertions(+), 5 deletions(-)
+
+commit 2e7f1ae48feaa2db8248b7ae01e46ef70e461a31
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 11 12:49:16 2022 -0600
+
+    [subset] Use vector.allocated size instead of tracking buf_size
+
+ src/hb-subset.cc | 10 +++++-----
+ src/hb-vector.hh |  3 +--
+ 2 files changed, 6 insertions(+), 7 deletions(-)
+
+commit f08537963b5157cd9e7a02f6e1695ff6bd27cc1b
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 11 12:10:03 2022 -0600
+
+    [cff-subset] Pre-alloc vector for operator decoding
+
+ src/hb-cff-interp-common.hh | 5 +++++
+ src/hb-subset-cff-common.hh | 1 +
+ 2 files changed, 6 insertions(+)
+
+commit 7edd54f3ddadc10307577575f47e943b86198e9d
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 10 18:44:14 2022 -0600
+
+    [perf/benchmark-subset] Minor cleanup
+
+ perf/benchmark-subset.cc | 32 +++++++++++++++++++-------------
+ 1 file changed, 19 insertions(+), 13 deletions(-)
+
+commit aeb50b8942b92cda2b1d5bb03d685f97f79faf5d
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 10 18:06:53 2022 -0600
+
+    [subset] Retain buffer across table subset operations
+
+ src/hb-subset.cc | 61 +++++++++++++++++++++++++++++---------------------------
+ 1 file changed, 32 insertions(+), 29 deletions(-)
+
+commit bff78e651555e6376d2a4b49c323cf5e9fe3a25c
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 10 16:33:37 2022 -0600
+
+    [cff] Convert interpretation environment to use constructor
+
+ src/hb-cff-interp-common.hh      | 29 ++++++++---------------------
+ src/hb-cff-interp-cs-common.hh   | 13 +++++--------
+ src/hb-cff-interp-dict-common.hh |  2 ++
+ src/hb-cff1-interp-cs.hh         |  8 +++-----
+ src/hb-cff2-interp-cs.hh         |  9 ++++-----
+ src/hb-ot-cff1-table.cc          | 16 ++++++++--------
+ src/hb-ot-cff1-table.hh          | 19 ++++++++++---------
+ src/hb-ot-cff2-table.cc          |  8 ++++----
+ src/hb-ot-cff2-table.hh          | 24 ++++++++++--------------
+ src/hb-subset-cff-common.hh      |  8 ++++----
+ 10 files changed, 58 insertions(+), 78 deletions(-)
+
+commit de053e2efbcf0166590868c993bfbe7cc3453a06
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 10 15:38:37 2022 -0600
+
+    [cff] Convert subr_subset_param_t to use constructor
+
+ src/hb-subset-cff-common.hh | 59 ++++++++++++++++++++++++---------------------
+ 1 file changed, 31 insertions(+), 28 deletions(-)
+
+commit 96140db485b61995b0fe9528b6323a5ea928e5a8
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 10 15:34:33 2022 -0600
+
+    [cff] Convert cff2_extents_param_t to use constructor
+
+ src/hb-ot-cff2-table.cc | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+commit 54544f2a57373b2d74bda55d4a48f58a0121c22c
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 10 15:31:49 2022 -0600
+
+    [cff] Convert cff1_extents_param_t to use constructor
+
+ src/hb-ot-cff1-table.cc | 9 +++------
+ 1 file changed, 3 insertions(+), 6 deletions(-)
+
+commit 377befd0c72071190029112ee04ab0a06fea60b6
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 10 15:29:12 2022 -0600
+
+    [cff] Convert get_seac_param_t to use constructor
+
+ src/hb-ot-cff1-table.cc | 14 ++++----------
+ 1 file changed, 4 insertions(+), 10 deletions(-)
+
+commit 8fd70362fa4c0f411fc67b15b67b69a7c43431e3
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 10 15:15:49 2022 -0600
+
+    [cff] Use hb_ubytes_t() instead of Null(hb_ubytes_t)
+
+ src/hb-cff-interp-cs-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 9033c7f99d5ffe80c349a2ed5e4ef68ca4bed434
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 10 14:58:53 2022 -0600
+
+    [cff-common] Optimize INDEX::operator[]
+    
+    Previous try showed slowdown in benchmarks, suprisingly.
+    
+    Rewrite it keeping the function, hopefully allowing better optimization.
+
+ src/hb-ot-cff-common.hh | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+commit 3aace2431b9bd503cb706760d831ae313d059107
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 10 14:54:04 2022 -0600
+
+    Revert "[cff-common] Optimize INDEX::operator[]"
+    
+    This reverts commit 9edb03ac7ac4b4d0814f3fd1f20cc8d2be99e971.
+
+ src/hb-ot-cff-common.hh | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+commit b31ef081db0d91fd6d3e72a59fc97248ab28a904
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 10 14:52:40 2022 -0600
+
+    Revert "[cff] Add an unlikely()"
+    
+    This reverts commit 9ba9adb7ed6d48504e97a2af117b7da1fdb28450.
+    
+    This shows slowdown in benchmarks.
+
+ src/hb-cff-interp-cs-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 9ba9adb7ed6d48504e97a2af117b7da1fdb28450
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 10 14:42:50 2022 -0600
+
+    [cff] Add an unlikely()
+
+ src/hb-cff-interp-cs-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 9edb03ac7ac4b4d0814f3fd1f20cc8d2be99e971
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 10 14:25:08 2022 -0600
+
+    [cff-common] Optimize INDEX::operator[]
+
+ src/hb-ot-cff-common.hh | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+commit 52d59bf150b2a6312fe4c3b6f2ec882febe814d9
+Author: Garret Rieger <grieger at google.com>
+Date:   Tue May 10 19:40:37 2022 +0000
+
+    [perf] Make subset benchmark data driven.
+
+ perf/benchmark-subset.cc                           | 153 +++++++--------------
+ .../data}/fonts/NotoSansDevanagari-Regular.ttf     | Bin
+ 2 files changed, 52 insertions(+), 101 deletions(-)
+
+commit 0a42410dc8a8457f49b94a0b533f0b83191ce8d5
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Tue May 10 12:05:19 2022 -0600
+
+    [cff2] Change extents/shape stack to be just a number
+    
+    Do the blending immediately.
+    
+    Fixes https://github.com/harfbuzz/harfbuzz/issues/3559
+    
+    Benchmark on AdobeVFPrototype shows 35% speedup. Now we're faster
+    than FreeType:
+    
+    Benchmark                                                           Time             CPU      Time Old      Time New       CPU Old       CPU New
+    ------------------------------------------------------------------------------------------------------------------------------------------------
+    BM_Font/glyph_extents/AdobeVFPrototype.otf/hb                    -0.3792         -0.3792          1584           983          1581           982
+    BM_Font/glyph_extents/AdobeVFPrototype.otf/ft                    +0.0228         +0.0224          1220          1248          1218          1245
+    BM_Font/glyph_extents/AdobeVFPrototype.otf/var/hb                -0.3513         -0.3518          1616          1048          1613          1046
+    BM_Font/glyph_extents/AdobeVFPrototype.otf/var/ft                +0.0172         +0.0169          1232          1254          1230          1251
+
+ src/hb-cff-interp-common.hh |  4 +--
+ src/hb-cff2-interp-cs.hh    | 82 +++++++++++++++++++++++++++------------------
+ src/hb-ot-cff2-table.cc     | 24 ++++++-------
+ src/hb-subset-cff2.cc       | 28 ++++++++--------
+ 4 files changed, 77 insertions(+), 61 deletions(-)
+
+commit 5277a5772b0b9ebbbcdec0eae7f1b13d41a8d170
+Author: Garret Rieger <grieger at google.com>
+Date:   Tue May 10 18:14:25 2022 +0000
+
+    [perf] Add benchmarks for CFF subsetting.
+
+ perf/benchmark-subset.cc | 27 +++++++++++++++++++++++++++
+ 1 file changed, 27 insertions(+)
+
+commit 8f9f0c494b9ea516903e8142e8aba391ddcb581c
+Author: Garret Rieger <grieger at google.com>
+Date:   Tue May 10 17:47:08 2022 +0000
+
+    [subset] Enforce cmap12 group ordering constraints in collect_mapping.
+    
+    Fixes fuzzer issue: https://oss-fuzz.com/testcase-detail/6365271012540416
+
+ src/hb-ot-cmap-table.hh                                |   8 ++++++++
+ ...estcase-minimized-hb-subset-fuzzer-6365271012540416 | Bin 0 -> 161424 bytes
+ 2 files changed, 8 insertions(+)
+
+commit c99ad0f015d1328cbb9803777f66ca491b2cb115
+Merge: c941ece60 1b14d2ff1
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 18:52:19 2022 -0600
+
+    Merge pull request #3572 from harfbuzz/cff-stack
+    
+    Cff stack
+
+commit 1b14d2ff136a9f7522995393fda6f6644377657f
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 18:15:31 2022 -0600
+
+    [cff] Fix arg-stack peek() impl
+
+ src/hb-cff-interp-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 6106ef8c0f61453c38c58f71a045481bf5546f2d
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 18:12:09 2022 -0600
+
+    [cff] Tighten up arg-stack access
+
+ src/hb-cff-interp-common.hh | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+commit 8c616a6efe7370e110d6a2f822bb1a38bf768ea6
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 17:49:54 2022 -0600
+
+    [cff] Allocate stack inline instead of using hb_vector_t
+    
+    Speeds up glyph_extents and glyph_shape benchmarks for CFF by 10
+    to 16 percent!
+
+ src/hb-cff-interp-common.hh | 18 +++++++-----------
+ 1 file changed, 7 insertions(+), 11 deletions(-)
+
+commit c941ece60fe791b58697a0ac9d92cd27682f0698
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 16:20:22 2022 -0600
+
+    [cff] Use using instead of typedef
+
+ src/hb-cff-interp-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 64d63cebe2968bc8d9882991b5402c7d626ecf90
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 16:16:07 2022 -0600
+
+    [cff-common] Use existing types for str_buff_vec_t
+
+ src/hb-ot-cff-common.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit e1838ec1f863758bdd3fa33dce8bf8bfb7fa1518
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 16:14:13 2022 -0600
+
+    [cff-common] Remove unused method
+
+ src/hb-ot-cff-common.hh | 11 +----------
+ 1 file changed, 1 insertion(+), 10 deletions(-)
+
+commit 8aa54aaca250e2934bd2c97047db8b40bf027908
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 16:09:56 2022 -0600
+
+    [cff] Replace byte_str_t with hb_bytes_t use
+
+ src/hb-cff-interp-common.hh    | 25 ++++++++++---------------
+ src/hb-cff-interp-cs-common.hh |  6 +++---
+ src/hb-cff1-interp-cs.hh       |  2 +-
+ src/hb-cff2-interp-cs.hh       |  2 +-
+ src/hb-ot-cff-common.hh        | 14 +++++++-------
+ src/hb-ot-cff1-table.cc        |  6 +++---
+ src/hb-ot-cff1-table.hh        | 12 ++++++------
+ src/hb-ot-cff2-table.cc        |  4 ++--
+ src/hb-ot-cff2-table.hh        |  8 ++++----
+ src/hb-subset-cff-common.hh    |  6 +++---
+ src/hb-subset-cff1.cc          |  2 +-
+ 11 files changed, 41 insertions(+), 46 deletions(-)
+
+commit fe1d85a55a53797f0808d1f473475b7ce15eeb92
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 16:04:52 2022 -0600
+
+    [cff] Remove custom byte_str_t impl
+
+ src/hb-cff-interp-common.hh | 35 ++++++++++-------------------------
+ src/hb-ot-cff1-table.hh     |  4 ++--
+ src/hb-ot-cff2-table.hh     |  4 ++--
+ 3 files changed, 14 insertions(+), 29 deletions(-)
+
+commit c8a5f1e3c0cb8b2c0c546e89134cb66b9af2b53a
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 15:49:47 2022 -0600
+
+    [cff-common] Indent
+
+ src/hb-ot-cff-common.hh | 49 +++++++++++++++++++++++++------------------------
+ 1 file changed, 25 insertions(+), 24 deletions(-)
+
+commit be7b2905cb118a5d4d08f42e870fe5f5f5ee9b0e
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 15:48:18 2022 -0600
+
+    [cff-common] Remove unused INDEX::serialize() method
+
+ src/hb-ot-cff-common.hh | 10 ----------
+ 1 file changed, 10 deletions(-)
+
+commit 60390169b65632406391f3492efdbd66c688555f
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 15:44:09 2022 -0600
+
+    [cff-common] Write str_buf_t::total_size() as dagger
+
+ src/hb-ot-cff-common.hh | 11 ++++-------
+ 1 file changed, 4 insertions(+), 7 deletions(-)
+
+commit 258afb45b7fefa42e36f74731d56862b9367f91e
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 15:40:55 2022 -0600
+
+    [cff-common] Use range-based loop in str_buff_vec_t
+
+ src/hb-ot-cff-common.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 8bb1a3ce9ae8a24c168a51c6faf16779561138ae
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 15:38:40 2022 -0600
+
+    [cff-common] Write INDEX offset-size calc using hb_bit_storage()
+
+ src/hb-ot-cff-common.hh | 15 +--------------
+ 1 file changed, 1 insertion(+), 14 deletions(-)
+
+commit 2ccfe84eff7f72159c87012d7e10e9c8ecdbc956
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 15:35:04 2022 -0600
+
+    [cff-common] Add assert to INDEX::set_offset_at()
+
+ src/hb-ot-cff-common.hh | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit 4bcab9e99a7fb7456f5788e2da6fae8fc5b14584
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 15:30:42 2022 -0600
+
+    [cff-common] Use byte_str_t() instead of Null(byte_str_t)
+
+ src/hb-ot-cff-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 94f7a263228a120754ca31600cabb15de0652501
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 15:29:14 2022 -0600
+
+    [cff-common] Fix get_size() for Null object
+    
+    The special-casing didn't make sense.
+
+ src/hb-ot-cff-common.hh | 1 -
+ 1 file changed, 1 deletion(-)
+
+commit c9cc7d5d21dc2550e820de841f5d24f5c94dcc7e
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 15:27:27 2022 -0600
+
+    [cff-common] Inline once-used method in INDEX
+
+ src/hb-ot-cff-common.hh | 5 +----
+ 1 file changed, 1 insertion(+), 4 deletions(-)
+
+commit 11482a3a3927eff8e408f825082f61a202c9be9b
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 15:25:21 2022 -0600
+
+    [cff-common] Remove unused method from INDEX
+
+ src/hb-ot-cff-common.hh | 2 --
+ 1 file changed, 2 deletions(-)
+
+commit d1bb3b08f65965bfc07b11becc3e344554c398cc
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 15:23:59 2022 -0600
+
+    [cff-common] Hide more INDEX internals
+
+ src/hb-ot-cff-common.hh | 2 ++
+ 1 file changed, 2 insertions(+)
+
+commit d3b21387fde2923a624903915a58c9745d2602af
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 15:22:55 2022 -0600
+
+    [cff-common] Remove redundant operator implementation
+
+ src/hb-ot-cff-common.hh | 7 -------
+ 1 file changed, 7 deletions(-)
+
+commit a96b408d805c53c051764b66a7e19aa902c82546
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 9 15:20:16 2022 -0600
+
+    [cff-common] Hide INDEX internals
+
+ src/hb-ot-cff-common.hh | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit 335b1d83cf61d1d712e9343a2217594f37018880
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri May 6 13:37:11 2022 -0600
+
+    [cff-common] No need to check max-offset in INDEX
+    
+    The length_at() function makes sure out-of-range offsets
+    are discarded. We just need to check the last offset.
+
+ src/hb-ot-cff-common.hh | 19 ++++---------------
+ 1 file changed, 4 insertions(+), 15 deletions(-)
+
+commit b051f3fa8388d25c7023a7f48dfea415bde1c94c
+Author: Garret Rieger <grieger at google.com>
+Date:   Thu May 5 23:27:34 2022 +0000
+
+    [subset] Fix cpal subsetting when there are partial palette overlaps.
+    
+    The existing code doesn't correctly handle the case where palettes partially overlap in the color record array. This changes the subsetting to only share entries in the color record array when palettes have the same first color index. Partially overlapping palettes will be converted to disjoint segments in the color record array.
+    
+    Updates one of the color tests to use multiple palettes.
+    
+    Also fixes fuzzer: https://oss-fuzz.com/testcase-detail/5568200165687296.
+
+ src/hb-ot-color-cpal-table.hh                      |  60 +++++++++++++--------
+ ...ase-minimized-hb-subset-fuzzer-5568200165687296 | Bin 0 -> 220551 bytes
+ .../colr_with_components/colr-table.default.6B.ttf | Bin 4260 -> 4320 bytes
+ .../colr-table.drop-hints-retain-gids.6B.ttf       | Bin 4984 -> 5044 bytes
+ .../colr-table.drop-hints.6B.ttf                   | Bin 4260 -> 4320 bytes
+ .../colr-table.retain-gids.6B.ttf                  | Bin 4984 -> 5044 bytes
+ test/subset/data/fonts/colr-table.ttf              | Bin 26952 -> 27328 bytes
+ 7 files changed, 37 insertions(+), 23 deletions(-)
+
+commit 2884eb97bf448448c8c06f51e1a60acbff33bcbf
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri May 6 12:54:02 2022 -0600
+
+    [cff-common] Remove special-casing of count=0 in INDEX serialize
+    
+    The generic code-path now can handle count=0.
+
+ src/hb-ot-cff-common.hh | 15 +++------------
+ 1 file changed, 3 insertions(+), 12 deletions(-)
+
+commit fc7f51aecea6b7a66772d4f759f52447f34197f1
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri May 6 12:53:19 2022 -0600
+
+    [cff-common] Reduce iterator calls
+
+ src/hb-ot-cff-common.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit c857b8e3c642476aedea634c294ee101d6ce39f3
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri May 6 12:50:37 2022 -0600
+
+    [cff-common] Set INDEX min_size to 2
+    
+    That is what it is, for an empty INDEX.
+
+ src/hb-ot-cff-common.hh | 21 ++++++++++++---------
+ 1 file changed, 12 insertions(+), 9 deletions(-)
+
+commit dd71d2c1c30ca85ddd7b1d7e3a9e2bbdacd6ae7a
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri May 6 13:02:26 2022 -0600
+
+    [gvar] Protect against offset underflow
+
+ src/hb-ot-var-gvar-table.hh | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+commit 9a6dabd61a1af848abbab21b0152e58875604a37
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri May 6 12:01:37 2022 -0600
+
+    [gvar] Remove sanitize check for data array
+    
+    We are not checking in sanitize that offset array is ascending,
+    so this check was bogus.
+
+ src/hb-ot-var-gvar-table.hh | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+commit 38478d1061d4971c6f10910db1b8988aab900bcf
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri May 6 12:00:01 2022 -0600
+
+    [gvar] DEFINE_SIZE_ARRAY instead of DEFINE_SIZE_MIN
+
+ src/hb-ot-var-gvar-table.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 90d278c92e2ef076d2b239fed56a9dc11f4b6c12
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri May 6 11:58:53 2022 -0600
+
+    [gvar] Remove requirement that num_glyphs matches the font's
+
+ src/hb-ot-var-gvar-table.hh | 1 -
+ 1 file changed, 1 deletion(-)
+
+commit ca8a0f3ea32af8fdaf2f99ad87a43e82be854f62
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri May 6 11:54:38 2022 -0600
+
+    [gvar] Protect against out-of-range access
+    
+    Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=47281
+    Fixes https://oss-fuzz.com/testcase-detail/5508865908670464
+
+ src/hb-ot-var-gvar-table.hh                             |   5 ++++-
+ ...usterfuzz-testcase-hb-subset-fuzzer-5508865908670464 | Bin 0 -> 17004 bytes
+ 2 files changed, 4 insertions(+), 1 deletion(-)
+
+commit f10ddb8dd870fd691c8876c1c7151e607aab0625
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 5 11:21:24 2022 -0600
+
+    [cmap] Use -1 as Unicode sentinel, not U+FFFF in Format12 serialize
+
+ src/hb-ot-cmap-table.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 8a19968c8b8f8118e6247489a65edfb707bc838e
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 5 11:17:23 2022 -0600
+
+    [cmap] Use iterator bool operator
+
+ src/hb-ot-cmap-table.hh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+commit 8bfeea482838a0c4f678c7f666f4520f4f2e8dd9
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 5 10:48:24 2022 -0600
+
+    [subset] Compute set max using previous()
+
+ src/hb-subset-plan.cc | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+commit 00cb8c629d8f5615d316ac6541d6652dfa2d3145
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 5 10:33:50 2022 -0600
+
+    [subset] Don't go into glyf table if it's empty
+
+ src/hb-ot-glyf-table.hh |  2 ++
+ src/hb-subset-plan.cc   | 17 ++++++++++-------
+ 2 files changed, 12 insertions(+), 7 deletions(-)
+
+commit 4fe69bc41327596af540a2f683062b41a4f37f45
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu May 5 10:19:16 2022 -0600
+
+    [subset] Use del_range in _remove_invalid_gids
+
+ src/hb-subset-plan.cc | 9 ++-------
+ 1 file changed, 2 insertions(+), 7 deletions(-)
+
+commit 2a42edccbe55ede9ed7bbf643b7bec41698078ed
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 4 17:06:18 2022 -0600
+
+    [subset] Cosmetic; use set bulk array population instead of for loop
+
+ src/hb-subset-plan.cc | 9 ++++-----
+ 1 file changed, 4 insertions(+), 5 deletions(-)
+
+commit bc5129d7fa6fae7ce4c653b699944dd9416eca68
+Author: Garret Rieger <grieger at google.com>
+Date:   Wed May 4 22:16:03 2022 +0000
+
+    [perf] use option_t in subset benchmark to select between glyphs and codepoint subset.
+
+ perf/benchmark-subset.cc | 134 ++++++++++++++++++++++-------------------------
+ 1 file changed, 62 insertions(+), 72 deletions(-)
+
+commit 43938ecdc2b5cda45f9499f8c3360a0a3ac0842b
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 4 16:59:28 2022 -0600
+
+    [subset] Remove outdated comment
+    
+    I tried something like that. It was slower because of the allocations.
+
+ src/hb-subset-plan.cc | 3 ---
+ 1 file changed, 3 deletions(-)
+
+commit 6212856ce80d1cbdb5ebbd6d8f899e2b1e45d611
+Author: Garret Rieger <grieger at google.com>
+Date:   Wed May 4 22:16:03 2022 +0000
+
+    [perf] benchmark subsetting via glyphs.
+
+ perf/benchmark-subset.cc | 78 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 78 insertions(+)
+
+commit 6829dd30ad9058170674760f3795fcafe3ed6f27
+Merge: 052812b6b 50db78ba8
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 4 16:49:45 2022 -0600
+
+    Merge pull request #3562 from harfbuzz/subset-cmap-no-qsort
+    
+    [subset] In cmap planning, remove a qsort()
+
+commit 50db78ba834b35b96a808c07e550a50b3e1fa5ec
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 4 15:48:18 2022 -0600
+
+    [subset] In cmap planning, remove a qsort()
+
+ src/hb-subset-plan.cc | 30 ++++++++++--------------------
+ 1 file changed, 10 insertions(+), 20 deletions(-)
+
+commit 052812b6ba424b4be677d60a722375f69decb89f
+Merge: f67e6bf79 7cb36e422
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed May 4 15:38:30 2022 -0600
+
+    Merge pull request #3561 from googlefonts/cmap_opt
+    
+    [subset] Further cmap subsetting speed optimizations
+
+commit 7cb36e422218305329102849c156ab94db91cbef
+Author: Garret Rieger <grieger at google.com>
+Date:   Wed May 4 21:22:26 2022 +0000
+
+    [subset] Re-introduce size threshold in choosing unicode collection method.
+    
+    Threshold is needed since the unicodes set might be an inverted set.
+
+ src/hb-subset-plan.cc | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+commit 42c54eba839f510c885fe1a63732b0f706af1bff
+Author: Garret Rieger <grieger at google.com>
+Date:   Wed May 4 20:21:43 2022 +0000
+
+    [subset] Presize unicode to gid list to unicodes + glyphs size.
+
+ src/hb-subset-plan.cc | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+commit 7c7c01d28cee221f1c64684539c8e6160f144f61
+Author: Garret Rieger <grieger at google.com>
+Date:   Tue May 3 22:40:56 2022 +0000
+
+    [subset] Remove switch to alternate unicode collection at large subset sizes.
+    
+    Benchmarks show that the first path is always faster even at large subset sizes:
+    
+    BM_subset_codepoints/subset_roboto/10_median                              +0.0324         +0.0325             0             0             0             0
+    BM_subset_codepoints/subset_roboto/64_median                              +0.0253         +0.0255             0             1             0             1
+    BM_subset_codepoints/subset_roboto/512_median                             +0.0126         +0.0128             1             1             1             1
+    BM_subset_codepoints/subset_roboto/4000_median                            +0.0500         +0.0491             6             7             6             7
+    BM_subset_codepoints/subset_amiri/10_median                               +0.0338         +0.0332             1             1             1             1
+    BM_subset_codepoints/subset_amiri/64_median                               +0.0238         +0.0234             1             1             1             1
+    BM_subset_codepoints/subset_amiri/512_median                              +0.0066         +0.0063             8             8             8             8
+    BM_subset_codepoints/subset_amiri/4000_median                             -0.0011         -0.0012            13            13            13            13
+    BM_subset_codepoints/subset_noto_nastaliq_urdu/10_median                  +0.0226         +0.0226             0             0             0             0
+    BM_subset_codepoints/subset_noto_nastaliq_urdu/64_median                  +0.0047         +0.0044            20            20            20            20
+    BM_subset_codepoints/subset_noto_nastaliq_urdu/512_median                 +0.0022         +0.0021           165           166           165           166
+    BM_subset_codepoints/subset_noto_nastaliq_urdu/1000_median                -0.0021         -0.0023           166           166           166           165
+    BM_subset_codepoints/subset_noto_devangari/10_median                      +0.0054         +0.0054             0             0             0             0
+    BM_subset_codepoints/subset_noto_devangari/64_median                      +0.0024         +0.0019             0             0             0             0
+    BM_subset_codepoints/subset_noto_devangari/512_median                     +0.0089         +0.0090             5             5             5             5
+    BM_subset_codepoints/subset_noto_devangari/1000_median                    -0.0028         -0.0019             5             5             5             5
+    BM_subset_codepoints/subset_mplus1p/10_median                             +0.0001         +0.0002             0             0             0             0
+    BM_subset_codepoints/subset_mplus1p/64_median                             +0.0073         +0.0075             1             1             1             1
+    BM_subset_codepoints/subset_mplus1p/512_median                            +0.0034         +0.0034             1             1             1             1
+    BM_subset_codepoints/subset_mplus1p/4096_median                           -0.1248         -0.1248             7             6             7             6
+    BM_subset_codepoints/subset_mplus1p/10000_median                          -0.0885         -0.0885            13            12            13            12
+    BM_subset_codepoints/subset_notocjk/10_median                             +0.0031         +0.0032             2             2             2             2
+    BM_subset_codepoints/subset_notocjk/64_median                             -0.0010         -0.0010             2             2             2             2
+    BM_subset_codepoints/subset_notocjk/512_median                            -0.0023         -0.0023             9             9             9             9
+    BM_subset_codepoints/subset_notocjk/4096_median                           -0.1725         -0.1726            28            23            28            23
+    BM_subset_codepoints/subset_notocjk/32768_median                          -0.0277         -0.0287           140           137           140           136
+    BM_subset_codepoints/subset_notocjk/100000_median                         -0.0929         -0.0926           162           147           162           147
+
+ src/hb-subset-plan.cc | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+commit f0c04114bc229b3b519ed2242689959ccec64098
+Author: Garret Rieger <grieger at google.com>
+Date:   Tue May 3 22:02:59 2022 +0000
+
+    [subset] Embed unicode to gid list vector in subset plan.
+
+ src/hb-ot-cmap-table.hh |  2 +-
+ src/hb-subset-plan.cc   | 35 ++++++++++++++++-------------------
+ src/hb-subset-plan.hh   |  2 +-
+ 3 files changed, 18 insertions(+), 21 deletions(-)
+
+commit f67e6bf79cd1ac3892a2d6dfe6e479483290bd41
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 2 16:59:48 2022 -0600
+
+    [perf/benchmark-font] Add benchmark for glyph_h_advance
+
+ perf/benchmark-font.cc | 26 +++++++++++++++++++++++---
+ 1 file changed, 23 insertions(+), 3 deletions(-)
+
+commit 1c0a3d4d16b3ff6864c701fc94aa6878ea82a5c4
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 2 16:50:54 2022 -0600
+
+    [perf/benchmark-font] Add a couple Noto fonts
+
+ perf/benchmark-font.cc | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+commit 15fa8afb217582bce4d360c43ad7674861dc1278
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 2 16:46:41 2022 -0600
+
+    Add fast-path for big-endian 32-bit byteswap
+    
+    Speeds up cmap format-12 decoding by some 40% as measured by
+    the newly added test in perf/benchmark-font!
+
+ src/hb-algs.hh | 24 ++++++++++++++++++++----
+ 1 file changed, 20 insertions(+), 4 deletions(-)
+
+commit 3fff2e9182fc6c3cd8ade0336fa67e71967e82c5
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 2 16:31:59 2022 -0600
+
+    [perf/benchmark-font] Cosmetic
+
+ perf/benchmark-font.cc  | 2 +-
+ src/hb-ot-cmap-table.hh | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+commit 307d2d8bb6e74ad974207d3b9f706568a6a87e75
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 2 16:30:22 2022 -0600
+
+    [cmap] Sprinkle some 'unlikely's
+
+ src/hb-ot-cmap-table.hh | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+commit 85ec5cbcefeb2361536031f2e05518c2d817d98a
+Author: Garret Rieger <grieger at google.com>
+Date:   Mon May 2 22:29:43 2022 +0000
+
+    [subset] In _populate_unicodes_to_retain populate unicodes in order.
+    
+    Allows the set insert to take advantage of page lookup cache.
+
+ src/hb-subset-plan.cc | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+commit 0d1f8dcaf3a45dc8ed61dde370df0874af008870
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 2 16:18:53 2022 -0600
+
+    [perf/benchmark-font] Actually make nominal_glyph bench work
+
+ perf/benchmark-font.cc | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+commit 6cf69d10e710cfa7282509c2a43e12618d4673bc
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 2 16:07:32 2022 -0600
+
+    [perf/benchmark-font] Add back testing of is_variable
+
+ perf/benchmark-font.cc | 18 +++++++++++-------
+ 1 file changed, 11 insertions(+), 7 deletions(-)
+
+commit 3aa2ff7988583a7c078032e762cd2bde006fc896
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 2 16:01:22 2022 -0600
+
+    [perf/benchmark-font] Fix build without freetype
+
+ perf/benchmark-font.cc | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+commit 58a0988b576f915a21f4171f71d6d2603d6f3414
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 2 15:57:19 2022 -0600
+
+    [perf/benchmark-font] Benchmark get_nominal_glyph
+
+ perf/benchmark-font.cc | 37 ++++++++++++++++++++++++++++++-------
+ 1 file changed, 30 insertions(+), 7 deletions(-)
+
+commit 03f16fab585e57f184642398172bb2e17aa57635
+Merge: a4522df37 6d29903e8
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 2 15:44:41 2022 -0600
+
+    Merge pull request #3560 from harfbuzz/perf-cleanup
+    
+    Perf cleanup
+
+commit 088133d939d8bc4ce3d97eed7d835c1831e68766
+Author: Garret Rieger <grieger at google.com>
+Date:   Mon May 2 21:29:16 2022 +0000
+
+    [subset] cache cp to new gid list in subset plan.
+    
+    This avoids having to recompute the ordered list multiple times during cmap generation.
+
+ src/hb-ot-cmap-table.hh |  9 +--------
+ src/hb-subset-plan.cc   | 30 ++++++++++++++++++++++++++++++
+ src/hb-subset-plan.hh   |  1 +
+ 3 files changed, 32 insertions(+), 8 deletions(-)
+
+commit 6d29903e86d1f6b0fe7ca884a071d047f0ee130b
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 2 14:03:15 2022 -0600
+
+    [perf/benchmark-font] Parametrize test
+
+ perf/benchmark-font.cc  | 115 +++++++++++++++++++++++++-----------------------
+ perf/benchmark-shape.cc |   2 +
+ 2 files changed, 63 insertions(+), 54 deletions(-)
+
+commit 636c90e81c2eb9a907a1c14d0f3450902d95f65a
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 2 13:41:49 2022 -0600
+
+    [perf/perf] Rename to benchmark-font
+
+ perf/Makefile.am                         |  3 +-
+ perf/{perf-draw.hh => benchmark-font.cc} |  0
+ perf/meson.build                         |  7 ++--
+ perf/perf-extents.hh                     | 65 --------------------------------
+ perf/perf.cc                             |  3 --
+ 5 files changed, 4 insertions(+), 74 deletions(-)
+
+commit 036d03d2e91fc20133150696c405d3281326a552
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 2 13:39:54 2022 -0600
+
+    [perf/perf] Move all logic to perf-draw, for now
+    
+    To be renamed.
+
+ perf/Makefile.am  |   1 -
+ perf/perf-draw.hh | 124 +++++++++++++++++++++++++++++++++++++++++-------------
+ perf/perf.cc      |  10 -----
+ 3 files changed, 94 insertions(+), 41 deletions(-)
+
+commit 746c3c03c5017b4e1404c65a04a5a6122a6cd831
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 2 13:26:41 2022 -0600
+
+    [perf/perf] Remove ttf-parser backend
+
+ perf/meson.build     | 11 +------
+ perf/perf-draw.hh    | 91 +++++++++++-----------------------------------------
+ perf/perf-extents.hh | 50 ++++-------------------------
+ perf/perf.cc         |  2 +-
+ 4 files changed, 28 insertions(+), 126 deletions(-)
+
+commit 4aaa0af7d99f7a44a02542ab8a8d467e3f6a3f64
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Mon May 2 13:06:27 2022 -0600
+
+    [perf/perf] Rely on hb-draw to measure ft performance
+
+ perf/perf-draw.hh | 50 +++++++-------------------------------------------
+ 1 file changed, 7 insertions(+), 43 deletions(-)
+
+commit a4522df378259653f6cdda535980c4acee4d3021
+Merge: 4de5352a3 6922a2561
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri Apr 29 18:34:00 2022 -0600
+
+    Merge pull request #3558 from harfbuzz/set-optimize
+    
+    [perf] hb_set_t optimizations and perf suite improvements
+
+commit 6922a2561f75468c328fa158fef289a0b4156d87
+Author: Garret Rieger <grieger at google.com>
+Date:   Fri Apr 29 23:30:32 2022 +0000
+
+    [subset] Change serialize_rangeoffset_glyid back to using iterator.
+
+ src/hb-ot-cmap-table.hh | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+commit c66fd50c269a7ab8ab22c404354c783ab5419bcc
+Author: Garret Rieger <grieger at google.com>
+Date:   Fri Apr 29 23:18:53 2022 +0000
+
+    [subset] in cmap4 serialization save cp to gid iter to memory.
+    
+    Iterator accesses are slow and it's iterated multiple times.
+
+ src/hb-ot-cmap-table.hh | 21 +++++++++++++--------
+ 1 file changed, 13 insertions(+), 8 deletions(-)
+
+commit 17b98563dc426674d633b79194ce591c8dd38e01
+Author: Garret Rieger <grieger at google.com>
+Date:   Fri Apr 29 22:49:02 2022 +0000
+
+    [subset] In cmap4 serialization reduce unnessecary calls into the iterator.
+    
+    Gives ~20% speedup for large subsets.
+
+ src/hb-ot-cmap-table.hh | 30 +++++++++++++++++-------------
+ 1 file changed, 17 insertions(+), 13 deletions(-)
+
+commit 5e241094bfa72840a4142c33264d128b60f12330
+Author: Garret Rieger <grieger at google.com>
+Date:   Fri Apr 29 22:44:43 2022 +0000
+
+    [subset] In unicodes cache cleanup if set insert fails.
+
+ src/hb-ot-cmap-table.hh | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+commit 217d38dfc7b7b1152b74ceb46472bf6a05d35f1a
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri Apr 29 16:18:17 2022 -0600
+
+    Try to fix distcheck
+
+ Makefile.am      | 15 +--------------
+ configure.ac     |  1 +
+ perf/Makefile.am | 24 ++++++++++++++++++++++++
+ 3 files changed, 26 insertions(+), 14 deletions(-)
+
+commit a424a92ce5e47b35d3128be1a612d3130c4c85b0
+Author: Garret Rieger <grieger at google.com>
+Date:   Fri Apr 29 22:14:03 2022 +0000
+
+    [subset] s/void */intptr_t.
+
+ src/hb-ot-cmap-table.hh | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+commit aad67f5629b1407df1b3152dfce0aefafbfb4132
+Author: Garret Rieger <grieger at google.com>
+Date:   Fri Apr 29 22:01:06 2022 +0000
+
+    [subset] cache results of collect_unicodes.
+
+ src/hb-ot-cmap-table.hh | 45 +++++++++++++++++++++++++++++++++++++--------
+ 1 file changed, 37 insertions(+), 8 deletions(-)
+
+commit 35681b3edb79b1286f1aa0ece2f6ae99e0363190
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri Apr 29 16:02:55 2022 -0600
+
+    [benchmark-shape] Break lines and shape separately
+
+ perf/benchmark-shape.cc | 23 +++++++++++++++++------
+ 1 file changed, 17 insertions(+), 6 deletions(-)
+
+commit be1ac9c57232317647e59983e72b6a86f93151a2
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri Apr 29 15:55:19 2022 -0600
+
+    [benchmark-shape] Data-driven test sets
+
+ perf/benchmark-shape.cc | 78 ++++++++++++++++++++++++++++---------------------
+ 1 file changed, 44 insertions(+), 34 deletions(-)
+
+commit ae3efc64248f46478fe9ad3863a5dfb0a362fe5f
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri Apr 29 15:37:11 2022 -0600
+
+    [perf] Spawn off benchmark-shape from perf runner
+
+ perf/{perf-shaping.hh => benchmark-shape.cc} |  2 ++
+ perf/meson.build                             | 10 ++++++++++
+ perf/perf.cc                                 |  1 -
+ 3 files changed, 12 insertions(+), 1 deletion(-)
+
+commit 5f43ce825afbedb1edbbc6610d1c017aa0f5fe27
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri Apr 29 13:37:46 2022 -0600
+
+    [benchmark-set] Split SetLookup into an ordered and random version
+
+ perf/benchmark-set.cc | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+commit ae9c7b861b257897a7ff0044d38e70f95df3eec7
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri Apr 29 13:39:04 2022 -0600
+
+    [benchmark-set] At least increase needle by one in lookup benchmark
+
+ perf/benchmark-set.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 68a9b83d157c2c2ece2c49732f5bf68d843a77a8
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri Apr 29 13:27:42 2022 -0600
+
+    [benchmark-set] At least increase needle by one in lookup benchmark
+
+ perf/benchmark-set.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit b4236b7de6bb2823a4561357342309b0f6d7d264
+Author: Garret Rieger <grieger at google.com>
+Date:   Fri Apr 29 19:21:13 2022 +0000
+
+    [subset] Optimize Cmap4 collect_unicodes.
+    
+    Use set add_range() instead of individual add() calls.
+
+ src/hb-ot-cmap-table.hh | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+commit 5866ec05f5a2a613501095e1de64d641ad898021
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri Apr 29 13:14:41 2022 -0600
+
+    [benchmark-map] Remove rand() overhead from benchmark
+
+ perf/benchmark-map.cc | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+commit 067225a86d4309020b950661ef9de6cb0c51eb98
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri Apr 29 13:04:36 2022 -0600
+
+    [set] Optimize const page_for() using last_page_lookup caching
+    
+    Similar to previous commit.
+    
+    This speeds up SetLookup benchmark by 50%, but that's because that
+    lookup always hits the same page...
+
+ src/hb-bit-set.hh | 24 +++++++++++++++++++-----
+ 1 file changed, 19 insertions(+), 5 deletions(-)
+
+commit c283e41ce39bb3740417bed4f240cf625fb38cd4
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri Apr 29 12:45:48 2022 -0600
+
+    [set] Optimize non-const page_for() using last_page_lookup caching
+    
+    This speeds up SetOrderedInsert tests by 15 to 40 percent, and the
+    subset_mplus1p benchmarks by 9 to 27 percent.
+
+ src/hb-bit-set.hh | 16 +++++++++++++++-
+ 1 file changed, 15 insertions(+), 1 deletion(-)
+
+commit dd005911b955da49a11aa755acb9addc0c8a2a24
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Fri Apr 29 12:23:53 2022 -0600
+
+    [benchmark-set] Reduce lookup benchmark overhead
+    
+    Turnsout 90% was overhead...  Now lookup is in the 4ns ballpark.
+
+ perf/benchmark-set.cc | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+commit 4de5352a3d4f501b68907fa419a4fed70676e720
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Thu Apr 28 14:40:33 2022 -0600
+
+    [test] Add test
+    
+    From https://github.com/harfbuzz/harfbuzz/issues/3545
+    
+    Dropped the CFF table.
+
+ .../fonts/a59fd13f1525a91cbe529c882e93d9d1fbb80463.ttf   | Bin 0 -> 1180 bytes
+ test/shape/data/in-house/tests/context-matching.tests    |   1 +
+ 2 files changed, 1 insertion(+)
+
+commit d8292b8446b7875281a0d6fc8cb90e96b2f8d156
+Author: Behdad Esfahbod <behdad at behdad.org>
+Date:   Wed Apr 27 12:38:35 2022 -0600
+
+    [CFF] Fix parsing of empty Index
+    
+    https://github.com/harfbuzz/harfbuzz/issues/3545#issuecomment-1111047941
+
+ src/hb-ot-cff-common.hh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 6454cec085ba51cefcd12b1f8027bc4a647347d5
+Author: David Corbett <corbett.dav at northeastern.edu>
+Date:   Sun Apr 24 11:10:17 2022 -0400
+
+    [USE] Classify U+10A38 as CONS_MOD_BELOW
+
+ src/gen-use-table.py                 | 3 +++
+ src/hb-ot-shape-complex-use-table.hh | 2 +-
+ 2 files changed, 4 insertions(+), 1 deletion(-)
+
 commit f7aee78e90bc53b3a95eb56d7550c9effe569ea2
 Author: Khaled Hosny <khaled at aliftype.com>
 Date:   Sun Apr 24 05:47:57 2022 +0200

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/Makefile.am
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/Makefile.am	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/Makefile.am	2022-05-21 02:44:01 UTC (rev 63353)
@@ -4,7 +4,7 @@
 
 ACLOCAL_AMFLAGS = -I m4
 
-SUBDIRS = src util test docs
+SUBDIRS = src util test perf docs
 
 EXTRA_DIST = \
 	autogen.sh \
@@ -26,19 +26,6 @@
 	subprojects/ragel.wrap \
 	subprojects/packagefiles/ragel/meson.build \
 	subprojects/ttf-parser.wrap \
-	perf/meson.build \
-	perf/perf-draw.hh \
-	perf/perf-extents.hh \
-	perf/perf-shaping.hh \
-	perf/perf.cc \
-	perf/fonts/Amiri-Regular.ttf \
-	perf/fonts/NotoNastaliqUrdu-Regular.ttf \
-	perf/fonts/NotoSansDevanagari-Regular.ttf \
-	perf/fonts/Roboto-Regular.ttf \
-	perf/texts/en-thelittleprince.txt \
-	perf/texts/en-words.txt \
-	perf/texts/fa-monologue.txt \
-	perf/texts/fa-thelittleprince.txt \
 	mingw-configure.sh \
 	$(NULL)
 

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/NEWS
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/NEWS	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/NEWS	2022-05-21 02:44:01 UTC (rev 63353)
@@ -1,3 +1,20 @@
+Overview of changes leading to 4.3.0
+Friday, May 20, 2022
+====================================
+- Major speed up in loading and subsetting fonts, especially in
+  handling CFF table. Subsetting some fonts is now 3 times faster.
+  (Behdad Esfahbod, Garret Rieger)
+- Speed up blending CFF2 table. (Behdad Esfahbod)
+- Speed up hb_ot_tags_from_language(). (Behdad Esfahbod, David Corbett)
+- Fix USE classification of U+10A38 to fix multiple marks on single Kharoshthi
+  base. (David Corbett)
+- Fix parsing of empty CFF Index. (Behdad Esfahbod)
+- Fix subsetting CPAL table with partial palette overlaps. (Garret Rieger)
+
+- New API
++hb_map_is_equal() (Behdad Esfahbod)
+
+
 Overview of changes leading to 4.2.1
 Sunday, April 24, 2022
 ====================================

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/configure.ac
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/configure.ac	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/configure.ac	2022-05-21 02:44:01 UTC (rev 63353)
@@ -1,6 +1,6 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [4.2.1],
+        [4.3.0],
         [https://github.com/harfbuzz/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
@@ -437,6 +437,7 @@
 test/subset/Makefile
 test/subset/data/Makefile
 test/subset/data/repack_tests/Makefile
+perf/Makefile
 docs/Makefile
 docs/version.xml
 ])

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/meson.build
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/meson.build	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/meson.build	2022-05-21 02:44:01 UTC (rev 63353)
@@ -1,6 +1,6 @@
 project('harfbuzz', 'c', 'cpp',
   meson_version: '>= 0.55.0',
-  version: '4.2.1',
+  version: '4.3.0',
   default_options: [
     'cpp_rtti=false',       # Just to support msvc, we are passing -fno-exceptions also anyway
     'cpp_std=c++11',

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/gen-tag-table.py
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/gen-tag-table.py	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/gen-tag-table.py	2022-05-21 02:44:01 UTC (rev 63353)
@@ -894,7 +894,6 @@
 print ('#ifndef HB_OT_TAG_TABLE_HH')
 print ('#define HB_OT_TAG_TABLE_HH')
 print ()
-print ('static const LangTag ot_languages[] = {')
 
 def hb_tag (tag):
 	"""Convert a tag to ``HB_TAG`` form.
@@ -944,34 +943,36 @@
 def same_tag (bcp_47_tag, ot_tags):
 	return len (bcp_47_tag) == 3 and len (ot_tags) == 1 and bcp_47_tag == ot_tags[0].lower ()
 
-for language, tags in sorted (ot.from_bcp_47.items ()):
-	if language == '' or '-' in language:
-		continue
-	commented_out = same_tag (language, tags)
-	for i, tag in enumerate (tags, start=1):
-		print ('%s{\"%s\",\t%s},' % ('/*' if commented_out else '  ', language, hb_tag (tag)), end='')
-		if commented_out:
-			print ('*/', end='')
-		print ('\t/* ', end='')
-		bcp_47_name = bcp_47.names.get (language, '')
-		bcp_47_name_candidates = bcp_47_name.split ('\n')
-		ot_name = ot.names[tag]
-		scope = bcp_47.scopes.get (language, '')
-		if tag == DEFAULT_LANGUAGE_SYSTEM:
-			write (f'{bcp_47_name_candidates[0]}{scope} != {ot.names[language.upper ()]}')
-		else:
-			intersection = language_name_intersection (bcp_47_name, ot_name)
-			if not intersection:
-				write ('%s%s -> %s' % (bcp_47_name_candidates[0], scope, ot_name))
+for language_len in (2, 3):
+	print ('static const LangTag ot_languages%d[] = {' % language_len)
+	for language, tags in sorted (ot.from_bcp_47.items ()):
+		if language == '' or '-' in language:
+			continue
+		if len(language) != language_len: continue
+		commented_out = same_tag (language, tags)
+		for i, tag in enumerate (tags, start=1):
+			print ('%s{%s,\t%s},' % ('/*' if commented_out else '  ', hb_tag (language), hb_tag (tag)), end='')
+			if commented_out:
+				print ('*/', end='')
+			print ('\t/* ', end='')
+			bcp_47_name = bcp_47.names.get (language, '')
+			bcp_47_name_candidates = bcp_47_name.split ('\n')
+			ot_name = ot.names[tag]
+			scope = bcp_47.scopes.get (language, '')
+			if tag == DEFAULT_LANGUAGE_SYSTEM:
+				write (f'{bcp_47_name_candidates[0]}{scope} != {ot.names[language.upper ()]}')
 			else:
-				name = get_matching_language_name (intersection, bcp_47_name_candidates)
-				bcp_47.names[language] = name
-				write ('%s%s' % (name if len (name) > len (ot_name) else ot_name, scope))
-		print (' */')
+				intersection = language_name_intersection (bcp_47_name, ot_name)
+				if not intersection:
+					write ('%s%s -> %s' % (bcp_47_name_candidates[0], scope, ot_name))
+				else:
+					name = get_matching_language_name (intersection, bcp_47_name_candidates)
+					bcp_47.names[language] = name
+					write ('%s%s' % (name if len (name) > len (ot_name) else ot_name, scope))
+			print (' */')
+	print ('};')
+	print ()
 
-print ('};')
-print ()
-
 print ('/**')
 print (' * hb_ot_tags_from_complex_language:')
 print (' * @lang_str: a BCP 47 language tag to convert.')
@@ -993,12 +994,12 @@
 print ('\t\t\t\t  hb_tag_t     *tags /* OUT */)')
 print ('{')
 
-def print_subtag_matches (subtag, new_line):
+def print_subtag_matches (subtag, string, new_line):
 	if subtag:
 		if new_line:
 			print ()
 			print ('\t&& ', end='')
-		print ('subtag_matches (lang_str, limit, "-%s")' % subtag, end='')
+		print ('subtag_matches (%s, limit, "-%s", %i)' % (string, subtag, 1 + len (subtag)), end='')
 
 complex_tags = collections.defaultdict (list)
 for initial, group in itertools.groupby ((lt_tags for lt_tags in [
@@ -1009,6 +1010,8 @@
 		key=lambda lt_tags: lt_tags[0].get_group ()):
 	complex_tags[initial] += group
 
+# Calculate the min length of the subtags outside the switch
+min_subtag_len = 100
 for initial, items in sorted (complex_tags.items ()):
 	if initial != 'und':
 		continue
@@ -1015,32 +1018,50 @@
 	for lt, tags in items:
 		if not tags:
 			continue
+		subtag_len = 0
+		subtag_len += 1 + len (lt.script) if lt.script is not None else 0
+		subtag_len += 1 + len (lt.region) if lt.region is not None else 0
+		subtag_len += 1 + len (lt.variant) if lt.variant is not None else 0
+		min_subtag_len = min(subtag_len, min_subtag_len)
+
+print ('  if (limit - lang_str >= %d)' % (min_subtag_len + 2))
+print ('  {')
+print ("    const char *p = strchr (lang_str, '-');")
+print ("    if (!p || p >= limit || limit - p < %i) goto out;" % min_subtag_len)
+for initial, items in sorted (complex_tags.items ()):
+	if initial != 'und':
+		continue
+	for lt, tags in items:
+		if not tags:
+			continue
 		if lt.variant in bcp_47.prefixes:
 			expect (next (iter (bcp_47.prefixes[lt.variant])) == lt.language,
 					'%s is not a valid prefix of %s' % (lt.language, lt.variant))
-		print ('  if (', end='')
-		print_subtag_matches (lt.script, False)
-		print_subtag_matches (lt.region, False)
-		print_subtag_matches (lt.variant, False)
+		print ('    if (', end='')
+		print_subtag_matches (lt.script, 'p', False)
+		print_subtag_matches (lt.region, 'p', False)
+		print_subtag_matches (lt.variant, 'p', False)
 		print (')')
-		print ('  {')
-		write ('    /* %s */' % bcp_47.get_name (lt))
+		print ('    {')
+		write ('      /* %s */' % bcp_47.get_name (lt))
 		print ()
 		if len (tags) == 1:
-			write ('    tags[0] = %s;  /* %s */' % (hb_tag (tags[0]), ot.names[tags[0]]))
+			write ('      tags[0] = %s;  /* %s */' % (hb_tag (tags[0]), ot.names[tags[0]]))
 			print ()
-			print ('    *count = 1;')
+			print ('      *count = 1;')
 		else:
 			print ('    hb_tag_t possible_tags[] = {')
 			for tag in tags:
 				write ('      %s,  /* %s */' % (hb_tag (tag), ot.names[tag]))
 				print ()
-			print ('    };')
-			print ('    for (i = 0; i < %s && i < *count; i++)' % len (tags))
-			print ('      tags[i] = possible_tags[i];')
-			print ('    *count = i;')
-		print ('    return true;')
-		print ('  }')
+			print ('      };')
+			print ('      for (i = 0; i < %s && i < *count; i++)' % len (tags))
+			print ('\ttags[i] = possible_tags[i];')
+			print ('      *count = i;')
+		print ('      return true;')
+		print ('    }')
+print ('  }')
+print ('out:')
 
 print ('  switch (lang_str[0])')
 print ('  {')
@@ -1067,10 +1088,10 @@
 			if string_literal[-1] == '-':
 				print ('0 == strncmp (&lang_str[1], "%s", %i)' % (string_literal, len (string_literal)), end='')
 			else:
-				print ('lang_matches (&lang_str[1], "%s")' % string_literal, end='')
-		print_subtag_matches (script, True)
-		print_subtag_matches (region, True)
-		print_subtag_matches (lt.variant, True)
+				print ('lang_matches (&lang_str[1], limit, "%s", %i)' % (string_literal, len (string_literal)), end='')
+		print_subtag_matches (script, 'lang_str', True)
+		print_subtag_matches (region, 'lang_str', True)
+		print_subtag_matches (lt.variant, 'lang_str', True)
 		print (')')
 		print ('    {')
 		write ('      /* %s */' % bcp_47.get_name (lt))

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/gen-use-table.py
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/gen-use-table.py	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/gen-use-table.py	2022-05-21 02:44:01 UTC (rev 63353)
@@ -377,6 +377,9 @@
 		if U in [0x11302, 0x11303, 0x114C1]: UIPC = Top
 		if 0x1CF8 <= U <= 0x1CF9: UIPC = Top
 
+		# TODO: https://github.com/harfbuzz/harfbuzz/issues/3550
+		if U == 0x10A38: UIPC = Bottom
+
 		# TODO: https://github.com/harfbuzz/harfbuzz/pull/982
 		# also  https://github.com/harfbuzz/harfbuzz/issues/1012
 		if 0x1112A <= U <= 0x1112B: UIPC = Top

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-algs.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-algs.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-algs.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -150,10 +150,26 @@
 			        uint8_t ((V >> 16) & 0xFF),
 			        uint8_t ((V >>  8) & 0xFF),
 			        uint8_t ((V      ) & 0xFF)} {}
-  constexpr operator Type () const { return (v[0] << 24)
-					  + (v[1] << 16)
-					  + (v[2] <<  8)
-					  + (v[3]      ); }
+
+  struct __attribute__((packed)) packed_uint32_t { uint32_t v; };
+  constexpr operator Type () const {
+#if ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \
+    defined(__BYTE_ORDER) && \
+    (__BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN)
+    /* Spoon-feed the compiler a big-endian integer with alignment 1.
+     * https://github.com/harfbuzz/harfbuzz/pull/1398 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+    return __builtin_bswap32 (((packed_uint32_t *) this)->v);
+#else /* __BYTE_ORDER == __BIG_ENDIAN */
+    return ((packed_uint32_t *) this)->v;
+#endif
+#else
+    return (v[0] << 24)
+	 + (v[1] << 16)
+	 + (v[2] <<  8)
+	 + (v[3]      );
+#endif
+  }
   private: uint8_t v[4];
 };
 
@@ -213,11 +229,11 @@
 
 template <typename T>
 static inline
-T hb_coerce (const T v) { return v; }
+constexpr T hb_coerce (const T v) { return v; }
 template <typename T, typename V,
 	  hb_enable_if (!hb_is_same (hb_decay<T>, hb_decay<V>) && std::is_pointer<V>::value)>
 static inline
-T hb_coerce (const V v) { return *v; }
+constexpr T hb_coerce (const V v) { return *v; }
 
 struct
 {

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-array.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-array.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-array.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -346,7 +346,7 @@
     unsigned int i;
     return bfind (x, &i) ? &this->arrayZ[i] : not_found;
   }
-  template <typename T>
+  template <typename T, typename ...Ts>
   const Type *bsearch (const T &x, const Type *not_found = nullptr) const
   {
     unsigned int i;
@@ -384,8 +384,8 @@
     }
     return false;
   }
-  template <typename T>
-  bool bsearch_impl (const T &x, unsigned *pos) const
+  template <typename T, typename ...Ts>
+  bool bsearch_impl (const T &x, unsigned *pos, Ts... ds) const
   {
     return hb_bsearch_impl (pos,
 			    x,
@@ -392,7 +392,8 @@
 			    this->arrayZ,
 			    this->length,
 			    sizeof (Type),
-			    _hb_cmp_method<T, Type>);
+			    _hb_cmp_method<T, Type, Ts...>,
+			    ds...);
   }
 };
 template <typename T> inline hb_sorted_array_t<T>
@@ -403,7 +404,7 @@
 { return hb_sorted_array_t<T> (array_); }
 
 template <typename T>
-bool hb_array_t<T>::operator == (const hb_array_t<T> &o) const
+inline bool hb_array_t<T>::operator == (const hb_array_t<T> &o) const
 {
   if (o.length != this->length) return false;
   for (unsigned int i = 0; i < this->length; i++) {
@@ -411,9 +412,19 @@
   }
   return true;
 }
+template <>
+inline bool hb_array_t<const char>::operator == (const hb_array_t<const char> &o) const
+{
+  if (o.length != this->length) return false;
+  return 0 == hb_memcmp (arrayZ, o.arrayZ, length);
+}
+template <>
+inline bool hb_array_t<const unsigned char>::operator == (const hb_array_t<const unsigned char> &o) const
+{
+  if (o.length != this->length) return false;
+  return 0 == hb_memcmp (arrayZ, o.arrayZ, length);
+}
 
-/* TODO Specialize operator== for hb_bytes_t and hb_ubytes_t. */
-
 template <>
 inline uint32_t hb_array_t<const char>::hash () const {
   uint32_t current = 0;
@@ -421,7 +432,6 @@
     current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u);
   return current;
 }
-
 template <>
 inline uint32_t hb_array_t<const unsigned char>::hash () const {
   uint32_t current = 0;

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-bimap.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-bimap.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-bimap.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -39,6 +39,12 @@
     back_map.reset ();
   }
 
+  void resize (unsigned pop)
+  {
+    forw_map.resize (pop);
+    back_map.resize (pop);
+  }
+
   bool in_error () const { return forw_map.in_error () || back_map.in_error (); }
 
   void set (hb_codepoint_t lhs, hb_codepoint_t rhs)

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-page.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-page.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-page.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -40,11 +40,18 @@
 
   bool is_empty () const
   {
-    for (unsigned int i = 0; i < len (); i++)
+    for (unsigned i = 0; i < len (); i++)
       if (v[i])
 	return false;
     return true;
   }
+  uint32_t hash () const
+  {
+    uint32_t h = 0;
+    for (unsigned i = 0; i < len (); i++)
+      h = h * 31 + hb_hash (v[i]);
+    return h;
+  }
 
   void add (hb_codepoint_t g) { elt (g) |= mask (g); }
   void del (hb_codepoint_t g) { elt (g) &= ~mask (g); }

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-set-invertible.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-set-invertible.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-set-invertible.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -38,10 +38,10 @@
   bool inverted = false;
 
   hb_bit_set_invertible_t () = default;
-  hb_bit_set_invertible_t (hb_bit_set_invertible_t& o) = default;
-  hb_bit_set_invertible_t (hb_bit_set_invertible_t&& o) = default;
+  hb_bit_set_invertible_t (const hb_bit_set_invertible_t& o) = default;
+  hb_bit_set_invertible_t (hb_bit_set_invertible_t&& other) : hb_bit_set_invertible_t () { hb_swap (*this, other); }
   hb_bit_set_invertible_t& operator= (const hb_bit_set_invertible_t& o) = default;
-  hb_bit_set_invertible_t& operator= (hb_bit_set_invertible_t&& o) = default;
+  hb_bit_set_invertible_t& operator= (hb_bit_set_invertible_t&& other) { hb_swap (*this, other); return *this; }
   friend void swap (hb_bit_set_invertible_t &a, hb_bit_set_invertible_t &b)
   {
     if (likely (!a.s.successful || !b.s.successful))
@@ -56,6 +56,7 @@
   bool in_error () const { return s.in_error (); }
   explicit operator bool () const { return !is_empty (); }
 
+  void alloc (unsigned sz) { s.alloc (sz); }
   void reset ()
   {
     s.reset ();
@@ -79,6 +80,8 @@
     next (&v);
     return v == INVALID;
   }
+  uint32_t hash () const { return s.hash () ^ inverted; }
+
   hb_codepoint_t get_min () const
   {
     hb_codepoint_t v = INVALID;

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-set.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-set.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-bit-set.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -97,6 +97,13 @@
     return true;
   }
 
+  void alloc (unsigned sz)
+  {
+    sz >>= (page_t::PAGE_BITS_LOG_2 - 1);
+    pages.alloc (sz);
+    page_map.alloc (sz);
+  }
+
   void reset ()
   {
     successful = true;
@@ -119,6 +126,14 @@
   }
   explicit operator bool () const { return !is_empty (); }
 
+  uint32_t hash () const
+  {
+    uint32_t h = 0;
+    for (auto &map : page_map)
+      h = h * 31 + hb_hash (map.major) + hb_hash (pages[map.index]);
+    return h;
+  }
+
   private:
   void dirty () { population = UINT_MAX; }
   public:
@@ -341,15 +356,14 @@
       return;
     population = other.population;
 
-    /* TODO switch to vector operator =. */
-    hb_memcpy ((void *) pages, (const void *) other.pages, count * pages.item_size);
-    hb_memcpy ((void *) page_map, (const void *) other.page_map, count * page_map.item_size);
+    page_map = other.page_map;
+    pages = other.pages;
   }
 
   bool is_equal (const hb_bit_set_t &other) const
   {
     if (has_population () && other.has_population () &&
-	get_population () != other.get_population ())
+	population != other.population)
       return false;
 
     unsigned int na = pages.length;
@@ -377,7 +391,7 @@
   bool is_subset (const hb_bit_set_t &larger_set) const
   {
     if (has_population () && larger_set.has_population () &&
-	get_population () != larger_set.get_population ())
+	population != larger_set.population)
       return false;
 
     uint32_t spi = 0;
@@ -874,7 +888,19 @@
 
   page_t *page_for (hb_codepoint_t g, bool insert = false)
   {
-    page_map_t map = {get_major (g), pages.length};
+    unsigned major = get_major (g);
+
+    /* The extra page_map length is necessary; can't just rely on vector here,
+     * since the next check would be tricked because a null page also has
+     * major==0, which we can't distinguish from an actualy major==0 page... */
+    if (likely (last_page_lookup < page_map.length))
+    {
+      auto &cached_page = page_map.arrayZ[last_page_lookup];
+      if (cached_page.major == major)
+	return &pages[cached_page.index];
+    }
+
+    page_map_t map = {major, pages.length};
     unsigned int i;
     if (!page_map.bfind (map, &i, HB_NOT_FOUND_STORE_CLOSEST))
     {
@@ -890,15 +916,31 @@
 	       (page_map.length - 1 - i) * page_map.item_size);
       page_map[i] = map;
     }
+
+    last_page_lookup = i;
     return &pages[page_map[i].index];
   }
   const page_t *page_for (hb_codepoint_t g) const
   {
-    page_map_t key = {get_major (g)};
-    const page_map_t *found = page_map.bsearch (key);
-    if (found)
-      return &pages[found->index];
-    return nullptr;
+    unsigned major = get_major (g);
+
+    /* The extra page_map length is necessary; can't just rely on vector here,
+     * since the next check would be tricked because a null page also has
+     * major==0, which we can't distinguish from an actualy major==0 page... */
+    if (likely (last_page_lookup < page_map.length))
+    {
+      auto &cached_page = page_map.arrayZ[last_page_lookup];
+      if (cached_page.major == major)
+	return &pages[cached_page.index];
+    }
+
+    page_map_t key = {major};
+    unsigned int i;
+    if (!page_map.bfind (key, &i))
+      return nullptr;
+
+    last_page_lookup = i;
+    return &pages[page_map[i].index];
   }
   page_t &page_at (unsigned int i) { return pages[page_map[i].index]; }
   const page_t &page_at (unsigned int i) const { return pages[page_map[i].index]; }

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-common.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-common.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-common.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -248,6 +248,9 @@
 /* byte string */
 struct UnsizedByteStr : UnsizedArrayOf <HBUINT8>
 {
+  hb_ubytes_t as_ubytes (unsigned l) const
+  { return hb_ubytes_t ((const unsigned char *) this, l); }
+
   // encode 2-byte int (Dict/CharString) or 4-byte int (Dict)
   template <typename T, typename V>
   static bool serialize_int (hb_serialize_context_t *c, op_code_t intOp, V value)
@@ -274,33 +277,10 @@
   /* Defining null_size allows a Null object may be created. Should be safe because:
    * A descendent struct Dict uses a Null pointer to indicate a missing table,
    * checked before access.
-   * byte_str_t, a wrapper struct pairing a byte pointer along with its length, always
-   * checks the length before access. A Null pointer is used as the initial pointer
-   * along with zero length by the default ctor.
    */
   DEFINE_SIZE_MIN(0);
 };
 
-/* Holder of a section of byte string within a CFFIndex entry */
-struct byte_str_t : hb_ubytes_t
-{
-  byte_str_t ()
-    : hb_ubytes_t () {}
-  byte_str_t (const UnsizedByteStr& s, unsigned int l)
-    : hb_ubytes_t ((const unsigned char*)&s, l) {}
-  byte_str_t (const unsigned char *s, unsigned int l)
-    : hb_ubytes_t (s, l) {}
-  byte_str_t (const hb_ubytes_t &ub)	/* conversion from hb_ubytes_t */
-    : hb_ubytes_t (ub) {}
-
-  /* sub-string */
-  byte_str_t sub_str (unsigned int offset, unsigned int len_) const
-  { return byte_str_t (hb_ubytes_t::sub_array (offset, len_)); }
-
-  bool check_limit (unsigned int offset, unsigned int count) const
-  { return (offset + count <= length); }
-};
-
 /* A byte string associated with the current offset and an error condition */
 struct byte_str_ref_t
 {
@@ -308,7 +288,7 @@
 
   void init ()
   {
-    str = byte_str_t ();
+    str = hb_ubytes_t ();
     offset = 0;
     error = false;
   }
@@ -315,10 +295,10 @@
 
   void fini () {}
 
-  byte_str_ref_t (const byte_str_t &str_, unsigned int offset_ = 0)
+  byte_str_ref_t (const hb_ubytes_t &str_, unsigned int offset_ = 0)
     : str (str_), offset (offset_), error (false) {}
 
-  void reset (const byte_str_t &str_, unsigned int offset_ = 0)
+  void reset (const hb_ubytes_t &str_, unsigned int offset_ = 0)
   {
     str = str_;
     offset = offset_;
@@ -334,14 +314,14 @@
     return str[offset + i];
   }
 
-  /* Conversion to byte_str_t */
-  operator byte_str_t () const { return str.sub_str (offset, str.length - offset); }
+  /* Conversion to hb_ubytes_t */
+  operator hb_ubytes_t () const { return str.sub_array (offset, str.length - offset); }
 
-  byte_str_t sub_str (unsigned int offset_, unsigned int len_) const
-  { return str.sub_str (offset_, len_); }
+  hb_ubytes_t sub_array (unsigned int offset_, unsigned int len_) const
+  { return str.sub_array (offset_, len_); }
 
   bool avail (unsigned int count=1) const
-  { return (!in_error () && str.check_limit (offset, count)); }
+  { return (!in_error () && offset + count <= str.length); }
   void inc (unsigned int count=1)
   {
     if (likely (!in_error () && (offset <= str.length) && (offset + count <= str.length)))
@@ -358,7 +338,7 @@
   void set_error ()      { error = true; }
   bool in_error () const { return error; }
 
-  byte_str_t       str;
+  hb_ubytes_t       str;
   unsigned int  offset; /* beginning of the sub-string within str */
 
   protected:
@@ -365,30 +345,25 @@
   bool	  error;
 };
 
-typedef hb_vector_t<byte_str_t> byte_str_array_t;
+using byte_str_array_t = hb_vector_t<hb_ubytes_t>;
 
 /* stack */
 template <typename ELEM, int LIMIT>
 struct cff_stack_t
 {
-  void init ()
-  {
-    error = false;
-    count = 0;
-    elements.init ();
-    elements.resize (kSizeLimit);
-  }
-  void fini () { elements.fini (); }
-
   ELEM& operator [] (unsigned int i)
   {
-    if (unlikely (i >= count)) set_error ();
+    if (unlikely (i >= count))
+    {
+      set_error ();
+      return Crap (ELEM);
+    }
     return elements[i];
   }
 
   void push (const ELEM &v)
   {
-    if (likely (count < elements.length))
+    if (likely (count < LIMIT))
       elements[count++] = v;
     else
       set_error ();
@@ -395,7 +370,7 @@
   }
   ELEM &push ()
   {
-    if (likely (count < elements.length))
+    if (likely (count < LIMIT))
       return elements[count++];
     else
     {
@@ -424,7 +399,7 @@
 
   const ELEM& peek ()
   {
-    if (unlikely (count < 0))
+    if (unlikely (count == 0))
     {
       set_error ();
       return Null (ELEM);
@@ -434,7 +409,7 @@
 
   void unpop ()
   {
-    if (likely (count < elements.length))
+    if (likely (count < LIMIT))
       count++;
     else
       set_error ();
@@ -442,18 +417,19 @@
 
   void clear () { count = 0; }
 
-  bool in_error () const { return (error || elements.in_error ()); }
+  bool in_error () const { return (error); }
   void set_error ()      { error = true; }
 
   unsigned int get_count () const { return count; }
   bool is_empty () const          { return !count; }
 
-  static constexpr unsigned kSizeLimit = LIMIT;
+  hb_array_t<const ELEM> sub_array (unsigned start, unsigned length) const
+  { return hb_array_t<const ELEM> (elements).sub_array (start, length); }
 
-  protected:
-  bool error;
-  unsigned int count;
-  hb_vector_t<ELEM> elements;
+  private:
+  bool error = false;
+  unsigned int count = 0;
+  ELEM elements[LIMIT];
 };
 
 /* argument stack */
@@ -508,9 +484,6 @@
     return true;
   }
 
-  hb_array_t<const ARG> get_subarray (unsigned int start) const
-  { return S::elements.sub_array (start); }
-
   private:
   typedef cff_stack_t<ARG, 513> S;
 };
@@ -518,8 +491,8 @@
 /* an operator prefixed by its operands in a byte string */
 struct op_str_t
 {
+  hb_ubytes_t str;
   op_code_t  op;
-  byte_str_t str;
 };
 
 /* base of OP_SERIALIZER */
@@ -547,11 +520,16 @@
   }
   void fini () { values.fini (); }
 
+  void alloc (unsigned n)
+  {
+    values.alloc (n);
+  }
+
   void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t ())
   {
     VAL *val = values.push ();
     val->op = op;
-    val->str = str_ref.str.sub_str (opStart, str_ref.offset - opStart);
+    val->str = str_ref.str.sub_array (opStart, str_ref.offset - opStart);
     opStart = str_ref.offset;
   }
 
@@ -559,14 +537,14 @@
   {
     VAL *val = values.push (v);
     val->op = op;
-    val->str = str_ref.sub_str ( opStart, str_ref.offset - opStart);
+    val->str = str_ref.sub_array ( opStart, str_ref.offset - opStart);
     opStart = str_ref.offset;
   }
 
   bool has_op (op_code_t op) const
   {
-    for (unsigned int i = 0; i < get_count (); i++)
-      if (get_value (i).op == op) return true;
+    for (const auto& v : values)
+      if (v.op == op) return true;
     return false;
   }
 
@@ -581,14 +559,11 @@
 template <typename ARG=number_t>
 struct interp_env_t
 {
-  void init (const byte_str_t &str_)
+  interp_env_t () {}
+  interp_env_t (const hb_ubytes_t &str_)
   {
     str_ref.reset (str_);
-    argStack.init ();
-    error = false;
   }
-  void fini () { argStack.fini (); }
-
   bool in_error () const
   { return error || str_ref.in_error () || argStack.in_error (); }
 
@@ -622,10 +597,10 @@
   arg_stack_t<ARG>
 		argStack;
   protected:
-  bool		error;
+  bool		error = false;
 };
 
-typedef interp_env_t<> num_interp_env_t;
+using num_interp_env_t =  interp_env_t<>;
 
 template <typename ARG=number_t>
 struct opset_t
@@ -668,11 +643,8 @@
 template <typename ENV>
 struct interpreter_t
 {
-  ~interpreter_t() { fini (); }
-
-  void fini () { env.fini (); }
-
-  ENV env;
+  interpreter_t (ENV& env_) : env (env_) {}
+  ENV& env;
 };
 
 } /* namespace CFF */

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-cs-common.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-cs-common.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-cs-common.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -79,10 +79,10 @@
   unsigned int get_count () const { return subrs ? subrs->count : 0; }
   unsigned int get_bias () const  { return bias; }
 
-  byte_str_t operator [] (unsigned int index) const
+  hb_ubytes_t operator [] (unsigned int index) const
   {
     if (unlikely (!subrs || index >= subrs->count))
-      return Null (byte_str_t);
+      return hb_ubytes_t ();
     else
       return (*subrs)[index];
   }
@@ -112,10 +112,9 @@
 template <typename ARG, typename SUBRS>
 struct cs_interp_env_t : interp_env_t<ARG>
 {
-  void init (const byte_str_t &str, const SUBRS *globalSubrs_, const SUBRS *localSubrs_)
+  cs_interp_env_t (const hb_ubytes_t &str, const SUBRS *globalSubrs_, const SUBRS *localSubrs_) :
+    interp_env_t<ARG> (str)
   {
-    interp_env_t<ARG>::init (str);
-
     context.init (str, CSType_CharString);
     seen_moveto = true;
     seen_hintmask = false;
@@ -123,15 +122,11 @@
     vstem_count = 0;
     hintmask_size = 0;
     pt.set_int (0, 0);
-    callStack.init ();
     globalSubrs.init (globalSubrs_);
     localSubrs.init (localSubrs_);
   }
-  void fini ()
+  ~cs_interp_env_t ()
   {
-    interp_env_t<ARG>::fini ();
-
-    callStack.fini ();
     globalSubrs.fini ();
     localSubrs.fini ();
   }
@@ -880,6 +875,8 @@
 template <typename ENV, typename OPSET, typename PARAM>
 struct cs_interpreter_t : interpreter_t<ENV>
 {
+  cs_interpreter_t (ENV& env_) : interpreter_t<ENV> (env_) {}
+
   bool interpret (PARAM& param)
   {
     SUPER::env.set_endchar (false);

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-dict-common.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-dict-common.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-cff-interp-dict-common.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -179,6 +179,8 @@
 template <typename OPSET, typename PARAM, typename ENV=num_interp_env_t>
 struct dict_interpreter_t : interpreter_t<ENV>
 {
+  dict_interpreter_t (ENV& env_) : interpreter_t<ENV> (env_) {}
+
   bool interpret (PARAM& param)
   {
     param.init ();

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-cff1-interp-cs.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-cff1-interp-cs.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-cff1-interp-cs.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -38,9 +38,9 @@
 struct cff1_cs_interp_env_t : cs_interp_env_t<number_t, CFF1Subrs>
 {
   template <typename ACC>
-  void init (const byte_str_t &str, ACC &acc, unsigned int fd)
+  cff1_cs_interp_env_t (const hb_ubytes_t &str, ACC &acc, unsigned int fd)
+    : SUPER (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs)
   {
-    SUPER::init (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs);
     processed_width = false;
     has_width = false;
     arg_start = 0;
@@ -47,8 +47,6 @@
     in_seac = false;
   }
 
-  void fini () { SUPER::fini (); }
-
   void set_width (bool has_width_)
   {
     if (likely (!processed_width && (SUPER::argStack.get_count () > 0)))
@@ -154,7 +152,7 @@
 };
 
 template <typename OPSET, typename PARAM>
-struct cff1_cs_interpreter_t : cs_interpreter_t<cff1_cs_interp_env_t, OPSET, PARAM> {};
+using cff1_cs_interpreter_t = cs_interpreter_t<cff1_cs_interp_env_t, OPSET, PARAM>;
 
 } /* namespace CFF */
 

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-cff2-interp-cs.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-cff2-interp-cs.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-cff2-interp-cs.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -64,14 +64,14 @@
 typedef interp_env_t<blend_arg_t> BlendInterpEnv;
 typedef biased_subrs_t<CFF2Subrs>   cff2_biased_subrs_t;
 
-struct cff2_cs_interp_env_t : cs_interp_env_t<blend_arg_t, CFF2Subrs>
+template <typename ELEM>
+struct cff2_cs_interp_env_t : cs_interp_env_t<ELEM, CFF2Subrs>
 {
   template <typename ACC>
-  void init (const byte_str_t &str, ACC &acc, unsigned int fd,
-	     const int *coords_=nullptr, unsigned int num_coords_=0)
+  cff2_cs_interp_env_t (const hb_ubytes_t &str, ACC &acc, unsigned int fd,
+			const int *coords_=nullptr, unsigned int num_coords_=0)
+    : SUPER (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs)
   {
-    SUPER::init (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs);
-
     coords = coords_;
     num_coords = num_coords_;
     varStore = acc.varStore;
@@ -100,18 +100,14 @@
       return OpCode_return;
   }
 
-  const blend_arg_t& eval_arg (unsigned int i)
+  const ELEM& eval_arg (unsigned int i)
   {
-    blend_arg_t  &arg = argStack[i];
-    blend_arg (arg);
-    return arg;
+    return SUPER::argStack[i];
   }
 
-  const blend_arg_t& pop_arg ()
+  const ELEM& pop_arg ()
   {
-    blend_arg_t  &arg = argStack.pop ();
-    blend_arg (arg);
-    return arg;
+    return SUPER::argStack.pop ();
   }
 
   void process_blend ()
@@ -122,7 +118,7 @@
       if (do_blend)
       {
 	if (unlikely (!scalars.resize (region_count)))
-	  set_error ();
+	  SUPER::set_error ();
 	else
 	  varStore->varStore.get_region_scalars (get_ivs (), coords, num_coords,
 						 &scalars[0], region_count);
@@ -133,10 +129,10 @@
 
   void process_vsindex ()
   {
-    unsigned int  index = argStack.pop_uint ();
+    unsigned int  index = SUPER::argStack.pop_uint ();
     if (unlikely (seen_vsindex () || seen_blend))
     {
-      set_error ();
+     SUPER::set_error ();
     }
     else
     {
@@ -151,22 +147,18 @@
   void	 set_ivs (unsigned int ivs_) { ivs = ivs_; }
   bool	 seen_vsindex () const { return seen_vsindex_; }
 
-  protected:
-  void blend_arg (blend_arg_t &arg)
+  double blend_deltas (hb_array_t<const ELEM> deltas) const
   {
-    if (do_blend && arg.blending ())
+    double v = 0;
+    if (do_blend)
     {
-      if (likely (scalars.length == arg.deltas.length))
+      if (likely (scalars.length == deltas.length))
       {
-	double v = arg.to_real ();
 	for (unsigned int i = 0; i < scalars.length; i++)
-	{
-	  v += (double)scalars[i] * arg.deltas[i].to_real ();
-	}
-	arg.set_real (v);
-	arg.deltas.resize (0);
+	  v += (double) scalars[i] * deltas[i].to_real ();
       }
     }
+    return v;
   }
 
   protected:
@@ -180,22 +172,24 @@
   bool	  seen_vsindex_;
   bool	  seen_blend;
 
-  typedef cs_interp_env_t<blend_arg_t, CFF2Subrs> SUPER;
+  typedef cs_interp_env_t<ELEM, CFF2Subrs> SUPER;
 };
-template <typename OPSET, typename PARAM, typename PATH=path_procs_null_t<cff2_cs_interp_env_t, PARAM>>
-struct cff2_cs_opset_t : cs_opset_t<blend_arg_t, OPSET, cff2_cs_interp_env_t, PARAM, PATH>
+template <typename OPSET, typename PARAM, typename ELEM, typename PATH=path_procs_null_t<cff2_cs_interp_env_t<ELEM>, PARAM>>
+struct cff2_cs_opset_t : cs_opset_t<ELEM, OPSET, cff2_cs_interp_env_t<ELEM>, PARAM, PATH>
 {
-  static void process_op (op_code_t op, cff2_cs_interp_env_t &env, PARAM& param)
+  static void process_op (op_code_t op, cff2_cs_interp_env_t<ELEM> &env, PARAM& param)
   {
     switch (op) {
       case OpCode_callsubr:
       case OpCode_callgsubr:
 	/* a subroutine number shouldn't be a blended value */
+#if 0
 	if (unlikely (env.argStack.peek ().blending ()))
 	{
 	  env.set_error ();
 	  break;
 	}
+#endif
 	SUPER::process_op (op, env, param);
 	break;
 
@@ -204,11 +198,13 @@
 	break;
 
       case OpCode_vsindexcs:
+#if 0
 	if (unlikely (env.argStack.peek ().blending ()))
 	{
 	  env.set_error ();
 	  break;
 	}
+#endif
 	OPSET::process_vsindex (env, param);
 	break;
 
@@ -217,8 +213,27 @@
     }
   }
 
-  static void process_blend (cff2_cs_interp_env_t &env, PARAM& param)
+  template <typename T = ELEM,
+	    hb_enable_if (hb_is_same (T, blend_arg_t))>
+  static void process_arg_blend (cff2_cs_interp_env_t<ELEM> &env,
+				 ELEM &arg,
+				 const hb_array_t<const ELEM> blends,
+				 unsigned n, unsigned i)
   {
+    arg.set_blends (n, i, blends.length, blends);
+  }
+  template <typename T = ELEM,
+	    hb_enable_if (!hb_is_same (T, blend_arg_t))>
+  static void process_arg_blend (cff2_cs_interp_env_t<ELEM> &env,
+				 ELEM &arg,
+				 const hb_array_t<const ELEM> blends,
+				 unsigned n, unsigned i)
+  {
+    arg.set_real (arg.to_real () + env.blend_deltas (blends));
+  }
+
+  static void process_blend (cff2_cs_interp_env_t<ELEM> &env, PARAM& param)
+  {
     unsigned int n, k;
 
     env.process_blend ();
@@ -234,8 +249,8 @@
     }
     for (unsigned int i = 0; i < n; i++)
     {
-      const hb_array_t<const blend_arg_t>	blends = env.argStack.get_subarray (start + n + (i * k));
-      env.argStack[start + i].set_blends (n, i, k, blends);
+      const hb_array_t<const ELEM> blends = env.argStack.sub_array (start + n + (i * k), k);
+      process_arg_blend (env, env.argStack[start + i], blends, n, i);
     }
 
     /* pop off blend values leaving default values now adorned with blend values */
@@ -242,7 +257,7 @@
     env.argStack.pop (k * n);
   }
 
-  static void process_vsindex (cff2_cs_interp_env_t &env, PARAM& param)
+  static void process_vsindex (cff2_cs_interp_env_t<ELEM> &env, PARAM& param)
   {
     env.process_vsindex ();
     env.clear_args ();
@@ -249,11 +264,11 @@
   }
 
   private:
-  typedef cs_opset_t<blend_arg_t, OPSET, cff2_cs_interp_env_t, PARAM, PATH>  SUPER;
+  typedef cs_opset_t<ELEM, OPSET, cff2_cs_interp_env_t<ELEM>, PARAM, PATH>  SUPER;
 };
 
-template <typename OPSET, typename PARAM>
-struct cff2_cs_interpreter_t : cs_interpreter_t<cff2_cs_interp_env_t, OPSET, PARAM> {};
+template <typename OPSET, typename PARAM, typename ELEM>
+using cff2_cs_interpreter_t = cs_interpreter_t<cff2_cs_interp_env_t<ELEM>, OPSET, PARAM>;
 
 } /* namespace CFF */
 

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-font.cc
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-font.cc	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-font.cc	2022-05-21 02:44:01 UTC (rev 63353)
@@ -2596,12 +2596,14 @@
     return;
   }
 
+  /* Since we pass it to two destroying functions. */
+  trampoline_reference (&trampoline->closure);
+
   hb_font_funcs_set_nominal_glyph_func (ffuncs,
 					hb_font_get_nominal_glyph_trampoline,
 					trampoline,
 					trampoline_destroy);
 
-  trampoline_reference (&trampoline->closure);
   hb_font_funcs_set_variation_glyph_func (ffuncs,
 					  hb_font_get_variation_glyph_trampoline,
 					  trampoline,

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ft.cc
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ft.cc	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ft.cc	2022-05-21 02:44:01 UTC (rev 63353)
@@ -80,12 +80,12 @@
 
 struct hb_ft_font_t
 {
-  mutable hb_mutex_t lock;
-  FT_Face ft_face;
   int load_flags;
   bool symbol; /* Whether selected cmap is symbol cmap. */
   bool unref; /* Whether to destroy ft_face when done. */
 
+  mutable hb_mutex_t lock;
+  FT_Face ft_face;
   mutable int cached_x_scale;
   mutable hb_advance_cache_t advance_cache;
 };

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-map.cc
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-map.cc	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-map.cc	2022-05-21 02:44:01 UTC (rev 63353)
@@ -289,3 +289,23 @@
 {
   return map->get_population ();
 }
+
+/**
+ * hb_map_is_equal:
+ * @map: A map
+ * @other: Another map
+ *
+ * Tests whether @map and @other are equal (contain the same
+ * elements).
+ *
+ * Return value: %true if the two maps are equal, %false otherwise.
+ *
+ * Since: 4.3.0
+ **/
+hb_bool_t
+hb_map_is_equal (const hb_map_t *map,
+		 const hb_map_t *other)
+{
+  return map->is_equal (*other);
+}
+

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-map.h
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-map.h	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-map.h	2022-05-21 02:44:01 UTC (rev 63353)
@@ -91,6 +91,10 @@
 HB_EXTERN unsigned int
 hb_map_get_population (const hb_map_t *map);
 
+HB_EXTERN hb_bool_t
+hb_map_is_equal (const hb_map_t *map,
+		 const hb_map_t *other);
+
 HB_EXTERN void
 hb_map_set (hb_map_t       *map,
 	    hb_codepoint_t  key,

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-map.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-map.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-map.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -42,11 +42,12 @@
 struct hb_hashmap_t
 {
   hb_hashmap_t ()  { init (); }
+  hb_hashmap_t (std::nullptr_t) : hb_hashmap_t () {}
   ~hb_hashmap_t () { fini (); }
 
-  hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { hb_copy (o, *this); }
+  hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { resize (population); hb_copy (o, *this); }
   hb_hashmap_t (hb_hashmap_t&& o) : hb_hashmap_t () { hb_swap (*this, o); }
-  hb_hashmap_t& operator= (const hb_hashmap_t& o)  { hb_copy (o, *this); return *this; }
+  hb_hashmap_t& operator= (const hb_hashmap_t& o)  { resize (population); hb_copy (o, *this); return *this; }
   hb_hashmap_t& operator= (hb_hashmap_t&& o)  { hb_swap (*this, o); return *this; }
 
   hb_hashmap_t (std::initializer_list<hb_pair_t<K, V>> lst) : hb_hashmap_t ()
@@ -58,7 +59,10 @@
 	    hb_requires (hb_is_iterable (Iterable))>
   hb_hashmap_t (const Iterable &o) : hb_hashmap_t ()
   {
-    hb_copy (o, *this);
+    auto iter = hb_iter (o);
+    if (iter.is_random_access_iterator)
+      resize (hb_len (iter));
+    hb_copy (iter, *this);
   }
 
   struct item_t
@@ -154,11 +158,11 @@
 
   bool in_error () const { return !successful; }
 
-  bool resize ()
+  bool resize (unsigned new_population = 0)
   {
     if (unlikely (!successful)) return false;
 
-    unsigned int power = hb_bit_storage (population * 2 + 8);
+    unsigned int power = hb_bit_storage (hb_max (population, new_population) * 2 + 8);
     unsigned int new_size = 1u << power;
     item_t *new_items = (item_t *) hb_malloc ((size_t) new_size * sizeof (item_t));
     if (unlikely (!new_items))
@@ -235,6 +239,27 @@
   bool is_empty () const { return population == 0; }
   explicit operator bool () const { return !is_empty (); }
 
+  uint32_t hash () const
+  {
+    uint32_t h = 0;
+    for (auto pair : iter ())
+      h ^= (hb_hash (pair.first) * 31) + hb_hash (pair.second);
+    return h;
+  }
+
+  bool is_equal (const hb_hashmap_t &other) const
+  {
+    if (population != other.population) return false;
+
+    for (auto pair : iter ())
+      if (get (pair.first) != pair.second)
+        return false;
+
+    return true;
+  }
+  bool operator == (const hb_hashmap_t &other) const { return is_equal (other); }
+  bool operator != (const hb_hashmap_t &other) const { return !is_equal (other); }
+
   unsigned int get_population () const { return population; }
 
   /*
@@ -389,9 +414,11 @@
 			       HB_MAP_VALUE_INVALID,
 			       HB_MAP_VALUE_INVALID>;
 
-  hb_map_t () = default;
   ~hb_map_t () = default;
-  hb_map_t (hb_map_t&) = default;
+  hb_map_t () : hashmap () {}
+  hb_map_t (std::nullptr_t) : hb_map_t () {}
+  hb_map_t (const hb_map_t &o) : hashmap ((hashmap &) o) {}
+  hb_map_t (hb_map_t &&o) : hashmap (std::move ((hashmap &) o)) {}
   hb_map_t& operator= (const hb_map_t&) = default;
   hb_map_t& operator= (hb_map_t&&) = default;
   hb_map_t (std::initializer_list<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> lst) : hashmap (lst) {}

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-meta.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-meta.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-meta.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -188,6 +188,19 @@
 template <> struct hb_int_max<unsigned long long>	: hb_integral_constant<unsigned long long,	ULLONG_MAX>	{};
 #define hb_int_max(T) hb_int_max<T>::value
 
+#if __GNUG__ && __GNUC__ < 5
+#define hb_is_trivially_copyable(T) __has_trivial_copy(T)
+#define hb_is_trivially_copy_assignable(T) __has_trivial_assign(T)
+#define hb_is_trivially_constructible(T) __has_trivial_constructor(T)
+#define hb_is_trivially_copy_constructible(T) __has_trivial_copy_constructor(T)
+#define hb_is_trivially_destructible(T) __has_trivial_destructor(T)
+#else
+#define hb_is_trivially_copyable(T) std::is_trivially_copyable<T>::value
+#define hb_is_trivially_copy_assignable(T) std::is_trivially_copy_assignable<T>::value
+#define hb_is_trivially_constructible(T) std::is_trivially_constructible<T>::value
+#define hb_is_trivially_copy_constructible(T) std::is_trivially_copy_constructible<T>::value
+#define hb_is_trivially_destructible(T) std::is_trivially_destructible<T>::value
+#endif
 
 /* Class traits. */
 

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-open-type.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-open-type.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-open-type.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -33,6 +33,7 @@
 #include "hb-blob.hh"
 #include "hb-face.hh"
 #include "hb-machinery.hh"
+#include "hb-meta.hh"
 #include "hb-subset.hh"
 
 
@@ -518,7 +519,7 @@
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c, count))) return_trace (false);
-    if (!sizeof... (Ts) && std::is_trivially_copyable<Type>::value) return_trace (true);
+    if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true);
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!c->dispatch (arrayZ[i], std::forward<Ts> (ds)...)))
 	return_trace (false);
@@ -707,7 +708,7 @@
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return_trace (false);
-    if (!sizeof... (Ts) && std::is_trivially_copyable<Type>::value) return_trace (true);
+    if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true);
     unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!c->dispatch (arrayZ[i], std::forward<Ts> (ds)...)))
@@ -835,7 +836,7 @@
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return_trace (false);
-    if (!sizeof... (Ts) && std::is_trivially_copyable<Type>::value) return_trace (true);
+    if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true);
     unsigned int count = get_length ();
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!c->dispatch (arrayZ[i], std::forward<Ts> (ds)...)))
@@ -884,7 +885,7 @@
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return_trace (false);
-    if (!sizeof... (Ts) && std::is_trivially_copyable<Type>::value) return_trace (true);
+    if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true);
     unsigned int count = lenM1 + 1;
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!c->dispatch (arrayZ[i], std::forward<Ts> (ds)...)))
@@ -1070,7 +1071,7 @@
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return_trace (false);
-    if (!sizeof... (Ts) && std::is_trivially_copyable<Type>::value) return_trace (true);
+    if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true);
     unsigned int count = get_length ();
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!(*this)[i].sanitize (c, std::forward<Ts> (ds)...)))

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff-common.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff-common.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff-common.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -46,19 +46,6 @@
 static inline const Type& StructAtOffsetOrNull (const void *P, unsigned int offset)
 { return offset ? StructAtOffset<Type> (P, offset) : Null (Type); }
 
-inline unsigned int calcOffSize (unsigned int dataSize)
-{
-  unsigned int size = 1;
-  unsigned int offset = dataSize + 1;
-  while (offset & ~0xFF)
-  {
-    size++;
-    offset >>= 8;
-  }
-  /* format does not support size > 4; caller should handle it as an error */
-  return size;
-}
-
 struct code_pair_t
 {
   hb_codepoint_t code;
@@ -65,31 +52,16 @@
   hb_codepoint_t glyph;
 };
 
-typedef hb_vector_t<unsigned char> str_buff_t;
-struct str_buff_vec_t : hb_vector_t<str_buff_t>
-{
-  unsigned int total_size () const
-  {
-    unsigned int size = 0;
-    for (unsigned int i = 0; i < length; i++)
-      size += (*this)[i].length;
-    return size;
-  }
+using str_buff_t = hb_vector_t<unsigned char>;
+using str_buff_vec_t = hb_vector_t<str_buff_t>;
 
-  private:
-  typedef hb_vector_t<str_buff_t> SUPER;
-};
-
 /* CFF INDEX */
 template <typename COUNT>
 struct CFFIndex
 {
-  static unsigned int calculate_offset_array_size (unsigned int offSize, unsigned int count)
+  unsigned int offset_array_size () const
   { return offSize * (count + 1); }
 
-  unsigned int offset_array_size () const
-  { return calculate_offset_array_size (offSize, count); }
-
   CFFIndex *copy (hb_serialize_context_t *c) const
   {
     TRACE_SERIALIZE (this);
@@ -100,55 +72,46 @@
     return_trace (out);
   }
 
-  bool serialize (hb_serialize_context_t *c, const CFFIndex &src)
-  {
-    TRACE_SERIALIZE (this);
-    unsigned int size = src.get_size ();
-    CFFIndex *dest = c->allocate_size<CFFIndex> (size);
-    if (unlikely (!dest)) return_trace (false);
-    memcpy (dest, &src, size);
-    return_trace (true);
-  }
-
   bool serialize (hb_serialize_context_t *c,
 		  unsigned int offSize_,
 		  const byte_str_array_t &byteArray)
   {
     TRACE_SERIALIZE (this);
+
     if (byteArray.length == 0)
     {
       COUNT *dest = c->allocate_min<COUNT> ();
       if (unlikely (!dest)) return_trace (false);
       *dest = 0;
+      return_trace (true);
     }
-    else
+
+    /* serialize CFFIndex header */
+    if (unlikely (!c->extend_min (this))) return_trace (false);
+    this->count = byteArray.length;
+    this->offSize = offSize_;
+    if (unlikely (!c->allocate_size<HBUINT8> (offSize_ * (byteArray.length + 1))))
+      return_trace (false);
+
+    /* serialize indices */
+    unsigned int  offset = 1;
+    unsigned int  i = 0;
+    for (; i < byteArray.length; i++)
     {
-      /* serialize CFFIndex header */
-      if (unlikely (!c->extend_min (this))) return_trace (false);
-      this->count = byteArray.length;
-      this->offSize = offSize_;
-      if (unlikely (!c->allocate_size<HBUINT8> (offSize_ * (byteArray.length + 1))))
-	return_trace (false);
-
-      /* serialize indices */
-      unsigned int  offset = 1;
-      unsigned int  i = 0;
-      for (; i < byteArray.length; i++)
-      {
-	set_offset_at (i, offset);
-	offset += byteArray[i].get_size ();
-      }
       set_offset_at (i, offset);
+      offset += byteArray[i].get_size ();
+    }
+    set_offset_at (i, offset);
 
-      /* serialize data */
-      for (unsigned int i = 0; i < byteArray.length; i++)
-      {
-	const byte_str_t &bs = byteArray[i];
-	unsigned char *dest = c->allocate_size<unsigned char> (bs.length);
-	if (unlikely (!dest)) return_trace (false);
-	memcpy (dest, &bs[0], bs.length);
-      }
+    /* serialize data */
+    for (unsigned int i = 0; i < byteArray.length; i++)
+    {
+      const hb_ubytes_t &bs = byteArray[i];
+      unsigned char *dest = c->allocate_size<unsigned char> (bs.length);
+      if (unlikely (!dest)) return_trace (false);
+      memcpy (dest, &bs[0], bs.length);
     }
+
     return_trace (true);
   }
 
@@ -160,7 +123,7 @@
     byteArray.init ();
     byteArray.resize (buffArray.length);
     for (unsigned int i = 0; i < byteArray.length; i++)
-      byteArray[i] = byte_str_t (buffArray[i].arrayZ, buffArray[i].length);
+      byteArray[i] = hb_ubytes_t (buffArray[i].arrayZ, buffArray[i].length);
     bool result = this->serialize (c, offSize_, byteArray);
     byteArray.fini ();
     return result;
@@ -172,18 +135,9 @@
 		  Iterator it)
   {
     TRACE_SERIALIZE (this);
-    if (it.len () == 0)
-    {
-      COUNT *dest = c->allocate_min<COUNT> ();
-      if (unlikely (!dest)) return_trace (false);
-      *dest = 0;
-    }
-    else
-    {
-      serialize_header(c, + it | hb_map ([] (const byte_str_t &_) { return _.length; }));
-      for (const auto &_ : +it)
-	_.copy (c);
-    }
+    serialize_header(c, + it | hb_map ([] (const hb_ubytes_t &_) { return _.length; }));
+    for (const auto &_ : +it)
+      _.copy (c);
     return_trace (true);
   }
 
@@ -196,7 +150,7 @@
   {
     auto it =
     + hb_iter (buffArray)
-    | hb_map ([] (const str_buff_t &_) { return byte_str_t (_.arrayZ, _.length); })
+    | hb_map ([] (const str_buff_t &_) { return hb_ubytes_t (_.arrayZ, _.length); })
     ;
     return serialize (c, it);
   }
@@ -209,13 +163,15 @@
     TRACE_SERIALIZE (this);
 
     unsigned total = + it | hb_reduce (hb_add, 0);
-    unsigned off_size = calcOffSize (total);
+    unsigned off_size = (hb_bit_storage (total + 1) + 7) / 8;
 
     /* serialize CFFIndex header */
     if (unlikely (!c->extend_min (this))) return_trace (false);
     this->count = it.len ();
+    if (!this->count) return_trace (true);
+    if (unlikely (!c->extend (this->offSize))) return_trace (false);
     this->offSize = off_size;
-    if (unlikely (!c->allocate_size<HBUINT8> (off_size * (it.len () + 1))))
+    if (unlikely (!c->allocate_size<HBUINT8> (off_size * (this->count + 1))))
       return_trace (false);
 
     /* serialize indices */
@@ -233,6 +189,7 @@
 
   void set_offset_at (unsigned int index, unsigned int offset)
   {
+    assert (index <= count);
     HBUINT8 *p = offsets + offSize * index + offSize;
     unsigned int size = offSize;
     for (; size; size--)
@@ -243,11 +200,13 @@
     }
   }
 
+  private:
   unsigned int offset_at (unsigned int index) const
   {
     assert (index <= count);
-    const HBUINT8 *p = offsets + offSize * index;
+
     unsigned int size = offSize;
+    const HBUINT8 *p = offsets + size * index;
     unsigned int offset = 0;
     for (; size; size--)
       offset = (offset << 8) + *p++;
@@ -256,72 +215,57 @@
 
   unsigned int length_at (unsigned int index) const
   {
-    if (unlikely ((offset_at (index + 1) < offset_at (index)) ||
-		  (offset_at (index + 1) > offset_at (count))))
+    unsigned offset0 = offset_at (index);
+    unsigned offset1 = offset_at (index + 1);
+    if (unlikely (offset1 < offset0 || offset1 > offset_at (count)))
       return 0;
-    return offset_at (index + 1) - offset_at (index);
+    return offset1 - offset0;
   }
 
   const unsigned char *data_base () const
-  { return (const unsigned char *) this + min_size + offset_array_size (); }
+  { return (const unsigned char *) this + min_size + offSize.static_size + offset_array_size (); }
+  public:
 
-  unsigned int data_size () const { return HBINT8::static_size; }
-
-  byte_str_t operator [] (unsigned int index) const
+  hb_ubytes_t operator [] (unsigned int index) const
   {
-    if (unlikely (index >= count)) return Null (byte_str_t);
-    return byte_str_t (data_base () + offset_at (index) - 1, length_at (index));
+    if (unlikely (index >= count)) return hb_ubytes_t ();
+    unsigned length = length_at (index);
+    if (unlikely (!length)) return hb_ubytes_t ();
+    return hb_ubytes_t (data_base () + offset_at (index) - 1, length);
   }
 
   unsigned int get_size () const
   {
-    if (this == &Null (CFFIndex)) return 0;
-    if (count > 0)
-      return min_size + offset_array_size () + (offset_at (count) - 1);
-    return count.static_size;  /* empty CFFIndex contains count only */
+    if (count)
+      return min_size + offSize.static_size + offset_array_size () + (offset_at (count) - 1);
+    return min_size;  /* empty CFFIndex contains count only */
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely ((c->check_struct (this) && count == 0) || /* empty INDEX */
-			  (c->check_struct (this) && offSize >= 1 && offSize <= 4 &&
-			   c->check_array (offsets, offSize, count + 1) &&
-			   c->check_array ((const HBUINT8*) data_base (), 1, max_offset () - 1))));
+    return_trace (likely (c->check_struct (this) &&
+			  (count == 0 || /* empty INDEX */
+			   (count < count + 1u &&
+			    c->check_struct (&offSize) && offSize >= 1 && offSize <= 4 &&
+			    c->check_array (offsets, offSize, count + 1u) &&
+			    c->check_array ((const HBUINT8*) data_base (), 1, offset_at (count) - 1)))));
   }
 
-  protected:
-  unsigned int max_offset () const
-  {
-    unsigned int max = 0;
-    for (unsigned int i = 0; i < count + 1u; i++)
-    {
-      unsigned int off = offset_at (i);
-      if (off > max) max = off;
-    }
-    return max;
-  }
-
   public:
   COUNT		count;		/* Number of object data. Note there are (count+1) offsets */
+  private:
   HBUINT8	offSize;	/* The byte size of each offset in the offsets array. */
   HBUINT8	offsets[HB_VAR_ARRAY];
 				/* The array of (count + 1) offsets into objects array (1-base). */
   /* HBUINT8 data[HB_VAR_ARRAY];	Object data */
   public:
-  DEFINE_SIZE_ARRAY (COUNT::static_size + HBUINT8::static_size, offsets);
+  DEFINE_SIZE_MIN (COUNT::static_size);
 };
 
 template <typename COUNT, typename TYPE>
 struct CFFIndexOf : CFFIndex<COUNT>
 {
-  const byte_str_t operator [] (unsigned int index) const
-  {
-    if (likely (index < CFFIndex<COUNT>::count))
-      return byte_str_t (CFFIndex<COUNT>::data_base () + CFFIndex<COUNT>::offset_at (index) - 1, CFFIndex<COUNT>::length_at (index));
-    return Null (byte_str_t);
-  }
-
   template <typename DATA, typename PARAM1, typename PARAM2>
   bool serialize (hb_serialize_context_t *c,
 		  unsigned int offSize_,

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff1-table.cc
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff1-table.cc	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff1-table.cc	2022-05-21 02:44:01 UTC (rev 63353)
@@ -311,10 +311,8 @@
 
 struct cff1_extents_param_t
 {
-  void init (const OT::cff1::accelerator_t *_cff)
+  cff1_extents_param_t (const OT::cff1::accelerator_t *_cff) : cff (_cff)
   {
-    path_open = false;
-    cff = _cff;
     bounds.init ();
   }
 
@@ -322,7 +320,7 @@
   void end_path     ()       { path_open = false; }
   bool is_path_open () const { return path_open; }
 
-  bool path_open;
+  bool path_open = false;
   bounds_t bounds;
 
   const OT::cff1::accelerator_t *cff;
@@ -395,12 +393,11 @@
   if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false;
 
   unsigned int fd = cff->fdSelect->get_fd (glyph);
-  cff1_cs_interpreter_t<cff1_cs_opset_extents_t, cff1_extents_param_t> interp;
-  const byte_str_t str = (*cff->charStrings)[glyph];
-  interp.env.init (str, *cff, fd);
-  interp.env.set_in_seac (in_seac);
-  cff1_extents_param_t  param;
-  param.init (cff);
+  const hb_ubytes_t str = (*cff->charStrings)[glyph];
+  cff1_cs_interp_env_t env (str, *cff, fd);
+  env.set_in_seac (in_seac);
+  cff1_cs_interpreter_t<cff1_cs_opset_extents_t, cff1_extents_param_t> interp (env);
+  cff1_extents_param_t param (cff);
   if (unlikely (!interp.interpret (param))) return false;
   bounds = param.bounds;
   return true;
@@ -541,10 +538,10 @@
   if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false;
 
   unsigned int fd = cff->fdSelect->get_fd (glyph);
-  cff1_cs_interpreter_t<cff1_cs_opset_path_t, cff1_path_param_t> interp;
-  const byte_str_t str = (*cff->charStrings)[glyph];
-  interp.env.init (str, *cff, fd);
-  interp.env.set_in_seac (in_seac);
+  const hb_ubytes_t str = (*cff->charStrings)[glyph];
+  cff1_cs_interp_env_t env (str, *cff, fd);
+  env.set_in_seac (in_seac);
+  cff1_cs_interpreter_t<cff1_cs_opset_path_t, cff1_path_param_t> interp (env);
   cff1_path_param_t param (cff, font, draw_session, delta);
   if (unlikely (!interp.interpret (param))) return false;
 
@@ -566,18 +563,13 @@
 
 struct get_seac_param_t
 {
-  void init (const OT::cff1::accelerator_t *_cff)
-  {
-    cff = _cff;
-    base = 0;
-    accent = 0;
-  }
+  get_seac_param_t (const OT::cff1::accelerator_t *_cff) : cff (_cff) {}
 
   bool has_seac () const { return base && accent; }
 
   const OT::cff1::accelerator_t *cff;
-  hb_codepoint_t  base;
-  hb_codepoint_t  accent;
+  hb_codepoint_t  base = 0;
+  hb_codepoint_t  accent = 0;
 };
 
 struct cff1_cs_opset_seac_t : cff1_cs_opset_t<cff1_cs_opset_seac_t, get_seac_param_t>
@@ -598,11 +590,10 @@
   if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false;
 
   unsigned int fd = fdSelect->get_fd (glyph);
-  cff1_cs_interpreter_t<cff1_cs_opset_seac_t, get_seac_param_t> interp;
-  const byte_str_t str = (*charStrings)[glyph];
-  interp.env.init (str, *this, fd);
-  get_seac_param_t  param;
-  param.init (this);
+  const hb_ubytes_t str = (*charStrings)[glyph];
+  cff1_cs_interp_env_t env (str, *this, fd);
+  cff1_cs_interpreter_t<cff1_cs_opset_seac_t, get_seac_param_t> interp (env);
+  get_seac_param_t  param (this);
   if (unlikely (!interp.interpret (param))) return false;
 
   if (param.has_seac ())

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff1-table.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff1-table.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff1-table.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -318,8 +318,9 @@
     return_trace (c->check_struct (this) && sids[num_glyphs - 1].sanitize (c));
   }
 
-  hb_codepoint_t get_sid (hb_codepoint_t glyph) const
+  hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned num_glyphs) const
   {
+    if (unlikely (glyph >= num_glyphs)) return 0;
     if (glyph == 0)
       return 0;
     else
@@ -326,6 +327,12 @@
       return sids[glyph - 1];
   }
 
+  void collect_glyph_to_sid_map (hb_map_t *mapping, unsigned int num_glyphs) const
+  {
+    for (hb_codepoint_t gid = 1; gid < num_glyphs; gid++)
+      mapping->set (gid, sids[gid - 1]);
+  }
+
   hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const
   {
     if (sid == 0)
@@ -381,14 +388,15 @@
     return_trace (true);
   }
 
-  hb_codepoint_t get_sid (hb_codepoint_t glyph) const
+  hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned num_glyphs) const
   {
+    if (unlikely (glyph >= num_glyphs)) return 0;
     if (glyph == 0) return 0;
     glyph--;
     for (unsigned int i = 0;; i++)
     {
       if (glyph <= ranges[i].nLeft)
-	return (hb_codepoint_t)ranges[i].first + glyph;
+	return (hb_codepoint_t) ranges[i].first + glyph;
       glyph -= (ranges[i].nLeft + 1);
     }
 
@@ -395,6 +403,21 @@
     return 0;
   }
 
+  void collect_glyph_to_sid_map (hb_map_t *mapping, unsigned int num_glyphs) const
+  {
+    hb_codepoint_t gid = 1;
+    for (unsigned i = 0;; i++)
+    {
+      hb_codepoint_t sid = ranges[i].first;
+      unsigned count = ranges[i].nLeft + 1;
+      for (unsigned j = 0; j < count; j++)
+	mapping->set (gid++, sid++);
+
+      if (gid >= num_glyphs)
+        break;
+    }
+  }
+
   hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const
   {
     if (sid == 0) return 0;
@@ -521,16 +544,26 @@
 
   hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned int num_glyphs) const
   {
-    if (unlikely (glyph >= num_glyphs)) return 0;
     switch (format)
     {
-    case 0: return u.format0.get_sid (glyph);
-    case 1: return u.format1.get_sid (glyph);
-    case 2: return u.format2.get_sid (glyph);
+    case 0: return u.format0.get_sid (glyph, num_glyphs);
+    case 1: return u.format1.get_sid (glyph, num_glyphs);
+    case 2: return u.format2.get_sid (glyph, num_glyphs);
     default:return 0;
     }
   }
 
+  void collect_glyph_to_sid_map (hb_map_t *mapping, unsigned int num_glyphs) const
+  {
+    switch (format)
+    {
+    case 0: u.format0.collect_glyph_to_sid_map (mapping, num_glyphs); return;
+    case 1: u.format1.collect_glyph_to_sid_map (mapping, num_glyphs); return;
+    case 2: u.format2.collect_glyph_to_sid_map (mapping, num_glyphs); return;
+    default:return;
+    }
+  }
+
   hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const
   {
     switch (format)
@@ -602,6 +635,8 @@
 {
   cff1_top_dict_interp_env_t ()
     : num_interp_env_t(), prev_offset(0), last_offset(0) {}
+  cff1_top_dict_interp_env_t (const hb_ubytes_t &bytes)
+    : num_interp_env_t(bytes), prev_offset(0), last_offset(0) {}
 
   unsigned int prev_offset;
   unsigned int last_offset;
@@ -1024,11 +1059,10 @@
       { fini (); return; }
 
       { /* parse top dict */
-	const byte_str_t topDictStr = (*topDictIndex)[0];
+	const hb_ubytes_t topDictStr = (*topDictIndex)[0];
 	if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
-	cff1_top_dict_interpreter_t top_interp;
-	top_interp.env.init (topDictStr);
-	topDict.init ();
+	cff1_top_dict_interp_env_t env (topDictStr);
+	cff1_top_dict_interpreter_t top_interp (env);
 	if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
       }
 
@@ -1098,20 +1132,20 @@
       {
 	for (unsigned int i = 0; i < fdCount; i++)
 	{
-	  byte_str_t fontDictStr = (*fdArray)[i];
+	  hb_ubytes_t fontDictStr = (*fdArray)[i];
 	  if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; }
 	  cff1_font_dict_values_t *font;
-	  cff1_font_dict_interpreter_t font_interp;
-	  font_interp.env.init (fontDictStr);
+	  cff1_top_dict_interp_env_t env (fontDictStr);
+	  cff1_font_dict_interpreter_t font_interp (env);
 	  font = fontDicts.push ();
 	  if (unlikely (font == &Crap (cff1_font_dict_values_t))) { fini (); return; }
 	  font->init ();
 	  if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
 	  PRIVDICTVAL *priv = &privateDicts[i];
-	  const byte_str_t privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size);
+	  const hb_ubytes_t privDictStr = StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size);
 	  if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
-	  dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp;
-	  priv_interp.env.init (privDictStr);
+	  num_interp_env_t env2 (privDictStr);
+	  dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp (env2);
 	  priv->init ();
 	  if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
 
@@ -1126,10 +1160,10 @@
 	cff1_top_dict_values_t *font = &topDict;
 	PRIVDICTVAL *priv = &privateDicts[0];
 
-	const byte_str_t privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size);
+	const hb_ubytes_t privDictStr = StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size);
 	if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
-	dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp;
-	priv_interp.env.init (privDictStr);
+	num_interp_env_t env (privDictStr);
+	dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp (env);
 	priv->init ();
 	if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
 
@@ -1194,6 +1228,19 @@
       }
     }
 
+    hb_map_t *create_glyph_to_sid_map () const
+    {
+      if (charset != &Null (Charset))
+      {
+	hb_map_t *mapping = hb_map_create ();
+	mapping->set (0, 0);
+	charset->collect_glyph_to_sid_map (mapping, num_glyphs);
+	return mapping;
+      }
+      else
+	return nullptr;
+    }
+
     hb_codepoint_t glyph_to_sid (hb_codepoint_t glyph) const
     {
       if (charset != &Null (Charset))
@@ -1274,30 +1321,20 @@
     {
       SUPER::init (face);
 
+      glyph_names.set_relaxed (nullptr);
+
       if (!is_valid ()) return;
       if (is_CID ()) return;
 
-      /* fill glyph_names */
-      for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++)
-      {
-	hb_codepoint_t	sid = glyph_to_sid (gid);
-	gname_t	gname;
-	gname.sid = sid;
-	if (sid < cff1_std_strings_length)
-	  gname.name = cff1_std_strings (sid);
-	else
-	{
-	  byte_str_t	ustr = (*stringIndex)[sid - cff1_std_strings_length];
-	  gname.name = hb_bytes_t ((const char*)ustr.arrayZ, ustr.length);
-	}
-	if (unlikely (!gname.name.arrayZ)) { fini (); return; }
-	glyph_names.push (gname);
-      }
-      glyph_names.qsort ();
     }
     ~accelerator_t ()
     {
-      glyph_names.fini ();
+      hb_sorted_vector_t<gname_t> *names = glyph_names.get_relaxed ();
+      if (names)
+      {
+	names->fini ();
+	free (names);
+      }
 
       SUPER::fini ();
     }
@@ -1305,9 +1342,9 @@
     bool get_glyph_name (hb_codepoint_t glyph,
 			 char *buf, unsigned int buf_len) const
     {
-      if (!buf) return true;
       if (unlikely (!is_valid ())) return false;
       if (is_CID()) return false;
+      if (unlikely (!buf_len)) return true;
       hb_codepoint_t sid = glyph_to_sid (glyph);
       const char *str;
       size_t str_len;
@@ -1319,7 +1356,7 @@
       }
       else
       {
-	byte_str_t ubyte_str = (*stringIndex)[sid - cff1_std_strings_length];
+	hb_ubytes_t ubyte_str = (*stringIndex)[sid - cff1_std_strings_length];
 	str = (const char *)ubyte_str.arrayZ;
 	str_len = ubyte_str.length;
       }
@@ -1333,11 +1370,53 @@
     bool get_glyph_from_name (const char *name, int len,
 			      hb_codepoint_t *glyph) const
     {
+      if (unlikely (!is_valid ())) return false;
+      if (is_CID()) return false;
       if (len < 0) len = strlen (name);
       if (unlikely (!len)) return false;
 
+    retry:
+      hb_sorted_vector_t<gname_t> *names = glyph_names.get ();
+      if (unlikely (!names))
+      {
+	names = (hb_sorted_vector_t<gname_t> *) calloc (sizeof (hb_sorted_vector_t<gname_t>), 1);
+	if (likely (names))
+	{
+	  names->init ();
+	  /* TODO */
+
+	  /* fill glyph names */
+	  for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++)
+	  {
+	    hb_codepoint_t	sid = glyph_to_sid (gid);
+	    gname_t	gname;
+	    gname.sid = sid;
+	    if (sid < cff1_std_strings_length)
+	      gname.name = cff1_std_strings (sid);
+	    else
+	    {
+	      hb_ubytes_t	ustr = (*stringIndex)[sid - cff1_std_strings_length];
+	      gname.name = hb_bytes_t ((const char*) ustr.arrayZ, ustr.length);
+	    }
+	    if (unlikely (!gname.name.arrayZ))
+	      gname.name = hb_bytes_t ("", 0); /* To avoid nullptr. */
+	    names->push (gname);
+	  }
+	  names->qsort ();
+	}
+	if (unlikely (!glyph_names.cmpexch (nullptr, names)))
+	{
+	  if (names)
+	  {
+	    names->fini ();
+	    free (names);
+	  }
+	  goto retry;
+	}
+       }
+
       gname_t key = { hb_bytes_t (name, len), 0 };
-      const gname_t *gname = glyph_names.bsearch (key);
+      const gname_t *gname = glyph_names->bsearch (key);
       if (!gname) return false;
       hb_codepoint_t gid = sid_to_glyph (gname->sid);
       if (!gid && gname->sid) return false;
@@ -1359,7 +1438,7 @@
       {
 	const gname_t *a = (const gname_t *)a_;
 	const gname_t *b = (const gname_t *)b_;
-	int minlen = hb_min (a->name.length, b->name.length);
+	unsigned minlen = hb_min (a->name.length, b->name.length);
 	int ret = strncmp (a->name.arrayZ, b->name.arrayZ, minlen);
 	if (ret) return ret;
 	return a->name.length - b->name.length;
@@ -1368,7 +1447,7 @@
       int cmp (const gname_t &a) const { return cmp (&a, this); }
     };
 
-    hb_sorted_vector_t<gname_t>	glyph_names;
+    mutable hb_atomic_ptr_t<hb_sorted_vector_t<gname_t>> glyph_names;
 
     typedef accelerator_templ_t<cff1_private_dict_opset_t, cff1_private_dict_values_t> SUPER;
   };

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff2-table.cc
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff2-table.cc	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff2-table.cc	2022-05-21 02:44:01 UTC (rev 63353)
@@ -36,9 +36,8 @@
 
 struct cff2_extents_param_t
 {
-  void init ()
+  cff2_extents_param_t ()
   {
-    path_open = false;
     min_x.set_int (INT_MAX);
     min_y.set_int (INT_MAX);
     max_x.set_int (INT_MIN);
@@ -57,7 +56,7 @@
     if (pt.y > max_y) max_y = pt.y;
   }
 
-  bool  path_open;
+  bool  path_open = false;
   number_t min_x;
   number_t min_y;
   number_t max_x;
@@ -64,15 +63,15 @@
   number_t max_y;
 };
 
-struct cff2_path_procs_extents_t : path_procs_t<cff2_path_procs_extents_t, cff2_cs_interp_env_t, cff2_extents_param_t>
+struct cff2_path_procs_extents_t : path_procs_t<cff2_path_procs_extents_t, cff2_cs_interp_env_t<number_t>, cff2_extents_param_t>
 {
-  static void moveto (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt)
+  static void moveto (cff2_cs_interp_env_t<number_t> &env, cff2_extents_param_t& param, const point_t &pt)
   {
     param.end_path ();
     env.moveto (pt);
   }
 
-  static void line (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt1)
+  static void line (cff2_cs_interp_env_t<number_t> &env, cff2_extents_param_t& param, const point_t &pt1)
   {
     if (!param.is_path_open ())
     {
@@ -83,7 +82,7 @@
     param.update_bounds (env.get_pt ());
   }
 
-  static void curve (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3)
+  static void curve (cff2_cs_interp_env_t<number_t> &env, cff2_extents_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3)
   {
     if (!param.is_path_open ())
     {
@@ -98,7 +97,7 @@
   }
 };
 
-struct cff2_cs_opset_extents_t : cff2_cs_opset_t<cff2_cs_opset_extents_t, cff2_extents_param_t, cff2_path_procs_extents_t> {};
+struct cff2_cs_opset_extents_t : cff2_cs_opset_t<cff2_cs_opset_extents_t, cff2_extents_param_t, number_t, cff2_path_procs_extents_t> {};
 
 bool OT::cff2::accelerator_t::get_extents (hb_font_t *font,
 					   hb_codepoint_t glyph,
@@ -112,11 +111,10 @@
   if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false;
 
   unsigned int fd = fdSelect->get_fd (glyph);
-  cff2_cs_interpreter_t<cff2_cs_opset_extents_t, cff2_extents_param_t> interp;
-  const byte_str_t str = (*charStrings)[glyph];
-  interp.env.init (str, *this, fd, font->coords, font->num_coords);
+  const hb_ubytes_t str = (*charStrings)[glyph];
+  cff2_cs_interp_env_t<number_t> env (str, *this, fd, font->coords, font->num_coords);
+  cff2_cs_interpreter_t<cff2_cs_opset_extents_t, cff2_extents_param_t, number_t> interp (env);
   cff2_extents_param_t  param;
-  param.init ();
   if (unlikely (!interp.interpret (param))) return false;
 
   if (param.min_x >= param.max_x)
@@ -169,21 +167,21 @@
   hb_font_t *font;
 };
 
-struct cff2_path_procs_path_t : path_procs_t<cff2_path_procs_path_t, cff2_cs_interp_env_t, cff2_path_param_t>
+struct cff2_path_procs_path_t : path_procs_t<cff2_path_procs_path_t, cff2_cs_interp_env_t<number_t>, cff2_path_param_t>
 {
-  static void moveto (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt)
+  static void moveto (cff2_cs_interp_env_t<number_t> &env, cff2_path_param_t& param, const point_t &pt)
   {
     param.move_to (pt);
     env.moveto (pt);
   }
 
-  static void line (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1)
+  static void line (cff2_cs_interp_env_t<number_t> &env, cff2_path_param_t& param, const point_t &pt1)
   {
     param.line_to (pt1);
     env.moveto (pt1);
   }
 
-  static void curve (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3)
+  static void curve (cff2_cs_interp_env_t<number_t> &env, cff2_path_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3)
   {
     param.cubic_to (pt1, pt2, pt3);
     env.moveto (pt3);
@@ -190,7 +188,7 @@
   }
 };
 
-struct cff2_cs_opset_path_t : cff2_cs_opset_t<cff2_cs_opset_path_t, cff2_path_param_t, cff2_path_procs_path_t> {};
+struct cff2_cs_opset_path_t : cff2_cs_opset_t<cff2_cs_opset_path_t, cff2_path_param_t, number_t, cff2_path_procs_path_t> {};
 
 bool OT::cff2::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const
 {
@@ -202,9 +200,9 @@
   if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false;
 
   unsigned int fd = fdSelect->get_fd (glyph);
-  cff2_cs_interpreter_t<cff2_cs_opset_path_t, cff2_path_param_t> interp;
-  const byte_str_t str = (*charStrings)[glyph];
-  interp.env.init (str, *this, fd, font->coords, font->num_coords);
+  const hb_ubytes_t str = (*charStrings)[glyph];
+  cff2_cs_interp_env_t<number_t> env (str, *this, fd, font->coords, font->num_coords);
+  cff2_cs_interpreter_t<cff2_cs_opset_path_t, cff2_path_param_t, number_t> interp (env);
   cff2_path_param_t param (font, draw_session);
   if (unlikely (!interp.interpret (param))) return false;
   return true;

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff2-table.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff2-table.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cff2-table.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -247,12 +247,8 @@
 
 struct cff2_priv_dict_interp_env_t : num_interp_env_t
 {
-  void init (const byte_str_t &str)
-  {
-    num_interp_env_t::init (str);
-    ivs = 0;
-    seen_vsindex = false;
-  }
+  cff2_priv_dict_interp_env_t (const hb_ubytes_t &str) :
+    num_interp_env_t (str) {}
 
   void process_vsindex ()
   {
@@ -267,8 +263,8 @@
   void	 set_ivs (unsigned int ivs_) { ivs = ivs_; }
 
   protected:
-  unsigned int  ivs;
-  bool	  seen_vsindex;
+  unsigned int  ivs = 0;
+  bool	  seen_vsindex = false;
 };
 
 struct cff2_private_dict_opset_t : dict_opset_t
@@ -415,10 +411,10 @@
         goto fail;
 
       { /* parse top dict */
-	byte_str_t topDictStr (cff2 + cff2->topDict, cff2->topDictSize);
+	hb_ubytes_t topDictStr = (cff2 + cff2->topDict).as_ubytes (cff2->topDictSize);
 	if (unlikely (!topDictStr.sanitize (&sc))) goto fail;
-	cff2_top_dict_interpreter_t top_interp;
-	top_interp.env.init (topDictStr);
+	num_interp_env_t env (topDictStr);
+	cff2_top_dict_interpreter_t top_interp (env);
 	topDict.init ();
 	if (unlikely (!top_interp.interpret (topDict))) goto fail;
       }
@@ -447,20 +443,20 @@
       /* parse font dicts and gather private dicts */
       for (unsigned int i = 0; i < fdCount; i++)
       {
-	const byte_str_t fontDictStr = (*fdArray)[i];
+	const hb_ubytes_t fontDictStr = (*fdArray)[i];
 	if (unlikely (!fontDictStr.sanitize (&sc))) goto fail;
 	cff2_font_dict_values_t  *font;
-	cff2_font_dict_interpreter_t font_interp;
-	font_interp.env.init (fontDictStr);
+	num_interp_env_t env (fontDictStr);
+	cff2_font_dict_interpreter_t font_interp (env);
 	font = fontDicts.push ();
 	if (unlikely (font == &Crap (cff2_font_dict_values_t))) goto fail;
 	font->init ();
 	if (unlikely (!font_interp.interpret (*font))) goto fail;
 
-	const byte_str_t privDictStr (StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset), font->privateDictInfo.size);
+	const hb_ubytes_t privDictStr = StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size);
 	if (unlikely (!privDictStr.sanitize (&sc))) goto fail;
-	dict_interpreter_t<PRIVOPSET, PRIVDICTVAL, cff2_priv_dict_interp_env_t>  priv_interp;
-	priv_interp.env.init(privDictStr);
+	cff2_priv_dict_interp_env_t env2 (privDictStr);
+	dict_interpreter_t<PRIVOPSET, PRIVDICTVAL, cff2_priv_dict_interp_env_t> priv_interp (env2);
 	privateDicts[i].init ();
 	if (unlikely (!priv_interp.interpret (privateDicts[i]))) goto fail;
 

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cmap-table.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cmap-table.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-cmap-table.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -44,7 +44,7 @@
   bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
   {
     hb_codepoint_t gid = codepoint < 256 ? glyphIdArray[codepoint] : 0;
-    if (!gid)
+    if (unlikely (!gid))
       return false;
     *glyph = gid;
     return true;
@@ -109,22 +109,26 @@
 
     while (it) {
       // Start a new range
-      start_cp = (*it).first;
-      prev_run_start_cp = (*it).first;
-      run_start_cp = (*it).first;
-      end_cp = (*it).first;
-      last_gid = (*it).second;
-      run_length = 1;
-      prev_delta = 0;
+      {
+        const auto& pair = *it;
+        start_cp = pair.first;
+        prev_run_start_cp = start_cp;
+        run_start_cp = start_cp;
+        end_cp = start_cp;
+        last_gid = pair.second;
+        run_length = 1;
+        prev_delta = 0;
+      }
 
-      delta = (*it).second - (*it).first;
+      delta = last_gid - start_cp;
       mode = FIRST_SUB_RANGE;
       it++;
 
       while (it) {
         // Process range
-        hb_codepoint_t next_cp = (*it).first;
-        hb_codepoint_t next_gid = (*it).second;
+        const auto& pair = *it;
+        hb_codepoint_t next_cp = pair.first;
+        hb_codepoint_t next_gid = pair.second;
         if (next_cp != end_cp + 1) {
           // Current range is over, stop processing.
           break;
@@ -282,16 +286,15 @@
   }
 
   template<typename Iterator,
-	   hb_requires (hb_is_iterator (Iterator))>
+          hb_requires (hb_is_iterator (Iterator))>
   HBUINT16* serialize_rangeoffset_glyid (hb_serialize_context_t *c,
-					 Iterator it,
+                                         Iterator it,
 					 HBUINT16 *endCode,
 					 HBUINT16 *startCode,
 					 HBINT16 *idDelta,
 					 unsigned segcount)
   {
-    hb_hashmap_t<hb_codepoint_t, hb_codepoint_t> cp_to_gid;
-    + it | hb_sink (cp_to_gid);
+    hb_map_t cp_to_gid { it };
 
     HBUINT16 *idRangeOffset = c->allocate_size<HBUINT16> (HBUINT16::static_size * segcount);
     if (unlikely (!c->check_success (idRangeOffset))) return nullptr;
@@ -298,7 +301,7 @@
     if (unlikely ((char *)idRangeOffset - (char *)idDelta != (int) segcount * (int) HBINT16::static_size)) return nullptr;
 
     for (unsigned i : + hb_range (segcount)
-             | hb_filter ([&] (const unsigned _) { return idDelta[_] == 0; }))
+		      | hb_filter ([&] (const unsigned _) { return idDelta[_] == 0; }))
     {
       idRangeOffset[i] = 2 * (c->start_embed<HBUINT16> () - idRangeOffset - i);
       for (hb_codepoint_t cp = startCode[i]; cp <= endCode[i]; cp++)
@@ -323,22 +326,31 @@
 		 { return _.first <= 0xFFFF; })
     ;
 
-    if (format4_iter.len () == 0) return;
+    if (!format4_iter) return;
 
     unsigned table_initpos = c->length ();
     if (unlikely (!c->extend_min (this))) return;
     this->format = 4;
 
+    hb_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> cp_to_gid {
+      format4_iter
+    };
+
     //serialize endCode[], startCode[], idDelta[]
     HBUINT16* endCode = c->start_embed<HBUINT16> ();
-    unsigned segcount = serialize_find_segcount (format4_iter);
-    if (unlikely (!serialize_start_end_delta_arrays (c, format4_iter, segcount)))
+    unsigned segcount = serialize_find_segcount (cp_to_gid.iter());
+    if (unlikely (!serialize_start_end_delta_arrays (c, cp_to_gid.iter(), segcount)))
       return;
 
     HBUINT16 *startCode = endCode + segcount + 1;
     HBINT16 *idDelta = ((HBINT16*)startCode) + segcount;
 
-    HBUINT16 *idRangeOffset = serialize_rangeoffset_glyid (c, format4_iter, endCode, startCode, idDelta, segcount);
+    HBUINT16 *idRangeOffset = serialize_rangeoffset_glyid (c,
+                                                           cp_to_gid.iter (),
+                                                           endCode,
+                                                           startCode,
+                                                           idDelta,
+                                                           segcount);
     if (unlikely (!c->check_success (idRangeOffset))) return;
 
     this->length = c->length () - table_initpos;
@@ -401,7 +413,7 @@
 					  2,
 					  _hb_cmp_method<hb_codepoint_t, CustomRange, unsigned>,
 					  this->segCount + 1);
-      if (!found)
+      if (unlikely (!found))
 	return false;
       unsigned int i = found - endCount;
 
@@ -421,7 +433,7 @@
 	gid += this->idDelta[i];
       }
       gid &= 0xFFFFu;
-      if (!gid)
+      if (unlikely (!gid))
 	return false;
       *glyph = gid;
       return true;
@@ -440,6 +452,7 @@
 	hb_codepoint_t start = this->startCount[i];
 	hb_codepoint_t end = this->endCount[i];
 	unsigned int rangeOffset = this->idRangeOffset[i];
+        out->add_range(start, end);
 	if (rangeOffset == 0)
 	{
 	  for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++)
@@ -446,8 +459,7 @@
 	  {
 	    hb_codepoint_t gid = (codepoint + this->idDelta[i]) & 0xFFFFu;
 	    if (unlikely (!gid))
-	      continue;
-	    out->add (codepoint);
+              out->del(codepoint);
 	  }
 	}
 	else
@@ -456,11 +468,13 @@
 	  {
 	    unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount;
 	    if (unlikely (index >= this->glyphIdArrayLength))
+            {
+              out->del_range (codepoint, end);
 	      break;
+            }
 	    hb_codepoint_t gid = this->glyphIdArray[index];
 	    if (unlikely (!gid))
-	      continue;
-	    out->add (codepoint);
+              out->del(codepoint);
 	  }
 	}
       }
@@ -469,6 +483,8 @@
     void collect_mapping (hb_set_t *unicodes, /* OUT */
 			  hb_map_t *mapping /* OUT */) const
     {
+      // TODO(grieger): optimize similar to collect_unicodes
+      // (ie. use add_range())
       unsigned count = this->segCount;
       if (count && this->startCount[count - 1] == 0xFFFFu)
 	count--; /* Skip sentinel segment. */
@@ -620,7 +636,7 @@
   {
     /* Rely on our implicit array bound-checking. */
     hb_codepoint_t gid = glyphIdArray[codepoint - startCharCode];
-    if (!gid)
+    if (unlikely (!gid))
       return false;
     *glyph = gid;
     return true;
@@ -674,7 +690,7 @@
 };
 
 struct CmapSubtableFormat6  : CmapSubtableTrimmed<HBUINT16> {};
-struct CmapSubtableFormat10 : CmapSubtableTrimmed<HBUINT32 > {};
+struct CmapSubtableFormat10 : CmapSubtableTrimmed<HBUINT32> {};
 
 template <typename T>
 struct CmapSubtableLongSegmented
@@ -684,7 +700,7 @@
   bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
   {
     hb_codepoint_t gid = T::group_get_glyph (groups.bsearch (codepoint), codepoint);
-    if (!gid)
+    if (unlikely (!gid))
       return false;
     *glyph = gid;
     return true;
@@ -722,11 +738,19 @@
 			hb_map_t *mapping, /* OUT */
 			unsigned num_glyphs) const
   {
+    hb_codepoint_t last_end = 0;
     for (unsigned i = 0; i < this->groups.len; i++)
     {
       hb_codepoint_t start = this->groups[i].startCharCode;
       hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode,
 				   (hb_codepoint_t) HB_UNICODE_MAX);
+      if (unlikely (start > end || start < last_end)) {
+        // Range is not in order and is invalid, skip it.
+        continue;
+      }
+      last_end = end;
+
+
       hb_codepoint_t gid = this->groups[i].glyphID;
       if (!gid)
       {
@@ -778,16 +802,16 @@
   void serialize (hb_serialize_context_t *c,
 		  Iterator it)
   {
-    if (it.len () == 0) return;
+    if (!it) return;
     unsigned table_initpos = c->length ();
     if (unlikely (!c->extend_min (this))) return;
 
-    hb_codepoint_t startCharCode = 0xFFFF, endCharCode = 0xFFFF;
+    hb_codepoint_t startCharCode = (hb_codepoint_t) -1, endCharCode = (hb_codepoint_t) -1;
     hb_codepoint_t glyphID = 0;
 
     for (const auto& _ : +it)
     {
-      if (startCharCode == 0xFFFF)
+      if (startCharCode == (hb_codepoint_t) -1)
       {
 	startCharCode = _.first;
 	endCharCode = _.first;
@@ -818,7 +842,7 @@
     this->format = 12;
     this->reserved = 0;
     this->length = c->length () - table_initpos;
-    this->groups.len = (this->length - min_size)/CmapSubtableLongGroup::static_size;
+    this->groups.len = (this->length - min_size) / CmapSubtableLongGroup::static_size;
   }
 
   static size_t get_sub_table_size (const hb_sorted_vector_t<CmapSubtableLongGroup> &groups_data)
@@ -1448,6 +1472,37 @@
   DEFINE_SIZE_STATIC (8);
 };
 
+struct SubtableUnicodesCache {
+
+ private:
+  const void* base;
+  hb_hashmap_t<intptr_t, hb_set_t*> cached_unicodes;
+
+ public:
+  SubtableUnicodesCache(const void* cmap_base)
+      : base(cmap_base), cached_unicodes() {}
+  ~SubtableUnicodesCache()
+  {
+    for (hb_set_t* s : cached_unicodes.values()) {
+      hb_set_destroy (s);
+    }
+  }
+
+  hb_set_t* set_for(const EncodingRecord* record)
+  {
+    if (!cached_unicodes.has ((intptr_t) record)) {
+      hb_set_t* new_set = hb_set_create ();
+      if (!cached_unicodes.set ((intptr_t) record, new_set)) {
+        hb_set_destroy (new_set);
+        return hb_set_get_empty ();
+      }
+      (base+record->subtable).collect_unicodes (cached_unicodes.get ((intptr_t) record));
+    }
+    return cached_unicodes.get ((intptr_t) record);
+  }
+
+};
+
 struct cmap
 {
   static constexpr hb_tag_t tableTag = HB_OT_TAG_cmap;
@@ -1467,6 +1522,7 @@
     unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0;
     auto snap = c->snapshot ();
 
+    SubtableUnicodesCache unicodes_cache (base);
     for (const EncodingRecord& _ : encodingrec_iter)
     {
       if (c->in_error ())
@@ -1475,12 +1531,11 @@
       unsigned format = (base+_.subtable).u.format;
       if (format != 4 && format != 12 && format != 14) continue;
 
-      hb_set_t unicodes_set;
-      (base+_.subtable).collect_unicodes (&unicodes_set);
+      hb_set_t* unicodes_set = unicodes_cache.set_for (&_);
 
       if (!drop_format_4 && format == 4)
       {
-        c->copy (_, + it | hb_filter (unicodes_set, hb_first), 4u, base, plan, &format4objidx);
+        c->copy (_, + it | hb_filter (*unicodes_set, hb_first), 4u, base, plan, &format4objidx);
         if (c->in_error () && c->only_overflow ())
         {
           // cmap4 overflowed, reset and retry serialization without format 4 subtables.
@@ -1495,8 +1550,8 @@
 
       else if (format == 12)
       {
-        if (_can_drop (_, unicodes_set, base, + it | hb_map (hb_first), encodingrec_iter)) continue;
-        c->copy (_, + it | hb_filter (unicodes_set, hb_first), 12u, base, plan, &format12objidx);
+        if (_can_drop (_, *unicodes_set, base, unicodes_cache, + it | hb_map (hb_first), encodingrec_iter)) continue;
+        c->copy (_, + it | hb_filter (*unicodes_set, hb_first), 12u, base, plan, &format12objidx);
       }
       else if (format == 14) c->copy (_, it, 14u, base, plan, &format14objidx);
     }
@@ -1514,6 +1569,7 @@
   bool _can_drop (const EncodingRecord& cmap12,
                   const hb_set_t& cmap12_unicodes,
                   const void* base,
+                  SubtableUnicodesCache& unicodes_cache,
                   Iterator subset_unicodes,
                   EncodingRecordIterator encoding_records)
   {
@@ -1544,11 +1600,10 @@
           || (base+_.subtable).get_language() != target_language)
         continue;
 
-      hb_set_t sibling_unicodes;
-      (base+_.subtable).collect_unicodes (&sibling_unicodes);
+      hb_set_t* sibling_unicodes = unicodes_cache.set_for (&_);
 
       auto cmap12 = + subset_unicodes | hb_filter (cmap12_unicodes);
-      auto sibling = + subset_unicodes | hb_filter (sibling_unicodes);
+      auto sibling = + subset_unicodes | hb_filter (*sibling_unicodes);
       for (; cmap12 && sibling; cmap12++, sibling++)
       {
         unsigned a = *cmap12;
@@ -1616,13 +1671,7 @@
     if (unlikely (has_format12 && (!unicode_ucs4 && !ms_ucs4))) return_trace (false);
 
     auto it =
-    + hb_iter (c->plan->unicodes)
-    | hb_map ([&] (hb_codepoint_t _)
-	      {
-		hb_codepoint_t new_gid = HB_MAP_VALUE_INVALID;
-		c->plan->new_gid_for_codepoint (_, &new_gid);
-		return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (_, new_gid);
-	      })
+    + c->plan->unicode_to_new_gid_list.iter ()
     | hb_filter ([&] (const hb_pair_t<hb_codepoint_t, hb_codepoint_t> _)
 		 { return (_.second != HB_MAP_VALUE_INVALID); })
     ;

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-color-cpal-table.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-color-cpal-table.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-color-cpal-table.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -197,30 +197,38 @@
 
   public:
   bool serialize (hb_serialize_context_t *c,
+                  const hb_array_t<const HBUINT16> &color_record_indices,
                   const hb_array_t<const BGRAColor> &color_records,
-                  const hb_array_t<const HBUINT16> &color_record_indices,
-                  const hb_map_t &color_record_index_map,
-                  const hb_set_t &retained_color_record_indices) const
+                  const hb_vector_t<unsigned>& first_color_index_for_layer,
+                  const hb_map_t& first_color_to_layer_index,
+                  const hb_set_t &retained_color_indices) const
   {
     TRACE_SERIALIZE (this);
 
+    // TODO(grieger): limit total final size.
+
     for (const auto idx : color_record_indices)
     {
+      hb_codepoint_t layer_index = first_color_to_layer_index[idx];
+
       HBUINT16 new_idx;
-      if (idx == 0) new_idx = 0;
-      else new_idx = color_record_index_map.get (idx);
+      new_idx = layer_index * retained_color_indices.get_population ();
       if (!c->copy<HBUINT16> (new_idx)) return_trace (false);
     }
 
     c->push ();
-    for (const auto _ : retained_color_record_indices.iter ())
+    for (unsigned first_color_index : first_color_index_for_layer)
     {
-      if (!c->copy<BGRAColor> (color_records[_]))
+      for (hb_codepoint_t color_index : retained_color_indices)
       {
-        c->pop_discard ();
-        return_trace (false);
+        if (!c->copy<BGRAColor> (color_records[first_color_index + color_index]))
+        {
+          c->pop_discard ();
+          return_trace (false);
+        }
       }
     }
+
     c->add_link (colorRecordsZ, c->pop_pack ());
     return_trace (true);
   }
@@ -228,6 +236,8 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
+    if (!numPalettes) return_trace (false);
+
     const hb_map_t *color_index_map = c->plan->colr_palettes;
     if (color_index_map->is_empty ()) return_trace (false);
 
@@ -242,30 +252,34 @@
     auto *out = c->serializer->start_embed (*this);
     if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
 
+
     out->version = version;
     out->numColors = retained_color_indices.get_population ();
     out->numPalettes = numPalettes;
 
+    hb_vector_t<unsigned> first_color_index_for_layer;
+    hb_map_t first_color_to_layer_index;
+
     const hb_array_t<const HBUINT16> colorRecordIndices = colorRecordIndicesZ.as_array (numPalettes);
-    hb_map_t color_record_index_map;
-    hb_set_t retained_color_record_indices;
-
-    unsigned record_count = 0;
     for (const auto first_color_record_idx : colorRecordIndices)
     {
-      for (unsigned retained_color_idx : retained_color_indices.iter ())
-      {
-        unsigned color_record_idx = first_color_record_idx + retained_color_idx;
-        if (color_record_index_map.has (color_record_idx)) continue;
-        color_record_index_map.set (color_record_idx, record_count);
-        retained_color_record_indices.add (color_record_idx);
-        record_count++;
-      }
+      if (first_color_to_layer_index.has (first_color_record_idx)) continue;
+
+      first_color_index_for_layer.push (first_color_record_idx);
+      first_color_to_layer_index.set (first_color_record_idx,
+                                      first_color_index_for_layer.length - 1);
     }
 
-    out->numColorRecords = record_count;
+    out->numColorRecords = first_color_index_for_layer.length
+                           * retained_color_indices.get_population ();
+
     const hb_array_t<const BGRAColor> color_records = (this+colorRecordsZ).as_array (numColorRecords);
-    if (!out->serialize (c->serializer, color_records, colorRecordIndices, color_record_index_map, retained_color_record_indices))
+    if (!out->serialize (c->serializer,
+                         colorRecordIndices,
+                         color_records,
+                         first_color_index_for_layer,
+                         first_color_to_layer_index,
+                         retained_color_indices))
       return_trace (false);
 
     if (version == 1)

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-glyf-table.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-glyf-table.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-glyf-table.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -953,6 +953,8 @@
       glyf_table.destroy ();
     }
 
+    bool has_data () const { return num_glyphs; }
+
     protected:
     template<typename T>
     bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer) const

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-common.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-common.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-common.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -91,12 +91,12 @@
 static inline void ClassDef_serialize (hb_serialize_context_t *c,
 				       Iterator it);
 
-static void ClassDef_remap_and_serialize (hb_serialize_context_t *c,
-					  const hb_map_t &gid_klass_map,
-					  hb_sorted_vector_t<HBGlyphID16> &glyphs,
-					  const hb_set_t &klasses,
-					  bool use_class_zero,
-					  hb_map_t *klass_map /*INOUT*/);
+static void ClassDef_remap_and_serialize (
+    hb_serialize_context_t *c,
+    const hb_set_t &klasses,
+    bool use_class_zero,
+    hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> &glyph_and_klass, /* IN/OUT */
+    hb_map_t *klass_map /*IN/OUT*/);
 
 
 struct hb_prune_langsys_context_t
@@ -1470,7 +1470,8 @@
     void next () { i++; }
     hb_codepoint_t get_glyph () const { return c->glyphArray[i]; }
     bool operator != (const iter_t& o) const
-    { return i != o.i || c != o.c; }
+    { return i != o.i; }
+    iter_t __end__ () const { iter_t it; it.init (*c); it.i = c->glyphArray.len; return it; }
 
     private:
     const struct CoverageFormat1 *c;
@@ -1506,12 +1507,6 @@
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (this))) return_trace (false);
 
-    if (unlikely (!glyphs))
-    {
-      rangeRecord.len = 0;
-      return_trace (true);
-    }
-
     /* TODO(iter) Write more efficiently? */
 
     unsigned num_ranges = 0;
@@ -1524,6 +1519,7 @@
     }
 
     if (unlikely (!rangeRecord.serialize (c, num_ranges))) return_trace (false);
+    if (!num_ranges) return_trace (true);
 
     unsigned count = 0;
     unsigned range = (unsigned) -1;
@@ -1552,25 +1548,26 @@
 
   bool intersects (const hb_set_t *glyphs) const
   {
-    /* TODO Speed up, using hb_set_next() and bsearch()? */
-    /* TODO(iter) Rewrite as dagger. */
-    for (const auto& range : rangeRecord.as_array ())
-      if (range.intersects (glyphs))
-	return true;
-    return false;
+    return hb_any (+ hb_iter (rangeRecord.as_array ())
+		   | hb_map ([glyphs] (const RangeRecord &range) { return range.intersects (glyphs); }));
   }
   bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
   {
-    /* TODO(iter) Rewrite as dagger. */
-    for (const auto& range : rangeRecord.as_array ())
+    auto cmp = [] (const void *pk, const void *pr) -> int
     {
-      if (range.value <= index &&
-	  index < (unsigned int) range.value + (range.last - range.first) &&
-	  range.intersects (glyphs))
-	return true;
-      else if (index < range.value)
-	return false;
-    }
+      unsigned index = * (const unsigned *) pk;
+      const RangeRecord &range = * (const RangeRecord *) pr;
+      if (index < range.value) return -1;
+      if (index > (unsigned int) range.value + (range.last - range.first)) return +1;
+      return 0;
+    };
+
+    auto arr = rangeRecord.as_array ();
+    unsigned idx;
+    if (hb_bsearch_impl (&idx, index,
+			 arr.arrayZ, arr.length, sizeof (arr[0]),
+			 (int (*)(const void *_key, const void *_item)) cmp))
+      return arr.arrayZ[idx].intersects (glyphs);
     return false;
   }
 
@@ -1579,8 +1576,10 @@
     for (const auto& range : rangeRecord.as_array ())
     {
       if (!range.intersects (glyphs)) continue;
-      for (hb_codepoint_t g = range.first; g <= range.last; g++)
-        if (glyphs->has (g)) intersect_glyphs->add (g);
+      unsigned last = range.last;
+      for (hb_codepoint_t g = range.first - 1;
+	   glyphs->next (&g) && g <= last;)
+        intersect_glyphs->add (g);
     }
   }
 
@@ -1632,6 +1631,8 @@
 	   return;
 	  }
 	}
+	else
+	  j = 0;
 	return;
       }
       coverage++;
@@ -1639,7 +1640,15 @@
     }
     hb_codepoint_t get_glyph () const { return j; }
     bool operator != (const iter_t& o) const
-    { return i != o.i || j != o.j || c != o.c; }
+    { return i != o.i || j != o.j; }
+    iter_t __end__ () const
+    {
+      iter_t it;
+      it.init (*c);
+      it.i = c->rangeRecord.len;
+      it.j = 0;
+      return it;
+    }
 
     private:
     const struct CoverageFormat2 *c;
@@ -1708,18 +1717,17 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
-    const hb_map_t &glyph_map = *c->plan->glyph_map;
-
     auto it =
     + iter ()
-    | hb_filter (glyphset)
-    | hb_map_retains_sorting (glyph_map)
+    | hb_filter (c->plan->glyph_map_gsub)
+    | hb_map_retains_sorting (c->plan->glyph_map_gsub)
     ;
 
-    bool ret = bool (it);
-    Coverage_serialize (c->serializer, it);
-    return_trace (ret);
+    // Cache the iterator result as it will be iterated multiple times
+    // by the serialize code below.
+    hb_sorted_vector_t<hb_codepoint_t> glyphs (it);
+    Coverage_serialize (c->serializer, glyphs.iter ());
+    return_trace (bool (glyphs));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -1822,7 +1830,7 @@
     }
     bool operator != (const iter_t& o) const
     {
-      if (format != o.format) return true;
+      if (unlikely (format != o.format)) return true;
       switch (format)
       {
       case 1: return u.format1 != o.u.format1;
@@ -1830,6 +1838,18 @@
       default:return false;
       }
     }
+    iter_t __end__ () const
+    {
+      iter_t it = {};
+      it.format = format;
+      switch (format)
+      {
+      case 1: it.u.format1 = u.format1.__end__ (); break;
+      case 2: it.u.format2 = u.format2.__end__ (); break;
+      default: break;
+      }
+      return it;
+    }
 
     private:
     unsigned int format;
@@ -1857,16 +1877,14 @@
 { c->start_embed<Coverage> ()->serialize (c, it); }
 
 static void ClassDef_remap_and_serialize (hb_serialize_context_t *c,
-					  const hb_map_t &gid_klass_map,
-					  hb_sorted_vector_t<HBGlyphID16> &glyphs,
 					  const hb_set_t &klasses,
                                           bool use_class_zero,
-					  hb_map_t *klass_map /*INOUT*/)
+                                          hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> &glyph_and_klass, /* IN/OUT */
+					  hb_map_t *klass_map /*IN/OUT*/)
 {
   if (!klass_map)
   {
-    ClassDef_serialize (c, hb_zip (glyphs.iter (), + glyphs.iter ()
-						   | hb_map (gid_klass_map)));
+    ClassDef_serialize (c, glyph_and_klass.iter ());
     return;
   }
 
@@ -1883,17 +1901,15 @@
     idx++;
   }
 
-  auto it =
-  + glyphs.iter ()
-  | hb_map_retains_sorting ([&] (const HBGlyphID16& gid) -> hb_pair_t<hb_codepoint_t, unsigned>
-			    {
-			      unsigned new_klass = klass_map->get (gid_klass_map[gid]);
-			      return hb_pair ((hb_codepoint_t)gid, new_klass);
-			    })
-  ;
 
-  c->propagate_error (glyphs, klasses);
-  ClassDef_serialize (c, it);
+  for (unsigned i = 0; i < glyph_and_klass.length; i++)
+  {
+    hb_codepoint_t klass = glyph_and_klass[i].second;
+    glyph_and_klass[i].second = klass_map->get (klass);
+  }
+
+  c->propagate_error (glyph_and_klass, klasses);
+  ClassDef_serialize (c, glyph_and_klass.iter ());
 }
 
 /*
@@ -1949,36 +1965,37 @@
                const Coverage* glyph_filter = nullptr) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
-    const hb_map_t &glyph_map = *c->plan->glyph_map;
+    const hb_map_t &glyph_map = *c->plan->glyph_map_gsub;
 
-    hb_sorted_vector_t<HBGlyphID16> glyphs;
+    hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> glyph_and_klass;
     hb_set_t orig_klasses;
-    hb_map_t gid_org_klass_map;
 
     hb_codepoint_t start = startGlyph;
     hb_codepoint_t end   = start + classValue.len;
 
-    for (const hb_codepoint_t gid : + hb_range (start, end)
-                                    | hb_filter (glyphset))
+    for (const hb_codepoint_t gid : + hb_range (start, end))
     {
+      hb_codepoint_t new_gid = glyph_map[gid];
+      if (new_gid == HB_MAP_VALUE_INVALID) continue;
       if (glyph_filter && !glyph_filter->has(gid)) continue;
 
       unsigned klass = classValue[gid - start];
       if (!klass) continue;
 
-      glyphs.push (glyph_map[gid]);
-      gid_org_klass_map.set (glyph_map[gid], klass);
+      glyph_and_klass.push (hb_pair (new_gid, klass));
       orig_klasses.add (klass);
     }
 
     unsigned glyph_count = glyph_filter
-                           ? hb_len (hb_iter (glyphset) | hb_filter (glyph_filter))
-                           : glyphset.get_population ();
-    use_class_zero = use_class_zero && glyph_count <= gid_org_klass_map.get_population ();
-    ClassDef_remap_and_serialize (c->serializer, gid_org_klass_map,
-				  glyphs, orig_klasses, use_class_zero, klass_map);
-    return_trace (keep_empty_table || (bool) glyphs);
+                           ? hb_len (hb_iter (glyph_map.keys()) | hb_filter (glyph_filter))
+                           : glyph_map.get_population ();
+    use_class_zero = use_class_zero && glyph_count <= glyph_and_klass.length;
+    ClassDef_remap_and_serialize (c->serializer,
+                                  orig_klasses,
+                                  use_class_zero,
+                                  glyph_and_klass,
+                                  klass_map);
+    return_trace (keep_empty_table || (bool) glyph_and_klass);
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -2044,10 +2061,9 @@
     }
     /* TODO Speed up, using set overlap first? */
     /* TODO(iter) Rewrite as dagger. */
-    HBUINT16 k {klass};
     const HBUINT16 *arr = classValue.arrayZ;
     for (unsigned int i = 0; i < count; i++)
-      if (arr[i] == k && glyphs->has (startGlyph + i))
+      if (arr[i] == klass && glyphs->has (startGlyph + i))
 	return true;
     return false;
   }
@@ -2057,17 +2073,32 @@
     unsigned count = classValue.len;
     if (klass == 0)
     {
-      hb_codepoint_t endGlyph = startGlyph + count -1;
-      for (hb_codepoint_t g : glyphs->iter ())
-        if (g < startGlyph || g > endGlyph)
-          intersect_glyphs->add (g);
+      unsigned start_glyph = startGlyph;
+      for (unsigned g = HB_SET_VALUE_INVALID;
+	   hb_set_next (glyphs, &g) && g < start_glyph;)
+	intersect_glyphs->add (g);
 
+      for (unsigned g = startGlyph + count - 1;
+	   hb_set_next (glyphs, &g);)
+	intersect_glyphs->add (g);
+
       return;
     }
 
     for (unsigned i = 0; i < count; i++)
       if (classValue[i] == klass && glyphs->has (startGlyph + i))
-        intersect_glyphs->add (startGlyph + i);
+	intersect_glyphs->add (startGlyph + i);
+
+#if 0
+    /* The following implementation is faster asymptotically, but slower
+     * in practice. */
+    unsigned start_glyph = startGlyph;
+    unsigned end_glyph = start_glyph + count;
+    for (unsigned g = startGlyph - 1;
+	 hb_set_next (glyphs, &g) && g < end_glyph;)
+      if (classValue.arrayZ[g - start_glyph] == klass)
+        intersect_glyphs->add (g);
+#endif
   }
 
   void intersected_classes (const hb_set_t *glyphs, hb_set_t *intersect_classes) const
@@ -2167,12 +2198,10 @@
                const Coverage* glyph_filter = nullptr) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
-    const hb_map_t &glyph_map = *c->plan->glyph_map;
+    const hb_map_t &glyph_map = *c->plan->glyph_map_gsub;
 
-    hb_sorted_vector_t<HBGlyphID16> glyphs;
+    hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> glyph_and_klass;
     hb_set_t orig_klasses;
-    hb_map_t gid_org_klass_map;
 
     unsigned count = rangeRecord.len;
     for (unsigned i = 0; i < count; i++)
@@ -2183,21 +2212,26 @@
       hb_codepoint_t end   = rangeRecord[i].last + 1;
       for (hb_codepoint_t g = start; g < end; g++)
       {
-	if (!glyphset.has (g)) continue;
+        hb_codepoint_t new_gid = glyph_map[g];
+	if (new_gid == HB_MAP_VALUE_INVALID) continue;
         if (glyph_filter && !glyph_filter->has (g)) continue;
-	glyphs.push (glyph_map[g]);
-	gid_org_klass_map.set (glyph_map[g], klass);
+
+	glyph_and_klass.push (hb_pair (new_gid, klass));
 	orig_klasses.add (klass);
       }
     }
 
+    const hb_set_t& glyphset = *c->plan->glyphset_gsub ();
     unsigned glyph_count = glyph_filter
                            ? hb_len (hb_iter (glyphset) | hb_filter (glyph_filter))
-                           : glyphset.get_population ();
-    use_class_zero = use_class_zero && glyph_count <= gid_org_klass_map.get_population ();
-    ClassDef_remap_and_serialize (c->serializer, gid_org_klass_map,
-				  glyphs, orig_klasses, use_class_zero, klass_map);
-    return_trace (keep_empty_table || (bool) glyphs);
+                           : glyph_map.get_population ();
+    use_class_zero = use_class_zero && glyph_count <= glyph_and_klass.length;
+    ClassDef_remap_and_serialize (c->serializer,
+                                  orig_klasses,
+                                  use_class_zero,
+                                  glyph_and_klass,
+                                  klass_map);
+    return_trace (keep_empty_table || (bool) glyph_and_klass);
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -2263,10 +2297,9 @@
     }
     /* TODO Speed up, using set overlap first? */
     /* TODO(iter) Rewrite as dagger. */
-    HBUINT16 k {klass};
     const RangeRecord *arr = rangeRecord.arrayZ;
     for (unsigned int i = 0; i < count; i++)
-      if (arr[i].value == k && arr[i].intersects (glyphs))
+      if (arr[i].value == klass && arr[i].intersects (glyphs))
 	return true;
     return false;
   }
@@ -2279,43 +2312,44 @@
       hb_codepoint_t g = HB_SET_VALUE_INVALID;
       for (unsigned int i = 0; i < count; i++)
       {
-        if (!hb_set_next (glyphs, &g))
-          break;
-        while (g != HB_SET_VALUE_INVALID && g < rangeRecord[i].first)
-        {
-          intersect_glyphs->add (g);
-          hb_set_next (glyphs, &g);
+	if (!hb_set_next (glyphs, &g))
+	  goto done;
+	while (g < rangeRecord[i].first)
+	{
+	  intersect_glyphs->add (g);
+	  if (!hb_set_next (glyphs, &g))
+	    goto done;
         }
         g = rangeRecord[i].last;
       }
-      while (g != HB_SET_VALUE_INVALID && hb_set_next (glyphs, &g))
-        intersect_glyphs->add (g);
+      while (hb_set_next (glyphs, &g))
+	intersect_glyphs->add (g);
+      done:
 
       return;
     }
 
-    hb_codepoint_t g = HB_SET_VALUE_INVALID;
+#if 0
+    /* The following implementation is faster asymptotically, but slower
+     * in practice. */
+    if ((count >> 3) > glyphs->get_population ())
+    {
+      for (hb_codepoint_t g = HB_SET_VALUE_INVALID;
+	   hb_set_next (glyphs, &g);)
+        if (rangeRecord.as_array ().bfind (g))
+	  intersect_glyphs->add (g);
+      return;
+    }
+#endif
+
     for (unsigned int i = 0; i < count; i++)
     {
       if (rangeRecord[i].value != klass) continue;
 
-      if (g != HB_SET_VALUE_INVALID)
-      {
-        if (g >= rangeRecord[i].first &&
-            g <= rangeRecord[i].last)
-          intersect_glyphs->add (g);
-        if (g > rangeRecord[i].last)
-          continue;
-      }
-
-      g = rangeRecord[i].first - 1;
-      while (hb_set_next (glyphs, &g))
-      {
-        if (g >= rangeRecord[i].first && g <= rangeRecord[i].last)
-          intersect_glyphs->add (g);
-        else if (g > rangeRecord[i].last)
-          break;
-      }
+      unsigned end = rangeRecord[i].last + 1;
+      for (hb_codepoint_t g = rangeRecord[i].first - 1;
+	   hb_set_next (glyphs, &g) && g < end;)
+	intersect_glyphs->add (g);
     }
   }
 

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-gsubgpos.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-gsubgpos.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-layout-gsubgpos.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -1328,7 +1328,7 @@
     bool has_pos_glyphs = false;
     hb_set_t pos_glyphs;
 
-    if (hb_set_is_empty (covered_seq_indicies) || !hb_set_has (covered_seq_indicies, seqIndex))
+    if (!hb_set_has (covered_seq_indicies, seqIndex))
     {
       has_pos_glyphs = true;
       if (seqIndex == 0)
@@ -1361,7 +1361,7 @@
 
     covered_seq_indicies->add (seqIndex);
     if (has_pos_glyphs) {
-      c->push_cur_active_glyphs () = pos_glyphs;
+      c->push_cur_active_glyphs () = std::move (pos_glyphs);
     } else {
       c->push_cur_active_glyphs ().set (*c->glyphs);
     }

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-use-table.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-use-table.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-shape-complex-use-table.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -606,7 +606,7 @@
   /* 10A00 */     B,  VBlw,  VBlw,  VBlw,    WJ,  VAbv,  VBlw,    WJ,    WJ,    WJ,    WJ,    WJ,  VPst, VMBlw, VMBlw, VMAbv,
   /* 10A10 */     B,     B,     B,     B,    WJ,     B,     B,     B,    WJ,     B,     B,     B,     B,     B,     B,     B,
   /* 10A20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
-  /* 10A30 */     B,     B,     B,     B,     B,     B,    WJ,    WJ, CMAbv, CMBlw, CMBlw,    WJ,    WJ,    WJ,    WJ,    IS,
+  /* 10A30 */     B,     B,     B,     B,     B,     B,    WJ,    WJ, CMBlw, CMBlw, CMBlw,    WJ,    WJ,    WJ,    WJ,    IS,
   /* 10A40 */     B,     B,     B,     B,     B,     B,     B,     B,     B,    WJ,    WJ,    WJ,    WJ,    WJ,    WJ,    WJ,
 
 #define use_offset_0x10ac0u 4304

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-tag-table.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-tag-table.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-tag-table.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -13,1612 +13,1615 @@
 #ifndef HB_OT_TAG_TABLE_HH
 #define HB_OT_TAG_TABLE_HH
 
-static const LangTag ot_languages[] = {
-  {"aa",	HB_TAG('A','F','R',' ')},	/* Afar */
-  {"aae",	HB_TAG('S','Q','I',' ')},	/* Arbëreshë Albanian -> Albanian */
-  {"aao",	HB_TAG('A','R','A',' ')},	/* Algerian Saharan Arabic -> Arabic */
-  {"aat",	HB_TAG('S','Q','I',' ')},	/* Arvanitika Albanian -> Albanian */
-  {"ab",	HB_TAG('A','B','K',' ')},	/* Abkhazian */
-  {"aba",	HB_TAG_NONE	       },	/* Abé != Abaza */
-  {"abh",	HB_TAG('A','R','A',' ')},	/* Tajiki Arabic -> Arabic */
-  {"abq",	HB_TAG('A','B','A',' ')},	/* Abaza */
-  {"abs",	HB_TAG('C','P','P',' ')},	/* Ambonese Malay -> Creoles */
-  {"abv",	HB_TAG('A','R','A',' ')},	/* Baharna Arabic -> Arabic */
-  {"acf",	HB_TAG('F','A','N',' ')},	/* Saint Lucian Creole French -> French Antillean */
-  {"acf",	HB_TAG('C','P','P',' ')},	/* Saint Lucian Creole French -> Creoles */
-/*{"ach",	HB_TAG('A','C','H',' ')},*/	/* Acoli -> Acholi */
-  {"acm",	HB_TAG('A','R','A',' ')},	/* Mesopotamian Arabic -> Arabic */
-  {"acq",	HB_TAG('A','R','A',' ')},	/* Ta'izzi-Adeni Arabic -> Arabic */
-  {"acr",	HB_TAG('A','C','R',' ')},	/* Achi */
-  {"acr",	HB_TAG('M','Y','N',' ')},	/* Achi -> Mayan */
-  {"acw",	HB_TAG('A','R','A',' ')},	/* Hijazi Arabic -> Arabic */
-  {"acx",	HB_TAG('A','R','A',' ')},	/* Omani Arabic -> Arabic */
-  {"acy",	HB_TAG('A','R','A',' ')},	/* Cypriot Arabic -> Arabic */
-  {"ada",	HB_TAG('D','N','G',' ')},	/* Adangme -> Dangme */
-  {"adf",	HB_TAG('A','R','A',' ')},	/* Dhofari Arabic -> Arabic */
-  {"adp",	HB_TAG('D','Z','N',' ')},	/* Adap (retired code) -> Dzongkha */
-/*{"ady",	HB_TAG('A','D','Y',' ')},*/	/* Adyghe */
-  {"aeb",	HB_TAG('A','R','A',' ')},	/* Tunisian Arabic -> Arabic */
-  {"aec",	HB_TAG('A','R','A',' ')},	/* Saidi Arabic -> Arabic */
-  {"af",	HB_TAG('A','F','K',' ')},	/* Afrikaans */
-  {"afb",	HB_TAG('A','R','A',' ')},	/* Gulf Arabic -> Arabic */
-  {"afk",	HB_TAG_NONE	       },	/* Nanubae != Afrikaans */
-  {"afs",	HB_TAG('C','P','P',' ')},	/* Afro-Seminole Creole -> Creoles */
-  {"agu",	HB_TAG('M','Y','N',' ')},	/* Aguacateco -> Mayan */
-  {"agw",	HB_TAG_NONE	       },	/* Kahua != Agaw */
-  {"ahg",	HB_TAG('A','G','W',' ')},	/* Qimant -> Agaw */
-  {"aht",	HB_TAG('A','T','H',' ')},	/* Ahtena -> Athapaskan */
-  {"aig",	HB_TAG('C','P','P',' ')},	/* Antigua and Barbuda Creole English -> Creoles */
-  {"aii",	HB_TAG('S','W','A',' ')},	/* Assyrian Neo-Aramaic -> Swadaya Aramaic */
-  {"aii",	HB_TAG('S','Y','R',' ')},	/* Assyrian Neo-Aramaic -> Syriac */
-/*{"aio",	HB_TAG('A','I','O',' ')},*/	/* Aiton */
-  {"aiw",	HB_TAG('A','R','I',' ')},	/* Aari */
-  {"ajp",	HB_TAG('A','R','A',' ')},	/* South Levantine Arabic -> Arabic */
-  {"ajt",	HB_TAG('A','R','A',' ')},	/* Judeo-Tunisian Arabic (retired code) -> Arabic */
-  {"ak",	HB_TAG('A','K','A',' ')},	/* Akan [macrolanguage] */
-  {"akb",	HB_TAG('A','K','B',' ')},	/* Batak Angkola */
-  {"akb",	HB_TAG('B','T','K',' ')},	/* Batak Angkola -> Batak */
-  {"aln",	HB_TAG('S','Q','I',' ')},	/* Gheg Albanian -> Albanian */
-  {"als",	HB_TAG('S','Q','I',' ')},	/* Tosk Albanian -> Albanian */
-/*{"alt",	HB_TAG('A','L','T',' ')},*/	/* Southern Altai -> Altai */
-  {"am",	HB_TAG('A','M','H',' ')},	/* Amharic */
-  {"amf",	HB_TAG('H','B','N',' ')},	/* Hamer-Banna -> Hammer-Banna */
-  {"amw",	HB_TAG('S','Y','R',' ')},	/* Western Neo-Aramaic -> Syriac */
-  {"an",	HB_TAG('A','R','G',' ')},	/* Aragonese */
-/*{"ang",	HB_TAG('A','N','G',' ')},*/	/* Old English (ca. 450-1100) -> Anglo-Saxon */
-  {"aoa",	HB_TAG('C','P','P',' ')},	/* Angolar -> Creoles */
-  {"apa",	HB_TAG('A','T','H',' ')},	/* Apache [collection] -> Athapaskan */
-  {"apc",	HB_TAG('A','R','A',' ')},	/* North Levantine Arabic -> Arabic */
-  {"apd",	HB_TAG('A','R','A',' ')},	/* Sudanese Arabic -> Arabic */
-  {"apj",	HB_TAG('A','T','H',' ')},	/* Jicarilla Apache -> Athapaskan */
-  {"apk",	HB_TAG('A','T','H',' ')},	/* Kiowa Apache -> Athapaskan */
-  {"apl",	HB_TAG('A','T','H',' ')},	/* Lipan Apache -> Athapaskan */
-  {"apm",	HB_TAG('A','T','H',' ')},	/* Mescalero-Chiricahua Apache -> Athapaskan */
-  {"apw",	HB_TAG('A','T','H',' ')},	/* Western Apache -> Athapaskan */
-  {"ar",	HB_TAG('A','R','A',' ')},	/* Arabic [macrolanguage] */
-  {"arb",	HB_TAG('A','R','A',' ')},	/* Standard Arabic -> Arabic */
-  {"ari",	HB_TAG_NONE	       },	/* Arikara != Aari */
-  {"ark",	HB_TAG_NONE	       },	/* Arikapú != Rakhine */
-  {"arn",	HB_TAG('M','A','P',' ')},	/* Mapudungun */
-  {"arq",	HB_TAG('A','R','A',' ')},	/* Algerian Arabic -> Arabic */
-  {"ars",	HB_TAG('A','R','A',' ')},	/* Najdi Arabic -> Arabic */
-  {"ary",	HB_TAG('M','O','R',' ')},	/* Moroccan Arabic -> Moroccan */
-  {"ary",	HB_TAG('A','R','A',' ')},	/* Moroccan Arabic -> Arabic */
-  {"arz",	HB_TAG('A','R','A',' ')},	/* Egyptian Arabic -> Arabic */
-  {"as",	HB_TAG('A','S','M',' ')},	/* Assamese */
-/*{"ast",	HB_TAG('A','S','T',' ')},*/	/* Asturian */
-/*{"ath",	HB_TAG('A','T','H',' ')},*/	/* Athapascan [collection] -> Athapaskan */
-  {"atj",	HB_TAG('R','C','R',' ')},	/* Atikamekw -> R-Cree */
-  {"atv",	HB_TAG('A','L','T',' ')},	/* Northern Altai -> Altai */
-  {"auj",	HB_TAG('B','B','R',' ')},	/* Awjilah -> Berber */
-  {"auz",	HB_TAG('A','R','A',' ')},	/* Uzbeki Arabic -> Arabic */
-  {"av",	HB_TAG('A','V','R',' ')},	/* Avaric -> Avar */
-  {"avl",	HB_TAG('A','R','A',' ')},	/* Eastern Egyptian Bedawi Arabic -> Arabic */
-/*{"avn",	HB_TAG('A','V','N',' ')},*/	/* Avatime */
-/*{"awa",	HB_TAG('A','W','A',' ')},*/	/* Awadhi */
-  {"ay",	HB_TAG('A','Y','M',' ')},	/* Aymara [macrolanguage] */
-  {"ayc",	HB_TAG('A','Y','M',' ')},	/* Southern Aymara -> Aymara */
-  {"ayh",	HB_TAG('A','R','A',' ')},	/* Hadrami Arabic -> Arabic */
-  {"ayl",	HB_TAG('A','R','A',' ')},	/* Libyan Arabic -> Arabic */
-  {"ayn",	HB_TAG('A','R','A',' ')},	/* Sanaani Arabic -> Arabic */
-  {"ayp",	HB_TAG('A','R','A',' ')},	/* North Mesopotamian Arabic -> Arabic */
-  {"ayr",	HB_TAG('A','Y','M',' ')},	/* Central Aymara -> Aymara */
-  {"az",	HB_TAG('A','Z','E',' ')},	/* Azerbaijani [macrolanguage] */
-  {"azb",	HB_TAG('A','Z','B',' ')},	/* South Azerbaijani -> Torki */
-  {"azb",	HB_TAG('A','Z','E',' ')},	/* South Azerbaijani -> Azerbaijani */
-  {"azd",	HB_TAG('N','A','H',' ')},	/* Eastern Durango Nahuatl -> Nahuatl */
-  {"azj",	HB_TAG('A','Z','E',' ')},	/* North Azerbaijani -> Azerbaijani */
-  {"azn",	HB_TAG('N','A','H',' ')},	/* Western Durango Nahuatl -> Nahuatl */
-  {"azz",	HB_TAG('N','A','H',' ')},	/* Highland Puebla Nahuatl -> Nahuatl */
-  {"ba",	HB_TAG('B','S','H',' ')},	/* Bashkir */
-  {"bad",	HB_TAG('B','A','D','0')},	/* Banda [collection] */
-  {"bag",	HB_TAG_NONE	       },	/* Tuki != Baghelkhandi */
-  {"bah",	HB_TAG('C','P','P',' ')},	/* Bahamas Creole English -> Creoles */
-  {"bai",	HB_TAG('B','M','L',' ')},	/* Bamileke [collection] */
-  {"bal",	HB_TAG('B','L','I',' ')},	/* Baluchi [macrolanguage] */
-/*{"ban",	HB_TAG('B','A','N',' ')},*/	/* Balinese */
-/*{"bar",	HB_TAG('B','A','R',' ')},*/	/* Bavarian */
-  {"bau",	HB_TAG_NONE	       },	/* Bada (Nigeria) != Baulé */
-  {"bbc",	HB_TAG('B','B','C',' ')},	/* Batak Toba */
-  {"bbc",	HB_TAG('B','T','K',' ')},	/* Batak Toba -> Batak */
-  {"bbj",	HB_TAG('B','M','L',' ')},	/* Ghomálá' -> Bamileke */
-  {"bbp",	HB_TAG('B','A','D','0')},	/* West Central Banda -> Banda */
-  {"bbr",	HB_TAG_NONE	       },	/* Girawa != Berber */
-  {"bbz",	HB_TAG('A','R','A',' ')},	/* Babalia Creole Arabic (retired code) -> Arabic */
-  {"bcc",	HB_TAG('B','L','I',' ')},	/* Southern Balochi -> Baluchi */
-  {"bch",	HB_TAG_NONE	       },	/* Bariai != Bench */
-  {"bci",	HB_TAG('B','A','U',' ')},	/* Baoulé -> Baulé */
-  {"bcl",	HB_TAG('B','I','K',' ')},	/* Central Bikol -> Bikol */
-  {"bcq",	HB_TAG('B','C','H',' ')},	/* Bench */
-  {"bcr",	HB_TAG('A','T','H',' ')},	/* Babine -> Athapaskan */
-/*{"bdy",	HB_TAG('B','D','Y',' ')},*/	/* Bandjalang */
-  {"be",	HB_TAG('B','E','L',' ')},	/* Belarusian -> Belarussian */
-  {"bea",	HB_TAG('A','T','H',' ')},	/* Beaver -> Athapaskan */
-  {"beb",	HB_TAG('B','T','I',' ')},	/* Bebele -> Beti */
-/*{"bem",	HB_TAG('B','E','M',' ')},*/	/* Bemba (Zambia) */
-  {"ber",	HB_TAG('B','B','R',' ')},	/* Berber [collection] */
-  {"bew",	HB_TAG('C','P','P',' ')},	/* Betawi -> Creoles */
-  {"bfl",	HB_TAG('B','A','D','0')},	/* Banda-Ndélé -> Banda */
-  {"bfq",	HB_TAG('B','A','D',' ')},	/* Badaga */
-  {"bft",	HB_TAG('B','L','T',' ')},	/* Balti */
-  {"bfu",	HB_TAG('L','A','H',' ')},	/* Gahri -> Lahuli */
-  {"bfy",	HB_TAG('B','A','G',' ')},	/* Bagheli -> Baghelkhandi */
-  {"bg",	HB_TAG('B','G','R',' ')},	/* Bulgarian */
-/*{"bgc",	HB_TAG('B','G','C',' ')},*/	/* Haryanvi */
-  {"bgn",	HB_TAG('B','L','I',' ')},	/* Western Balochi -> Baluchi */
-  {"bgp",	HB_TAG('B','L','I',' ')},	/* Eastern Balochi -> Baluchi */
-  {"bgq",	HB_TAG('B','G','Q',' ')},	/* Bagri */
-  {"bgq",	HB_TAG('R','A','J',' ')},	/* Bagri -> Rajasthani */
-  {"bgr",	HB_TAG('Q','I','N',' ')},	/* Bawm Chin -> Chin */
-  {"bhb",	HB_TAG('B','H','I',' ')},	/* Bhili */
-/*{"bhi",	HB_TAG('B','H','I',' ')},*/	/* Bhilali -> Bhili */
-  {"bhk",	HB_TAG('B','I','K',' ')},	/* Albay Bicolano (retired code) -> Bikol */
-/*{"bho",	HB_TAG('B','H','O',' ')},*/	/* Bhojpuri */
-  {"bhr",	HB_TAG('M','L','G',' ')},	/* Bara Malagasy -> Malagasy */
-  {"bi",	HB_TAG('B','I','S',' ')},	/* Bislama */
-  {"bi",	HB_TAG('C','P','P',' ')},	/* Bislama -> Creoles */
-/*{"bik",	HB_TAG('B','I','K',' ')},*/	/* Bikol [macrolanguage] */
-  {"bil",	HB_TAG_NONE	       },	/* Bile != Bilen */
-  {"bin",	HB_TAG('E','D','O',' ')},	/* Edo */
-  {"biu",	HB_TAG('Q','I','N',' ')},	/* Biete -> Chin */
-/*{"bjj",	HB_TAG('B','J','J',' ')},*/	/* Kanauji */
-  {"bjn",	HB_TAG('M','L','Y',' ')},	/* Banjar -> Malay */
-  {"bjo",	HB_TAG('B','A','D','0')},	/* Mid-Southern Banda -> Banda */
-  {"bjq",	HB_TAG('M','L','G',' ')},	/* Southern Betsimisaraka Malagasy (retired code) -> Malagasy */
-  {"bjs",	HB_TAG('C','P','P',' ')},	/* Bajan -> Creoles */
-  {"bjt",	HB_TAG('B','L','N',' ')},	/* Balanta-Ganja -> Balante */
-  {"bkf",	HB_TAG_NONE	       },	/* Beeke != Blackfoot */
-  {"bko",	HB_TAG('B','M','L',' ')},	/* Kwa' -> Bamileke */
-  {"bla",	HB_TAG('B','K','F',' ')},	/* Siksika -> Blackfoot */
-  {"ble",	HB_TAG('B','L','N',' ')},	/* Balanta-Kentohe -> Balante */
-  {"blg",	HB_TAG('I','B','A',' ')},	/* Balau (retired code) -> Iban */
-  {"bli",	HB_TAG_NONE	       },	/* Bolia != Baluchi */
-  {"blk",	HB_TAG('B','L','K',' ')},	/* Pa’o Karen */
-  {"blk",	HB_TAG('K','R','N',' ')},	/* Pa'o Karen -> Karen */
-  {"bln",	HB_TAG('B','I','K',' ')},	/* Southern Catanduanes Bikol -> Bikol */
-  {"blt",	HB_TAG_NONE	       },	/* Tai Dam != Balti */
-  {"bm",	HB_TAG('B','M','B',' ')},	/* Bambara (Bamanankan) */
-  {"bmb",	HB_TAG_NONE	       },	/* Bembe != Bambara (Bamanankan) */
-  {"bml",	HB_TAG_NONE	       },	/* Bomboli != Bamileke */
-  {"bmm",	HB_TAG('M','L','G',' ')},	/* Northern Betsimisaraka Malagasy -> Malagasy */
-  {"bn",	HB_TAG('B','E','N',' ')},	/* Bengali */
-  {"bo",	HB_TAG('T','I','B',' ')},	/* Tibetan */
-  {"bpd",	HB_TAG('B','A','D','0')},	/* Banda-Banda -> Banda */
-  {"bpl",	HB_TAG('C','P','P',' ')},	/* Broome Pearling Lugger Pidgin -> Creoles */
-  {"bpq",	HB_TAG('C','P','P',' ')},	/* Banda Malay -> Creoles */
-/*{"bpy",	HB_TAG('B','P','Y',' ')},*/	/* Bishnupriya -> Bishnupriya Manipuri */
-  {"bqi",	HB_TAG('L','R','C',' ')},	/* Bakhtiari -> Luri */
-  {"bqk",	HB_TAG('B','A','D','0')},	/* Banda-Mbrès -> Banda */
-  {"br",	HB_TAG('B','R','E',' ')},	/* Breton */
-  {"bra",	HB_TAG('B','R','I',' ')},	/* Braj -> Braj Bhasha */
-  {"brc",	HB_TAG('C','P','P',' ')},	/* Berbice Creole Dutch -> Creoles */
-/*{"brh",	HB_TAG('B','R','H',' ')},*/	/* Brahui */
-  {"bri",	HB_TAG_NONE	       },	/* Mokpwe != Braj Bhasha */
-  {"brm",	HB_TAG_NONE	       },	/* Barambu != Burmese */
-/*{"brx",	HB_TAG('B','R','X',' ')},*/	/* Bodo (India) */
-  {"bs",	HB_TAG('B','O','S',' ')},	/* Bosnian */
-  {"bsh",	HB_TAG_NONE	       },	/* Kati != Bashkir */
-/*{"bsk",	HB_TAG('B','S','K',' ')},*/	/* Burushaski */
-  {"btb",	HB_TAG('B','T','I',' ')},	/* Beti (Cameroon) (retired code) */
-  {"btd",	HB_TAG('B','T','D',' ')},	/* Batak Dairi (Pakpak) */
-  {"btd",	HB_TAG('B','T','K',' ')},	/* Batak Dairi -> Batak */
-  {"bti",	HB_TAG_NONE	       },	/* Burate != Beti */
-  {"btj",	HB_TAG('M','L','Y',' ')},	/* Bacanese Malay -> Malay */
-/*{"btk",	HB_TAG('B','T','K',' ')},*/	/* Batak [collection] */
-  {"btm",	HB_TAG('B','T','M',' ')},	/* Batak Mandailing */
-  {"btm",	HB_TAG('B','T','K',' ')},	/* Batak Mandailing -> Batak */
-  {"bto",	HB_TAG('B','I','K',' ')},	/* Rinconada Bikol -> Bikol */
-  {"bts",	HB_TAG('B','T','S',' ')},	/* Batak Simalungun */
-  {"bts",	HB_TAG('B','T','K',' ')},	/* Batak Simalungun -> Batak */
-  {"btx",	HB_TAG('B','T','X',' ')},	/* Batak Karo */
-  {"btx",	HB_TAG('B','T','K',' ')},	/* Batak Karo -> Batak */
-  {"btz",	HB_TAG('B','T','Z',' ')},	/* Batak Alas-Kluet */
-  {"btz",	HB_TAG('B','T','K',' ')},	/* Batak Alas-Kluet -> Batak */
-/*{"bug",	HB_TAG('B','U','G',' ')},*/	/* Buginese -> Bugis */
-  {"bum",	HB_TAG('B','T','I',' ')},	/* Bulu (Cameroon) -> Beti */
-  {"bve",	HB_TAG('M','L','Y',' ')},	/* Berau Malay -> Malay */
-  {"bvu",	HB_TAG('M','L','Y',' ')},	/* Bukit Malay -> Malay */
-  {"bwe",	HB_TAG('K','R','N',' ')},	/* Bwe Karen -> Karen */
-  {"bxk",	HB_TAG('L','U','H',' ')},	/* Bukusu -> Luyia */
-  {"bxo",	HB_TAG('C','P','P',' ')},	/* Barikanchi -> Creoles */
-  {"bxp",	HB_TAG('B','T','I',' ')},	/* Bebil -> Beti */
-  {"bxr",	HB_TAG('R','B','U',' ')},	/* Russia Buriat -> Russian Buriat */
-  {"byn",	HB_TAG('B','I','L',' ')},	/* Bilin -> Bilen */
-  {"byv",	HB_TAG('B','Y','V',' ')},	/* Medumba */
-  {"byv",	HB_TAG('B','M','L',' ')},	/* Medumba -> Bamileke */
-  {"bzc",	HB_TAG('M','L','G',' ')},	/* Southern Betsimisaraka Malagasy -> Malagasy */
-  {"bzj",	HB_TAG('C','P','P',' ')},	/* Belize Kriol English -> Creoles */
-  {"bzk",	HB_TAG('C','P','P',' ')},	/* Nicaragua Creole English -> Creoles */
-  {"ca",	HB_TAG('C','A','T',' ')},	/* Catalan */
-  {"caa",	HB_TAG('M','Y','N',' ')},	/* Chortí -> Mayan */
-  {"cac",	HB_TAG('M','Y','N',' ')},	/* Chuj -> Mayan */
-  {"caf",	HB_TAG('C','R','R',' ')},	/* Southern Carrier -> Carrier */
-  {"caf",	HB_TAG('A','T','H',' ')},	/* Southern Carrier -> Athapaskan */
-  {"cak",	HB_TAG('C','A','K',' ')},	/* Kaqchikel */
-  {"cak",	HB_TAG('M','Y','N',' ')},	/* Kaqchikel -> Mayan */
-  {"cbk",	HB_TAG('C','B','K',' ')},	/* Chavacano -> Zamboanga Chavacano */
-  {"cbk",	HB_TAG('C','P','P',' ')},	/* Chavacano -> Creoles */
-  {"cbl",	HB_TAG('Q','I','N',' ')},	/* Bualkhaw Chin -> Chin */
-  {"ccl",	HB_TAG('C','P','P',' ')},	/* Cutchi-Swahili -> Creoles */
-  {"ccm",	HB_TAG('C','P','P',' ')},	/* Malaccan Creole Malay -> Creoles */
-  {"cco",	HB_TAG('C','C','H','N')},	/* Comaltepec Chinantec -> Chinantec */
-  {"ccq",	HB_TAG('A','R','K',' ')},	/* Chaungtha (retired code) -> Rakhine */
-  {"cdo",	HB_TAG('Z','H','S',' ')},	/* Min Dong Chinese -> Chinese, Simplified */
-  {"ce",	HB_TAG('C','H','E',' ')},	/* Chechen */
-/*{"ceb",	HB_TAG('C','E','B',' ')},*/	/* Cebuano */
-  {"cek",	HB_TAG('Q','I','N',' ')},	/* Eastern Khumi Chin -> Chin */
-  {"cey",	HB_TAG('Q','I','N',' ')},	/* Ekai Chin -> Chin */
-  {"cfm",	HB_TAG('H','A','L',' ')},	/* Halam (Falam Chin) */
-  {"cfm",	HB_TAG('Q','I','N',' ')},	/* Falam Chin -> Chin */
-/*{"cgg",	HB_TAG('C','G','G',' ')},*/	/* Chiga */
-  {"ch",	HB_TAG('C','H','A',' ')},	/* Chamorro */
-  {"chf",	HB_TAG('M','Y','N',' ')},	/* Tabasco Chontal -> Mayan */
-  {"chg",	HB_TAG_NONE	       },	/* Chagatai != Chaha Gurage */
-  {"chh",	HB_TAG_NONE	       },	/* Chinook != Chattisgarhi */
-  {"chj",	HB_TAG('C','C','H','N')},	/* Ojitlán Chinantec -> Chinantec */
-  {"chk",	HB_TAG('C','H','K','0')},	/* Chuukese */
-  {"chm",	HB_TAG('H','M','A',' ')},	/* Mari (Russia) [macrolanguage] -> High Mari */
-  {"chm",	HB_TAG('L','M','A',' ')},	/* Mari (Russia) [macrolanguage] -> Low Mari */
-  {"chn",	HB_TAG('C','P','P',' ')},	/* Chinook jargon -> Creoles */
-/*{"cho",	HB_TAG('C','H','O',' ')},*/	/* Choctaw */
-  {"chp",	HB_TAG('C','H','P',' ')},	/* Chipewyan */
-  {"chp",	HB_TAG('S','A','Y',' ')},	/* Chipewyan -> Sayisi */
-  {"chp",	HB_TAG('A','T','H',' ')},	/* Chipewyan -> Athapaskan */
-  {"chq",	HB_TAG('C','C','H','N')},	/* Quiotepec Chinantec -> Chinantec */
-/*{"chr",	HB_TAG('C','H','R',' ')},*/	/* Cherokee */
-/*{"chy",	HB_TAG('C','H','Y',' ')},*/	/* Cheyenne */
-  {"chz",	HB_TAG('C','C','H','N')},	/* Ozumacín Chinantec -> Chinantec */
-  {"ciw",	HB_TAG('O','J','B',' ')},	/* Chippewa -> Ojibway */
-/*{"cja",	HB_TAG('C','J','A',' ')},*/	/* Western Cham */
-/*{"cjm",	HB_TAG('C','J','M',' ')},*/	/* Eastern Cham */
-  {"cjy",	HB_TAG('Z','H','S',' ')},	/* Jinyu Chinese -> Chinese, Simplified */
-  {"cka",	HB_TAG('Q','I','N',' ')},	/* Khumi Awa Chin (retired code) -> Chin */
-  {"ckb",	HB_TAG('K','U','R',' ')},	/* Central Kurdish -> Kurdish */
-  {"ckn",	HB_TAG('Q','I','N',' ')},	/* Kaang Chin -> Chin */
-  {"cks",	HB_TAG('C','P','P',' ')},	/* Tayo -> Creoles */
-  {"ckt",	HB_TAG('C','H','K',' ')},	/* Chukot -> Chukchi */
-  {"ckz",	HB_TAG('M','Y','N',' ')},	/* Cakchiquel-Quiché Mixed Language -> Mayan */
-  {"clc",	HB_TAG('A','T','H',' ')},	/* Chilcotin -> Athapaskan */
-  {"cld",	HB_TAG('S','Y','R',' ')},	/* Chaldean Neo-Aramaic -> Syriac */
-  {"cle",	HB_TAG('C','C','H','N')},	/* Lealao Chinantec -> Chinantec */
-  {"clj",	HB_TAG('Q','I','N',' ')},	/* Laitu Chin -> Chin */
-  {"clt",	HB_TAG('Q','I','N',' ')},	/* Lautu Chin -> Chin */
-  {"cmn",	HB_TAG('Z','H','S',' ')},	/* Mandarin Chinese -> Chinese, Simplified */
-  {"cmr",	HB_TAG('Q','I','N',' ')},	/* Mro-Khimi Chin -> Chin */
-  {"cnb",	HB_TAG('Q','I','N',' ')},	/* Chinbon Chin -> Chin */
-  {"cnh",	HB_TAG('Q','I','N',' ')},	/* Hakha Chin -> Chin */
-  {"cnk",	HB_TAG('Q','I','N',' ')},	/* Khumi Chin -> Chin */
-  {"cnl",	HB_TAG('C','C','H','N')},	/* Lalana Chinantec -> Chinantec */
-  {"cnp",	HB_TAG('Z','H','S',' ')},	/* Northern Ping Chinese -> Chinese, Simplified */
-  {"cnr",	HB_TAG('S','R','B',' ')},	/* Montenegrin -> Serbian */
-  {"cnt",	HB_TAG('C','C','H','N')},	/* Tepetotutla Chinantec -> Chinantec */
-  {"cnu",	HB_TAG('B','B','R',' ')},	/* Chenoua -> Berber */
-  {"cnw",	HB_TAG('Q','I','N',' ')},	/* Ngawn Chin -> Chin */
-  {"co",	HB_TAG('C','O','S',' ')},	/* Corsican */
-  {"coa",	HB_TAG('M','L','Y',' ')},	/* Cocos Islands Malay -> Malay */
-  {"cob",	HB_TAG('M','Y','N',' ')},	/* Chicomuceltec -> Mayan */
-/*{"cop",	HB_TAG('C','O','P',' ')},*/	/* Coptic */
-  {"coq",	HB_TAG('A','T','H',' ')},	/* Coquille -> Athapaskan */
-  {"cpa",	HB_TAG('C','C','H','N')},	/* Palantla Chinantec -> Chinantec */
-  {"cpe",	HB_TAG('C','P','P',' ')},	/* English-based creoles and pidgins [collection] -> Creoles */
-  {"cpf",	HB_TAG('C','P','P',' ')},	/* French-based creoles and pidgins [collection] -> Creoles */
-  {"cpi",	HB_TAG('C','P','P',' ')},	/* Chinese Pidgin English -> Creoles */
-/*{"cpp",	HB_TAG('C','P','P',' ')},*/	/* Portuguese-based creoles and pidgins [collection] -> Creoles */
-  {"cpx",	HB_TAG('Z','H','S',' ')},	/* Pu-Xian Chinese -> Chinese, Simplified */
-  {"cqd",	HB_TAG('H','M','N',' ')},	/* Chuanqiandian Cluster Miao -> Hmong */
-  {"cqu",	HB_TAG('Q','U','H',' ')},	/* Chilean Quechua (retired code) -> Quechua (Bolivia) */
-  {"cqu",	HB_TAG('Q','U','Z',' ')},	/* Chilean Quechua (retired code) -> Quechua */
-  {"cr",	HB_TAG('C','R','E',' ')},	/* Cree [macrolanguage] */
-  {"crh",	HB_TAG('C','R','T',' ')},	/* Crimean Tatar */
-  {"cri",	HB_TAG('C','P','P',' ')},	/* Sãotomense -> Creoles */
-  {"crj",	HB_TAG('E','C','R',' ')},	/* Southern East Cree -> Eastern Cree */
-  {"crj",	HB_TAG('Y','C','R',' ')},	/* Southern East Cree -> Y-Cree */
-  {"crj",	HB_TAG('C','R','E',' ')},	/* Southern East Cree -> Cree */
-  {"crk",	HB_TAG('W','C','R',' ')},	/* Plains Cree -> West-Cree */
-  {"crk",	HB_TAG('Y','C','R',' ')},	/* Plains Cree -> Y-Cree */
-  {"crk",	HB_TAG('C','R','E',' ')},	/* Plains Cree -> Cree */
-  {"crl",	HB_TAG('E','C','R',' ')},	/* Northern East Cree -> Eastern Cree */
-  {"crl",	HB_TAG('Y','C','R',' ')},	/* Northern East Cree -> Y-Cree */
-  {"crl",	HB_TAG('C','R','E',' ')},	/* Northern East Cree -> Cree */
-  {"crm",	HB_TAG('M','C','R',' ')},	/* Moose Cree */
-  {"crm",	HB_TAG('L','C','R',' ')},	/* Moose Cree -> L-Cree */
-  {"crm",	HB_TAG('C','R','E',' ')},	/* Moose Cree -> Cree */
-  {"crp",	HB_TAG('C','P','P',' ')},	/* Creoles and pidgins [collection] -> Creoles */
-  {"crr",	HB_TAG_NONE	       },	/* Carolina Algonquian != Carrier */
-  {"crs",	HB_TAG('C','P','P',' ')},	/* Seselwa Creole French -> Creoles */
-  {"crt",	HB_TAG_NONE	       },	/* Iyojwa'ja Chorote != Crimean Tatar */
-  {"crx",	HB_TAG('C','R','R',' ')},	/* Carrier */
-  {"crx",	HB_TAG('A','T','H',' ')},	/* Carrier -> Athapaskan */
-  {"cs",	HB_TAG('C','S','Y',' ')},	/* Czech */
-  {"csa",	HB_TAG('C','C','H','N')},	/* Chiltepec Chinantec -> Chinantec */
-/*{"csb",	HB_TAG('C','S','B',' ')},*/	/* Kashubian */
-  {"csh",	HB_TAG('Q','I','N',' ')},	/* Asho Chin -> Chin */
-  {"csj",	HB_TAG('Q','I','N',' ')},	/* Songlai Chin -> Chin */
-  {"csl",	HB_TAG_NONE	       },	/* Chinese Sign Language != Church Slavonic */
-  {"cso",	HB_TAG('C','C','H','N')},	/* Sochiapam Chinantec -> Chinantec */
-  {"csp",	HB_TAG('Z','H','S',' ')},	/* Southern Ping Chinese -> Chinese, Simplified */
-  {"csv",	HB_TAG('Q','I','N',' ')},	/* Sumtu Chin -> Chin */
-  {"csw",	HB_TAG('N','C','R',' ')},	/* Swampy Cree -> N-Cree */
-  {"csw",	HB_TAG('N','H','C',' ')},	/* Swampy Cree -> Norway House Cree */
-  {"csw",	HB_TAG('C','R','E',' ')},	/* Swampy Cree -> Cree */
-  {"csy",	HB_TAG('Q','I','N',' ')},	/* Siyin Chin -> Chin */
-  {"ctc",	HB_TAG('A','T','H',' ')},	/* Chetco -> Athapaskan */
-  {"ctd",	HB_TAG('Q','I','N',' ')},	/* Tedim Chin -> Chin */
-  {"cte",	HB_TAG('C','C','H','N')},	/* Tepinapa Chinantec -> Chinantec */
-/*{"ctg",	HB_TAG('C','T','G',' ')},*/	/* Chittagonian */
-  {"cth",	HB_TAG('Q','I','N',' ')},	/* Thaiphum Chin -> Chin */
-  {"ctl",	HB_TAG('C','C','H','N')},	/* Tlacoatzintepec Chinantec -> Chinantec */
-  {"cts",	HB_TAG('B','I','K',' ')},	/* Northern Catanduanes Bikol -> Bikol */
-/*{"ctt",	HB_TAG('C','T','T',' ')},*/	/* Wayanad Chetti */
-  {"ctu",	HB_TAG('M','Y','N',' ')},	/* Chol -> Mayan */
-  {"cu",	HB_TAG('C','S','L',' ')},	/* Church Slavonic */
-  {"cuc",	HB_TAG('C','C','H','N')},	/* Usila Chinantec -> Chinantec */
-/*{"cuk",	HB_TAG('C','U','K',' ')},*/	/* San Blas Kuna */
-  {"cv",	HB_TAG('C','H','U',' ')},	/* Chuvash */
-  {"cvn",	HB_TAG('C','C','H','N')},	/* Valle Nacional Chinantec -> Chinantec */
-  {"cwd",	HB_TAG('D','C','R',' ')},	/* Woods Cree */
-  {"cwd",	HB_TAG('T','C','R',' ')},	/* Woods Cree -> TH-Cree */
-  {"cwd",	HB_TAG('C','R','E',' ')},	/* Woods Cree -> Cree */
-  {"cy",	HB_TAG('W','E','L',' ')},	/* Welsh */
-  {"czh",	HB_TAG('Z','H','S',' ')},	/* Huizhou Chinese -> Chinese, Simplified */
-  {"czo",	HB_TAG('Z','H','S',' ')},	/* Min Zhong Chinese -> Chinese, Simplified */
-  {"czt",	HB_TAG('Q','I','N',' ')},	/* Zotung Chin -> Chin */
-  {"da",	HB_TAG('D','A','N',' ')},	/* Danish */
-/*{"dag",	HB_TAG('D','A','G',' ')},*/	/* Dagbani */
-  {"dao",	HB_TAG('Q','I','N',' ')},	/* Daai Chin -> Chin */
-  {"dap",	HB_TAG('N','I','S',' ')},	/* Nisi (India) (retired code) */
-/*{"dar",	HB_TAG('D','A','R',' ')},*/	/* Dargwa */
-/*{"dax",	HB_TAG('D','A','X',' ')},*/	/* Dayi */
-  {"dcr",	HB_TAG('C','P','P',' ')},	/* Negerhollands -> Creoles */
-  {"de",	HB_TAG('D','E','U',' ')},	/* German */
-  {"den",	HB_TAG('S','L','A',' ')},	/* Slave (Athapascan) [macrolanguage] -> Slavey */
-  {"den",	HB_TAG('A','T','H',' ')},	/* Slave (Athapascan) [macrolanguage] -> Athapaskan */
-  {"dep",	HB_TAG('C','P','P',' ')},	/* Pidgin Delaware -> Creoles */
-  {"dgo",	HB_TAG('D','G','O',' ')},	/* Dogri (individual language) */
-  {"dgo",	HB_TAG('D','G','R',' ')},	/* Dogri (macrolanguage) */
-  {"dgr",	HB_TAG('A','T','H',' ')},	/* Dogrib -> Athapaskan */
-  {"dhd",	HB_TAG('M','A','W',' ')},	/* Dhundari -> Marwari */
-/*{"dhg",	HB_TAG('D','H','G',' ')},*/	/* Dhangu */
-  {"dhv",	HB_TAG_NONE	       },	/* Dehu != Divehi (Dhivehi, Maldivian) (deprecated) */
-  {"dib",	HB_TAG('D','N','K',' ')},	/* South Central Dinka -> Dinka */
-  {"dik",	HB_TAG('D','N','K',' ')},	/* Southwestern Dinka -> Dinka */
-  {"din",	HB_TAG('D','N','K',' ')},	/* Dinka [macrolanguage] */
-  {"dip",	HB_TAG('D','N','K',' ')},	/* Northeastern Dinka -> Dinka */
-  {"diq",	HB_TAG('D','I','Q',' ')},	/* Dimli */
-  {"diq",	HB_TAG('Z','Z','A',' ')},	/* Dimli -> Zazaki */
-  {"diw",	HB_TAG('D','N','K',' ')},	/* Northwestern Dinka -> Dinka */
-  {"dje",	HB_TAG('D','J','R',' ')},	/* Zarma */
-  {"djk",	HB_TAG('C','P','P',' ')},	/* Eastern Maroon Creole -> Creoles */
-  {"djr",	HB_TAG('D','J','R','0')},	/* Djambarrpuyngu */
-  {"dks",	HB_TAG('D','N','K',' ')},	/* Southeastern Dinka -> Dinka */
-  {"dng",	HB_TAG('D','U','N',' ')},	/* Dungan */
-/*{"dnj",	HB_TAG('D','N','J',' ')},*/	/* Dan */
-  {"dnk",	HB_TAG_NONE	       },	/* Dengka != Dinka */
-  {"doi",	HB_TAG('D','G','R',' ')},	/* Dogri (macrolanguage) [macrolanguage] */
-  {"drh",	HB_TAG('M','N','G',' ')},	/* Darkhat (retired code) -> Mongolian */
-  {"dri",	HB_TAG_NONE	       },	/* C'Lela != Dari */
-  {"drw",	HB_TAG('D','R','I',' ')},	/* Darwazi (retired code) -> Dari */
-  {"drw",	HB_TAG('F','A','R',' ')},	/* Darwazi (retired code) -> Persian */
-  {"dsb",	HB_TAG('L','S','B',' ')},	/* Lower Sorbian */
-  {"dty",	HB_TAG('N','E','P',' ')},	/* Dotyali -> Nepali */
-/*{"duj",	HB_TAG('D','U','J',' ')},*/	/* Dhuwal (retired code) */
-  {"dun",	HB_TAG_NONE	       },	/* Dusun Deyah != Dungan */
-  {"dup",	HB_TAG('M','L','Y',' ')},	/* Duano -> Malay */
-  {"dv",	HB_TAG('D','I','V',' ')},	/* Divehi (Dhivehi, Maldivian) */
-  {"dv",	HB_TAG('D','H','V',' ')},	/* Divehi (Dhivehi, Maldivian) (deprecated) */
-  {"dwk",	HB_TAG('K','U','I',' ')},	/* Dawik Kui -> Kui */
-  {"dwu",	HB_TAG('D','U','J',' ')},	/* Dhuwal */
-  {"dwy",	HB_TAG('D','U','J',' ')},	/* Dhuwaya -> Dhuwal */
-  {"dyu",	HB_TAG('J','U','L',' ')},	/* Dyula -> Jula */
-  {"dz",	HB_TAG('D','Z','N',' ')},	/* Dzongkha */
-  {"dzn",	HB_TAG_NONE	       },	/* Dzando != Dzongkha */
-  {"ecr",	HB_TAG_NONE	       },	/* Eteocretan != Eastern Cree */
-  {"ee",	HB_TAG('E','W','E',' ')},	/* Ewe */
-/*{"efi",	HB_TAG('E','F','I',' ')},*/	/* Efik */
-  {"ekk",	HB_TAG('E','T','I',' ')},	/* Standard Estonian -> Estonian */
-  {"eky",	HB_TAG('K','R','N',' ')},	/* Eastern Kayah -> Karen */
-  {"el",	HB_TAG('E','L','L',' ')},	/* Modern Greek (1453-) -> Greek */
-  {"emk",	HB_TAG('E','M','K',' ')},	/* Eastern Maninkakan */
-  {"emk",	HB_TAG('M','N','K',' ')},	/* Eastern Maninkakan -> Maninka */
-  {"emy",	HB_TAG('M','Y','N',' ')},	/* Epigraphic Mayan -> Mayan */
-  {"en",	HB_TAG('E','N','G',' ')},	/* English */
-  {"enb",	HB_TAG('K','A','L',' ')},	/* Markweeta -> Kalenjin */
-  {"enf",	HB_TAG('F','N','E',' ')},	/* Forest Enets */
-  {"enh",	HB_TAG('T','N','E',' ')},	/* Tundra Enets */
-  {"eo",	HB_TAG('N','T','O',' ')},	/* Esperanto */
-  {"es",	HB_TAG('E','S','P',' ')},	/* Spanish */
-  {"esg",	HB_TAG('G','O','N',' ')},	/* Aheri Gondi -> Gondi */
-  {"esi",	HB_TAG('I','P','K',' ')},	/* North Alaskan Inupiatun -> Inupiat */
-  {"esk",	HB_TAG('I','P','K',' ')},	/* Northwest Alaska Inupiatun -> Inupiat */
-/*{"esu",	HB_TAG('E','S','U',' ')},*/	/* Central Yupik */
-  {"et",	HB_TAG('E','T','I',' ')},	/* Estonian [macrolanguage] */
-  {"eto",	HB_TAG('B','T','I',' ')},	/* Eton (Cameroon) -> Beti */
-  {"eu",	HB_TAG('E','U','Q',' ')},	/* Basque */
-  {"euq",	HB_TAG_NONE	       },	/* Basque [collection] != Basque */
-  {"eve",	HB_TAG('E','V','N',' ')},	/* Even */
-  {"evn",	HB_TAG('E','V','K',' ')},	/* Evenki */
-  {"ewo",	HB_TAG('B','T','I',' ')},	/* Ewondo -> Beti */
-  {"eyo",	HB_TAG('K','A','L',' ')},	/* Keiyo -> Kalenjin */
-  {"fa",	HB_TAG('F','A','R',' ')},	/* Persian [macrolanguage] */
-  {"fab",	HB_TAG('C','P','P',' ')},	/* Fa d'Ambu -> Creoles */
-  {"fan",	HB_TAG('F','A','N','0')},	/* Fang (Equatorial Guinea) */
-  {"fan",	HB_TAG('B','T','I',' ')},	/* Fang (Equatorial Guinea) -> Beti */
-  {"far",	HB_TAG_NONE	       },	/* Fataleka != Persian */
-  {"fat",	HB_TAG('F','A','T',' ')},	/* Fanti */
-  {"fat",	HB_TAG('A','K','A',' ')},	/* Fanti -> Akan */
-  {"fbl",	HB_TAG('B','I','K',' ')},	/* West Albay Bikol -> Bikol */
-  {"ff",	HB_TAG('F','U','L',' ')},	/* Fulah [macrolanguage] */
-  {"ffm",	HB_TAG('F','U','L',' ')},	/* Maasina Fulfulde -> Fulah */
-  {"fi",	HB_TAG('F','I','N',' ')},	/* Finnish */
-  {"fil",	HB_TAG('P','I','L',' ')},	/* Filipino */
-  {"fj",	HB_TAG('F','J','I',' ')},	/* Fijian */
-  {"flm",	HB_TAG('H','A','L',' ')},	/* Halam (Falam Chin) (retired code) */
-  {"flm",	HB_TAG('Q','I','N',' ')},	/* Falam Chin (retired code) -> Chin */
-  {"fmp",	HB_TAG('F','M','P',' ')},	/* Fe’fe’ */
-  {"fmp",	HB_TAG('B','M','L',' ')},	/* Fe'fe' -> Bamileke */
-  {"fng",	HB_TAG('C','P','P',' ')},	/* Fanagalo -> Creoles */
-  {"fo",	HB_TAG('F','O','S',' ')},	/* Faroese */
-/*{"fon",	HB_TAG('F','O','N',' ')},*/	/* Fon */
-  {"fos",	HB_TAG_NONE	       },	/* Siraya != Faroese */
-  {"fpe",	HB_TAG('C','P','P',' ')},	/* Fernando Po Creole English -> Creoles */
-  {"fr",	HB_TAG('F','R','A',' ')},	/* French */
-/*{"frc",	HB_TAG('F','R','C',' ')},*/	/* Cajun French */
-/*{"frp",	HB_TAG('F','R','P',' ')},*/	/* Arpitan */
-  {"fub",	HB_TAG('F','U','L',' ')},	/* Adamawa Fulfulde -> Fulah */
-  {"fuc",	HB_TAG('F','U','L',' ')},	/* Pulaar -> Fulah */
-  {"fue",	HB_TAG('F','U','L',' ')},	/* Borgu Fulfulde -> Fulah */
-  {"fuf",	HB_TAG('F','T','A',' ')},	/* Pular -> Futa */
-  {"fuf",	HB_TAG('F','U','L',' ')},	/* Pular -> Fulah */
-  {"fuh",	HB_TAG('F','U','L',' ')},	/* Western Niger Fulfulde -> Fulah */
-  {"fui",	HB_TAG('F','U','L',' ')},	/* Bagirmi Fulfulde -> Fulah */
-  {"fuq",	HB_TAG('F','U','L',' ')},	/* Central-Eastern Niger Fulfulde -> Fulah */
-  {"fur",	HB_TAG('F','R','L',' ')},	/* Friulian */
-  {"fuv",	HB_TAG('F','U','V',' ')},	/* Nigerian Fulfulde */
-  {"fuv",	HB_TAG('F','U','L',' ')},	/* Nigerian Fulfulde -> Fulah */
-  {"fy",	HB_TAG('F','R','I',' ')},	/* Western Frisian -> Frisian */
-  {"ga",	HB_TAG('I','R','I',' ')},	/* Irish */
-  {"gaa",	HB_TAG('G','A','D',' ')},	/* Ga */
-  {"gac",	HB_TAG('C','P','P',' ')},	/* Mixed Great Andamanese -> Creoles */
-  {"gad",	HB_TAG_NONE	       },	/* Gaddang != Ga */
-  {"gae",	HB_TAG_NONE	       },	/* Guarequena != Scottish Gaelic (Gaelic) */
-/*{"gag",	HB_TAG('G','A','G',' ')},*/	/* Gagauz */
-  {"gal",	HB_TAG_NONE	       },	/* Galolen != Galician */
-  {"gan",	HB_TAG('Z','H','S',' ')},	/* Gan Chinese -> Chinese, Simplified */
-  {"gar",	HB_TAG_NONE	       },	/* Galeya != Garshuni */
-  {"gaw",	HB_TAG_NONE	       },	/* Nobonob != Garhwali */
-  {"gax",	HB_TAG('O','R','O',' ')},	/* Borana-Arsi-Guji Oromo -> Oromo */
-  {"gaz",	HB_TAG('O','R','O',' ')},	/* West Central Oromo -> Oromo */
-  {"gbm",	HB_TAG('G','A','W',' ')},	/* Garhwali */
-  {"gce",	HB_TAG('A','T','H',' ')},	/* Galice -> Athapaskan */
-  {"gcf",	HB_TAG('C','P','P',' ')},	/* Guadeloupean Creole French -> Creoles */
-  {"gcl",	HB_TAG('C','P','P',' ')},	/* Grenadian Creole English -> Creoles */
-  {"gcr",	HB_TAG('C','P','P',' ')},	/* Guianese Creole French -> Creoles */
-  {"gd",	HB_TAG('G','A','E',' ')},	/* Scottish Gaelic (Gaelic) */
-  {"gda",	HB_TAG('R','A','J',' ')},	/* Gade Lohar -> Rajasthani */
-/*{"gez",	HB_TAG('G','E','Z',' ')},*/	/* Geez */
-  {"ggo",	HB_TAG('G','O','N',' ')},	/* Southern Gondi (retired code) -> Gondi */
-  {"gha",	HB_TAG('B','B','R',' ')},	/* Ghadamès -> Berber */
-  {"ghk",	HB_TAG('K','R','N',' ')},	/* Geko Karen -> Karen */
-  {"gho",	HB_TAG('B','B','R',' ')},	/* Ghomara -> Berber */
-  {"gib",	HB_TAG('C','P','P',' ')},	/* Gibanawa -> Creoles */
-/*{"gih",	HB_TAG('G','I','H',' ')},*/	/* Githabul */
-  {"gil",	HB_TAG('G','I','L','0')},	/* Kiribati (Gilbertese) */
-  {"gju",	HB_TAG('R','A','J',' ')},	/* Gujari -> Rajasthani */
-  {"gkp",	HB_TAG('G','K','P',' ')},	/* Guinea Kpelle -> Kpelle (Guinea) */
-  {"gkp",	HB_TAG('K','P','L',' ')},	/* Guinea Kpelle -> Kpelle */
-  {"gl",	HB_TAG('G','A','L',' ')},	/* Galician */
-  {"gld",	HB_TAG('N','A','N',' ')},	/* Nanai */
-/*{"glk",	HB_TAG('G','L','K',' ')},*/	/* Gilaki */
-  {"gmz",	HB_TAG_NONE	       },	/* Mgbolizhia != Gumuz */
-  {"gn",	HB_TAG('G','U','A',' ')},	/* Guarani [macrolanguage] */
-  {"gnb",	HB_TAG('Q','I','N',' ')},	/* Gangte -> Chin */
-/*{"gnn",	HB_TAG('G','N','N',' ')},*/	/* Gumatj */
-  {"gno",	HB_TAG('G','O','N',' ')},	/* Northern Gondi -> Gondi */
-  {"gnw",	HB_TAG('G','U','A',' ')},	/* Western Bolivian Guaraní -> Guarani */
-/*{"gog",	HB_TAG('G','O','G',' ')},*/	/* Gogo */
-  {"gom",	HB_TAG('K','O','K',' ')},	/* Goan Konkani -> Konkani */
-/*{"gon",	HB_TAG('G','O','N',' ')},*/	/* Gondi [macrolanguage] */
-  {"goq",	HB_TAG('C','P','P',' ')},	/* Gorap -> Creoles */
-  {"gox",	HB_TAG('B','A','D','0')},	/* Gobu -> Banda */
-  {"gpe",	HB_TAG('C','P','P',' ')},	/* Ghanaian Pidgin English -> Creoles */
-  {"gro",	HB_TAG_NONE	       },	/* Groma != Garo */
-  {"grr",	HB_TAG('B','B','R',' ')},	/* Taznatit -> Berber */
-  {"grt",	HB_TAG('G','R','O',' ')},	/* Garo */
-  {"gru",	HB_TAG('S','O','G',' ')},	/* Kistane -> Sodo Gurage */
-  {"gsw",	HB_TAG('A','L','S',' ')},	/* Alsatian */
-  {"gu",	HB_TAG('G','U','J',' ')},	/* Gujarati */
-  {"gua",	HB_TAG_NONE	       },	/* Shiki != Guarani */
-/*{"guc",	HB_TAG('G','U','C',' ')},*/	/* Wayuu */
-/*{"guf",	HB_TAG('G','U','F',' ')},*/	/* Gupapuyngu */
-  {"gug",	HB_TAG('G','U','A',' ')},	/* Paraguayan Guaraní -> Guarani */
-  {"gui",	HB_TAG('G','U','A',' ')},	/* Eastern Bolivian Guaraní -> Guarani */
-  {"guk",	HB_TAG('G','M','Z',' ')},	/* Gumuz */
-  {"gul",	HB_TAG('C','P','P',' ')},	/* Sea Island Creole English -> Creoles */
-  {"gun",	HB_TAG('G','U','A',' ')},	/* Mbyá Guaraní -> Guarani */
-/*{"guz",	HB_TAG('G','U','Z',' ')},*/	/* Gusii */
-  {"gv",	HB_TAG('M','N','X',' ')},	/* Manx */
-  {"gwi",	HB_TAG('A','T','H',' ')},	/* Gwichʼin -> Athapaskan */
-  {"gyn",	HB_TAG('C','P','P',' ')},	/* Guyanese Creole English -> Creoles */
-  {"ha",	HB_TAG('H','A','U',' ')},	/* Hausa */
-  {"haa",	HB_TAG('A','T','H',' ')},	/* Han -> Athapaskan */
-  {"hae",	HB_TAG('O','R','O',' ')},	/* Eastern Oromo -> Oromo */
-  {"hai",	HB_TAG('H','A','I','0')},	/* Haida [macrolanguage] */
-  {"hak",	HB_TAG('Z','H','S',' ')},	/* Hakka Chinese -> Chinese, Simplified */
-  {"hal",	HB_TAG_NONE	       },	/* Halang != Halam (Falam Chin) */
-  {"har",	HB_TAG('H','R','I',' ')},	/* Harari */
-/*{"haw",	HB_TAG('H','A','W',' ')},*/	/* Hawaiian */
-  {"hax",	HB_TAG('H','A','I','0')},	/* Southern Haida -> Haida */
-/*{"hay",	HB_TAG('H','A','Y',' ')},*/	/* Haya */
-/*{"haz",	HB_TAG('H','A','Z',' ')},*/	/* Hazaragi */
-  {"hbn",	HB_TAG_NONE	       },	/* Heiban != Hammer-Banna */
-  {"hca",	HB_TAG('C','P','P',' ')},	/* Andaman Creole Hindi -> Creoles */
-  {"hdn",	HB_TAG('H','A','I','0')},	/* Northern Haida -> Haida */
-  {"he",	HB_TAG('I','W','R',' ')},	/* Hebrew */
-  {"hea",	HB_TAG('H','M','N',' ')},	/* Northern Qiandong Miao -> Hmong */
-/*{"hei",	HB_TAG('H','E','I',' ')},*/	/* Heiltsuk */
-  {"hi",	HB_TAG('H','I','N',' ')},	/* Hindi */
-/*{"hil",	HB_TAG('H','I','L',' ')},*/	/* Hiligaynon */
-  {"hji",	HB_TAG('M','L','Y',' ')},	/* Haji -> Malay */
-  {"hlt",	HB_TAG('Q','I','N',' ')},	/* Matu Chin -> Chin */
-  {"hma",	HB_TAG('H','M','N',' ')},	/* Southern Mashan Hmong -> Hmong */
-  {"hmc",	HB_TAG('H','M','N',' ')},	/* Central Huishui Hmong -> Hmong */
-  {"hmd",	HB_TAG('H','M','D',' ')},	/* Large Flowery Miao -> A-Hmao */
-  {"hmd",	HB_TAG('H','M','N',' ')},	/* Large Flowery Miao -> Hmong */
-  {"hme",	HB_TAG('H','M','N',' ')},	/* Eastern Huishui Hmong -> Hmong */
-  {"hmg",	HB_TAG('H','M','N',' ')},	/* Southwestern Guiyang Hmong -> Hmong */
-  {"hmh",	HB_TAG('H','M','N',' ')},	/* Southwestern Huishui Hmong -> Hmong */
-  {"hmi",	HB_TAG('H','M','N',' ')},	/* Northern Huishui Hmong -> Hmong */
-  {"hmj",	HB_TAG('H','M','N',' ')},	/* Ge -> Hmong */
-  {"hml",	HB_TAG('H','M','N',' ')},	/* Luopohe Hmong -> Hmong */
-  {"hmm",	HB_TAG('H','M','N',' ')},	/* Central Mashan Hmong -> Hmong */
-/*{"hmn",	HB_TAG('H','M','N',' ')},*/	/* Hmong [macrolanguage] */
-  {"hmp",	HB_TAG('H','M','N',' ')},	/* Northern Mashan Hmong -> Hmong */
-  {"hmq",	HB_TAG('H','M','N',' ')},	/* Eastern Qiandong Miao -> Hmong */
-  {"hmr",	HB_TAG('Q','I','N',' ')},	/* Hmar -> Chin */
-  {"hms",	HB_TAG('H','M','N',' ')},	/* Southern Qiandong Miao -> Hmong */
-  {"hmw",	HB_TAG('H','M','N',' ')},	/* Western Mashan Hmong -> Hmong */
-  {"hmy",	HB_TAG('H','M','N',' ')},	/* Southern Guiyang Hmong -> Hmong */
-  {"hmz",	HB_TAG('H','M','Z',' ')},	/* Hmong Shua -> Hmong Shuat */
-  {"hmz",	HB_TAG('H','M','N',' ')},	/* Hmong Shua -> Hmong */
-/*{"hnd",	HB_TAG('H','N','D',' ')},*/	/* Southern Hindko -> Hindko */
-  {"hne",	HB_TAG('C','H','H',' ')},	/* Chhattisgarhi -> Chattisgarhi */
-  {"hnj",	HB_TAG('H','M','N',' ')},	/* Hmong Njua -> Hmong */
-  {"hno",	HB_TAG('H','N','D',' ')},	/* Northern Hindko -> Hindko */
-  {"ho",	HB_TAG('H','M','O',' ')},	/* Hiri Motu */
-  {"ho",	HB_TAG('C','P','P',' ')},	/* Hiri Motu -> Creoles */
-  {"hoc",	HB_TAG('H','O',' ',' ')},	/* Ho */
-  {"hoi",	HB_TAG('A','T','H',' ')},	/* Holikachuk -> Athapaskan */
-  {"hoj",	HB_TAG('H','A','R',' ')},	/* Hadothi -> Harauti */
-  {"hoj",	HB_TAG('R','A','J',' ')},	/* Hadothi -> Rajasthani */
-  {"hr",	HB_TAG('H','R','V',' ')},	/* Croatian */
-  {"hra",	HB_TAG('Q','I','N',' ')},	/* Hrangkhol -> Chin */
-  {"hrm",	HB_TAG('H','M','N',' ')},	/* Horned Miao -> Hmong */
-  {"hsb",	HB_TAG('U','S','B',' ')},	/* Upper Sorbian */
-  {"hsn",	HB_TAG('Z','H','S',' ')},	/* Xiang Chinese -> Chinese, Simplified */
-  {"ht",	HB_TAG('H','A','I',' ')},	/* Haitian (Haitian Creole) */
-  {"ht",	HB_TAG('C','P','P',' ')},	/* Haitian -> Creoles */
-  {"hu",	HB_TAG('H','U','N',' ')},	/* Hungarian */
-  {"huj",	HB_TAG('H','M','N',' ')},	/* Northern Guiyang Hmong -> Hmong */
-  {"hup",	HB_TAG('A','T','H',' ')},	/* Hupa -> Athapaskan */
-  {"hus",	HB_TAG('M','Y','N',' ')},	/* Huastec -> Mayan */
-  {"hwc",	HB_TAG('C','P','P',' ')},	/* Hawai'i Creole English -> Creoles */
-  {"hy",	HB_TAG('H','Y','E','0')},	/* Armenian -> Armenian East */
-  {"hy",	HB_TAG('H','Y','E',' ')},	/* Armenian */
-  {"hyw",	HB_TAG('H','Y','E',' ')},	/* Western Armenian -> Armenian */
-  {"hz",	HB_TAG('H','E','R',' ')},	/* Herero */
-  {"ia",	HB_TAG('I','N','A',' ')},	/* Interlingua (International Auxiliary Language Association) */
-/*{"iba",	HB_TAG('I','B','A',' ')},*/	/* Iban */
-/*{"ibb",	HB_TAG('I','B','B',' ')},*/	/* Ibibio */
-  {"iby",	HB_TAG('I','J','O',' ')},	/* Ibani -> Ijo */
-  {"icr",	HB_TAG('C','P','P',' ')},	/* Islander Creole English -> Creoles */
-  {"id",	HB_TAG('I','N','D',' ')},	/* Indonesian */
-  {"id",	HB_TAG('M','L','Y',' ')},	/* Indonesian -> Malay */
-  {"ida",	HB_TAG('L','U','H',' ')},	/* Idakho-Isukha-Tiriki -> Luyia */
-  {"idb",	HB_TAG('C','P','P',' ')},	/* Indo-Portuguese -> Creoles */
-  {"ie",	HB_TAG('I','L','E',' ')},	/* Interlingue */
-  {"ig",	HB_TAG('I','B','O',' ')},	/* Igbo */
-  {"igb",	HB_TAG('E','B','I',' ')},	/* Ebira */
-  {"ihb",	HB_TAG('C','P','P',' ')},	/* Iha Based Pidgin -> Creoles */
-  {"ii",	HB_TAG('Y','I','M',' ')},	/* Sichuan Yi -> Yi Modern */
-  {"ijc",	HB_TAG('I','J','O',' ')},	/* Izon -> Ijo */
-  {"ije",	HB_TAG('I','J','O',' ')},	/* Biseni -> Ijo */
-  {"ijn",	HB_TAG('I','J','O',' ')},	/* Kalabari -> Ijo */
-/*{"ijo",	HB_TAG('I','J','O',' ')},*/	/* Ijo [collection] */
-  {"ijs",	HB_TAG('I','J','O',' ')},	/* Southeast Ijo -> Ijo */
-  {"ik",	HB_TAG('I','P','K',' ')},	/* Inupiaq [macrolanguage] -> Inupiat */
-  {"ike",	HB_TAG('I','N','U',' ')},	/* Eastern Canadian Inuktitut -> Inuktitut */
-  {"ike",	HB_TAG('I','N','U','K')},	/* Eastern Canadian Inuktitut -> Nunavik Inuktitut */
-  {"ikt",	HB_TAG('I','N','U',' ')},	/* Inuinnaqtun -> Inuktitut */
-/*{"ilo",	HB_TAG('I','L','O',' ')},*/	/* Iloko -> Ilokano */
-  {"in",	HB_TAG('I','N','D',' ')},	/* Indonesian (retired code) */
-  {"in",	HB_TAG('M','L','Y',' ')},	/* Indonesian (retired code) -> Malay */
-  {"ing",	HB_TAG('A','T','H',' ')},	/* Degexit'an -> Athapaskan */
-  {"inh",	HB_TAG('I','N','G',' ')},	/* Ingush */
-  {"io",	HB_TAG('I','D','O',' ')},	/* Ido */
-  {"iri",	HB_TAG_NONE	       },	/* Rigwe != Irish */
-/*{"iru",	HB_TAG('I','R','U',' ')},*/	/* Irula */
-  {"is",	HB_TAG('I','S','L',' ')},	/* Icelandic */
-  {"ism",	HB_TAG_NONE	       },	/* Masimasi != Inari Sami */
-  {"it",	HB_TAG('I','T','A',' ')},	/* Italian */
-  {"itz",	HB_TAG('M','Y','N',' ')},	/* Itzá -> Mayan */
-  {"iu",	HB_TAG('I','N','U',' ')},	/* Inuktitut [macrolanguage] */
-  {"iu",	HB_TAG('I','N','U','K')},	/* Inuktitut [macrolanguage] -> Nunavik Inuktitut */
-  {"iw",	HB_TAG('I','W','R',' ')},	/* Hebrew (retired code) */
-  {"ixl",	HB_TAG('M','Y','N',' ')},	/* Ixil -> Mayan */
-  {"ja",	HB_TAG('J','A','N',' ')},	/* Japanese */
-  {"jac",	HB_TAG('M','Y','N',' ')},	/* Popti' -> Mayan */
-  {"jak",	HB_TAG('M','L','Y',' ')},	/* Jakun -> Malay */
-  {"jam",	HB_TAG('J','A','M',' ')},	/* Jamaican Creole English -> Jamaican Creole */
-  {"jam",	HB_TAG('C','P','P',' ')},	/* Jamaican Creole English -> Creoles */
-  {"jan",	HB_TAG_NONE	       },	/* Jandai != Japanese */
-  {"jax",	HB_TAG('M','L','Y',' ')},	/* Jambi Malay -> Malay */
-  {"jbe",	HB_TAG('B','B','R',' ')},	/* Judeo-Berber -> Berber */
-  {"jbn",	HB_TAG('B','B','R',' ')},	/* Nafusi -> Berber */
-/*{"jbo",	HB_TAG('J','B','O',' ')},*/	/* Lojban */
-/*{"jct",	HB_TAG('J','C','T',' ')},*/	/* Krymchak */
-  {"jgo",	HB_TAG('B','M','L',' ')},	/* Ngomba -> Bamileke */
-  {"ji",	HB_TAG('J','I','I',' ')},	/* Yiddish (retired code) */
-  {"jii",	HB_TAG_NONE	       },	/* Jiiddu != Yiddish */
-  {"jkm",	HB_TAG('K','R','N',' ')},	/* Mobwa Karen -> Karen */
-  {"jkp",	HB_TAG('K','R','N',' ')},	/* Paku Karen -> Karen */
-  {"jud",	HB_TAG_NONE	       },	/* Worodougou != Ladino */
-  {"jul",	HB_TAG_NONE	       },	/* Jirel != Jula */
-  {"jv",	HB_TAG('J','A','V',' ')},	/* Javanese */
-  {"jvd",	HB_TAG('C','P','P',' ')},	/* Javindo -> Creoles */
-  {"jw",	HB_TAG('J','A','V',' ')},	/* Javanese (retired code) */
-  {"ka",	HB_TAG('K','A','T',' ')},	/* Georgian */
-  {"kaa",	HB_TAG('K','R','K',' ')},	/* Karakalpak */
-  {"kab",	HB_TAG('K','A','B','0')},	/* Kabyle */
-  {"kab",	HB_TAG('B','B','R',' ')},	/* Kabyle -> Berber */
-  {"kac",	HB_TAG_NONE	       },	/* Kachin != Kachchi */
-  {"kam",	HB_TAG('K','M','B',' ')},	/* Kamba (Kenya) */
-  {"kar",	HB_TAG('K','R','N',' ')},	/* Karen [collection] */
-/*{"kaw",	HB_TAG('K','A','W',' ')},*/	/* Kawi (Old Javanese) */
-  {"kbd",	HB_TAG('K','A','B',' ')},	/* Kabardian */
-  {"kby",	HB_TAG('K','N','R',' ')},	/* Manga Kanuri -> Kanuri */
-  {"kca",	HB_TAG('K','H','K',' ')},	/* Khanty -> Khanty-Kazim */
-  {"kca",	HB_TAG('K','H','S',' ')},	/* Khanty -> Khanty-Shurishkar */
-  {"kca",	HB_TAG('K','H','V',' ')},	/* Khanty -> Khanty-Vakhi */
-  {"kcn",	HB_TAG('C','P','P',' ')},	/* Nubi -> Creoles */
-/*{"kde",	HB_TAG('K','D','E',' ')},*/	/* Makonde */
-  {"kdr",	HB_TAG('K','R','M',' ')},	/* Karaim */
-  {"kdt",	HB_TAG('K','U','Y',' ')},	/* Kuy */
-  {"kea",	HB_TAG('K','E','A',' ')},	/* Kabuverdianu (Crioulo) */
-  {"kea",	HB_TAG('C','P','P',' ')},	/* Kabuverdianu -> Creoles */
-  {"keb",	HB_TAG_NONE	       },	/* Kélé != Kebena */
-  {"kek",	HB_TAG('K','E','K',' ')},	/* Kekchi */
-  {"kek",	HB_TAG('M','Y','N',' ')},	/* Kekchí -> Mayan */
-  {"kex",	HB_TAG('K','K','N',' ')},	/* Kukna -> Kokni */
-  {"kfa",	HB_TAG('K','O','D',' ')},	/* Kodava -> Kodagu */
-  {"kfr",	HB_TAG('K','A','C',' ')},	/* Kachhi -> Kachchi */
-  {"kfx",	HB_TAG('K','U','L',' ')},	/* Kullu Pahari -> Kulvi */
-  {"kfy",	HB_TAG('K','M','N',' ')},	/* Kumaoni */
-  {"kg",	HB_TAG('K','O','N','0')},	/* Kongo [macrolanguage] */
-  {"kge",	HB_TAG_NONE	       },	/* Komering != Khutsuri Georgian */
-  {"kha",	HB_TAG('K','S','I',' ')},	/* Khasi */
-  {"khb",	HB_TAG('X','B','D',' ')},	/* Lü */
-  {"khk",	HB_TAG('M','N','G',' ')},	/* Halh Mongolian -> Mongolian */
-  {"khn",	HB_TAG_NONE	       },	/* Khandesi != Khamti Shan (Microsoft fonts) */
-  {"khs",	HB_TAG_NONE	       },	/* Kasua != Khanty-Shurishkar */
-  {"kht",	HB_TAG('K','H','T',' ')},	/* Khamti -> Khamti Shan */
-  {"kht",	HB_TAG('K','H','N',' ')},	/* Khamti -> Khamti Shan (Microsoft fonts) */
-  {"khv",	HB_TAG_NONE	       },	/* Khvarshi != Khanty-Vakhi */
-/*{"khw",	HB_TAG('K','H','W',' ')},*/	/* Khowar */
-  {"ki",	HB_TAG('K','I','K',' ')},	/* Kikuyu (Gikuyu) */
-  {"kis",	HB_TAG_NONE	       },	/* Kis != Kisii */
-  {"kiu",	HB_TAG('K','I','U',' ')},	/* Kirmanjki */
-  {"kiu",	HB_TAG('Z','Z','A',' ')},	/* Kirmanjki -> Zazaki */
-  {"kj",	HB_TAG('K','U','A',' ')},	/* Kuanyama */
-  {"kjb",	HB_TAG('M','Y','N',' ')},	/* Q'anjob'al -> Mayan */
-/*{"kjd",	HB_TAG('K','J','D',' ')},*/	/* Southern Kiwai */
-  {"kjh",	HB_TAG('K','H','A',' ')},	/* Khakas -> Khakass */
-  {"kjp",	HB_TAG('K','J','P',' ')},	/* Pwo Eastern Karen -> Eastern Pwo Karen */
-  {"kjp",	HB_TAG('K','R','N',' ')},	/* Pwo Eastern Karen -> Karen */
-  {"kjt",	HB_TAG('K','R','N',' ')},	/* Phrae Pwo Karen -> Karen */
-/*{"kjz",	HB_TAG('K','J','Z',' ')},*/	/* Bumthangkha */
-  {"kk",	HB_TAG('K','A','Z',' ')},	/* Kazakh */
-  {"kkn",	HB_TAG_NONE	       },	/* Kon Keu != Kokni */
-  {"kkz",	HB_TAG('A','T','H',' ')},	/* Kaska -> Athapaskan */
-  {"kl",	HB_TAG('G','R','N',' ')},	/* Greenlandic */
-  {"klm",	HB_TAG_NONE	       },	/* Migum != Kalmyk */
-  {"kln",	HB_TAG('K','A','L',' ')},	/* Kalenjin [macrolanguage] */
-  {"km",	HB_TAG('K','H','M',' ')},	/* Khmer */
-  {"kmb",	HB_TAG('M','B','N',' ')},	/* Kimbundu -> Mbundu */
-  {"kmn",	HB_TAG_NONE	       },	/* Awtuw != Kumaoni */
-  {"kmo",	HB_TAG_NONE	       },	/* Kwoma != Komo */
-  {"kmr",	HB_TAG('K','U','R',' ')},	/* Northern Kurdish -> Kurdish */
-  {"kms",	HB_TAG_NONE	       },	/* Kamasau != Komso */
-  {"kmv",	HB_TAG('C','P','P',' ')},	/* Karipúna Creole French -> Creoles */
-  {"kmw",	HB_TAG('K','M','O',' ')},	/* Komo (Democratic Republic of Congo) */
-/*{"kmz",	HB_TAG('K','M','Z',' ')},*/	/* Khorasani Turkish -> Khorasani Turkic */
-  {"kn",	HB_TAG('K','A','N',' ')},	/* Kannada */
-  {"knc",	HB_TAG('K','N','R',' ')},	/* Central Kanuri -> Kanuri */
-  {"kng",	HB_TAG('K','O','N','0')},	/* Koongo -> Kongo */
-  {"knj",	HB_TAG('M','Y','N',' ')},	/* Western Kanjobal -> Mayan */
-  {"knn",	HB_TAG('K','O','K',' ')},	/* Konkani */
-  {"knr",	HB_TAG_NONE	       },	/* Kaningra != Kanuri */
-  {"ko",	HB_TAG('K','O','R',' ')},	/* Korean */
-  {"ko",	HB_TAG('K','O','H',' ')},	/* Korean -> Korean Old Hangul */
-  {"kod",	HB_TAG_NONE	       },	/* Kodi != Kodagu */
-  {"koh",	HB_TAG_NONE	       },	/* Koyo != Korean Old Hangul */
-  {"koi",	HB_TAG('K','O','P',' ')},	/* Komi-Permyak */
-  {"koi",	HB_TAG('K','O','M',' ')},	/* Komi-Permyak -> Komi */
-/*{"kok",	HB_TAG('K','O','K',' ')},*/	/* Konkani [macrolanguage] */
-  {"kop",	HB_TAG_NONE	       },	/* Waube != Komi-Permyak */
-/*{"kos",	HB_TAG('K','O','S',' ')},*/	/* Kosraean */
-  {"koy",	HB_TAG('A','T','H',' ')},	/* Koyukon -> Athapaskan */
-  {"koz",	HB_TAG_NONE	       },	/* Korak != Komi-Zyrian */
-  {"kpe",	HB_TAG('K','P','L',' ')},	/* Kpelle [macrolanguage] */
-  {"kpl",	HB_TAG_NONE	       },	/* Kpala != Kpelle */
-  {"kpp",	HB_TAG('K','R','N',' ')},	/* Paku Karen (retired code) -> Karen */
-  {"kpv",	HB_TAG('K','O','Z',' ')},	/* Komi-Zyrian */
-  {"kpv",	HB_TAG('K','O','M',' ')},	/* Komi-Zyrian -> Komi */
-  {"kpy",	HB_TAG('K','Y','K',' ')},	/* Koryak */
-  {"kqs",	HB_TAG('K','I','S',' ')},	/* Northern Kissi -> Kisii */
-  {"kqy",	HB_TAG('K','R','T',' ')},	/* Koorete */
-  {"kr",	HB_TAG('K','N','R',' ')},	/* Kanuri [macrolanguage] */
-  {"krc",	HB_TAG('K','A','R',' ')},	/* Karachay-Balkar -> Karachay */
-  {"krc",	HB_TAG('B','A','L',' ')},	/* Karachay-Balkar -> Balkar */
-  {"kri",	HB_TAG('K','R','I',' ')},	/* Krio */
-  {"kri",	HB_TAG('C','P','P',' ')},	/* Krio -> Creoles */
-  {"krk",	HB_TAG_NONE	       },	/* Kerek != Karakalpak */
-/*{"krl",	HB_TAG('K','R','L',' ')},*/	/* Karelian */
-  {"krm",	HB_TAG_NONE	       },	/* Krim (retired code) != Karaim */
-  {"krn",	HB_TAG_NONE	       },	/* Sapo != Karen */
-  {"krt",	HB_TAG('K','N','R',' ')},	/* Tumari Kanuri -> Kanuri */
-  {"kru",	HB_TAG('K','U','U',' ')},	/* Kurukh */
-  {"ks",	HB_TAG('K','S','H',' ')},	/* Kashmiri */
-  {"ksh",	HB_TAG('K','S','H','0')},	/* Kölsch -> Ripuarian */
-  {"ksi",	HB_TAG_NONE	       },	/* Krisa != Khasi */
-  {"ksm",	HB_TAG_NONE	       },	/* Kumba != Kildin Sami */
-  {"kss",	HB_TAG('K','I','S',' ')},	/* Southern Kisi -> Kisii */
-  {"ksw",	HB_TAG('K','S','W',' ')},	/* S’gaw Karen */
-  {"ksw",	HB_TAG('K','R','N',' ')},	/* S'gaw Karen -> Karen */
-  {"ktb",	HB_TAG('K','E','B',' ')},	/* Kambaata -> Kebena */
-  {"ktu",	HB_TAG('K','O','N',' ')},	/* Kituba (Democratic Republic of Congo) -> Kikongo */
-  {"ktw",	HB_TAG('A','T','H',' ')},	/* Kato -> Athapaskan */
-  {"ku",	HB_TAG('K','U','R',' ')},	/* Kurdish [macrolanguage] */
-  {"kui",	HB_TAG_NONE	       },	/* Kuikúro-Kalapálo != Kui */
-  {"kul",	HB_TAG_NONE	       },	/* Kulere != Kulvi */
-/*{"kum",	HB_TAG('K','U','M',' ')},*/	/* Kumyk */
-  {"kuu",	HB_TAG('A','T','H',' ')},	/* Upper Kuskokwim -> Athapaskan */
-  {"kuw",	HB_TAG('B','A','D','0')},	/* Kpagua -> Banda */
-  {"kuy",	HB_TAG_NONE	       },	/* Kuuku-Ya'u != Kuy */
-  {"kv",	HB_TAG('K','O','M',' ')},	/* Komi [macrolanguage] */
-  {"kvb",	HB_TAG('M','L','Y',' ')},	/* Kubu -> Malay */
-  {"kvl",	HB_TAG('K','R','N',' ')},	/* Kayaw -> Karen */
-  {"kvq",	HB_TAG('K','R','N',' ')},	/* Geba Karen -> Karen */
-  {"kvr",	HB_TAG('M','L','Y',' ')},	/* Kerinci -> Malay */
-  {"kvt",	HB_TAG('K','R','N',' ')},	/* Lahta Karen -> Karen */
-  {"kvu",	HB_TAG('K','R','N',' ')},	/* Yinbaw Karen -> Karen */
-  {"kvy",	HB_TAG('K','R','N',' ')},	/* Yintale Karen -> Karen */
-  {"kw",	HB_TAG('C','O','R',' ')},	/* Cornish */
-/*{"kwk",	HB_TAG('K','W','K',' ')},*/	/* Kwakiutl -> Kwakʼwala */
-  {"kww",	HB_TAG('C','P','P',' ')},	/* Kwinti -> Creoles */
-  {"kwy",	HB_TAG('K','O','N','0')},	/* San Salvador Kongo -> Kongo */
-  {"kxc",	HB_TAG('K','M','S',' ')},	/* Konso -> Komso */
-  {"kxd",	HB_TAG('M','L','Y',' ')},	/* Brunei -> Malay */
-  {"kxf",	HB_TAG('K','R','N',' ')},	/* Manumanaw Karen -> Karen */
-  {"kxk",	HB_TAG('K','R','N',' ')},	/* Zayein Karen -> Karen */
-  {"kxl",	HB_TAG('K','U','U',' ')},	/* Nepali Kurux (retired code) -> Kurukh */
-  {"kxu",	HB_TAG('K','U','I',' ')},	/* Kui (India) (retired code) */
-  {"ky",	HB_TAG('K','I','R',' ')},	/* Kirghiz (Kyrgyz) */
-  {"kyk",	HB_TAG_NONE	       },	/* Kamayo != Koryak */
-  {"kyu",	HB_TAG('K','Y','U',' ')},	/* Western Kayah */
-  {"kyu",	HB_TAG('K','R','N',' ')},	/* Western Kayah -> Karen */
-  {"la",	HB_TAG('L','A','T',' ')},	/* Latin */
-  {"lac",	HB_TAG('M','Y','N',' ')},	/* Lacandon -> Mayan */
-  {"lad",	HB_TAG('J','U','D',' ')},	/* Ladino */
-  {"lah",	HB_TAG_NONE	       },	/* Lahnda [macrolanguage] != Lahuli */
-  {"lak",	HB_TAG_NONE	       },	/* Laka (Nigeria) (retired code) != Lak */
-  {"lam",	HB_TAG_NONE	       },	/* Lamba != Lambani */
-  {"laz",	HB_TAG_NONE	       },	/* Aribwatsa != Laz */
-  {"lb",	HB_TAG('L','T','Z',' ')},	/* Luxembourgish */
-  {"lbe",	HB_TAG('L','A','K',' ')},	/* Lak */
-  {"lbj",	HB_TAG('L','D','K',' ')},	/* Ladakhi */
-  {"lbl",	HB_TAG('B','I','K',' ')},	/* Libon Bikol -> Bikol */
-  {"lce",	HB_TAG('M','L','Y',' ')},	/* Loncong -> Malay */
-  {"lcf",	HB_TAG('M','L','Y',' ')},	/* Lubu -> Malay */
-  {"ldi",	HB_TAG('K','O','N','0')},	/* Laari -> Kongo */
-  {"ldk",	HB_TAG_NONE	       },	/* Leelau != Ladakhi */
-/*{"lef",	HB_TAG('L','E','F',' ')},*/	/* Lelemi */
-/*{"lez",	HB_TAG('L','E','Z',' ')},*/	/* Lezghian -> Lezgi */
-  {"lg",	HB_TAG('L','U','G',' ')},	/* Ganda */
-  {"li",	HB_TAG('L','I','M',' ')},	/* Limburgish */
-  {"lif",	HB_TAG('L','M','B',' ')},	/* Limbu */
-/*{"lij",	HB_TAG('L','I','J',' ')},*/	/* Ligurian */
-  {"lir",	HB_TAG('C','P','P',' ')},	/* Liberian English -> Creoles */
-/*{"lis",	HB_TAG('L','I','S',' ')},*/	/* Lisu */
-  {"liw",	HB_TAG('M','L','Y',' ')},	/* Col -> Malay */
-  {"liy",	HB_TAG('B','A','D','0')},	/* Banda-Bambari -> Banda */
-/*{"ljp",	HB_TAG('L','J','P',' ')},*/	/* Lampung Api -> Lampung */
-  {"lkb",	HB_TAG('L','U','H',' ')},	/* Kabras -> Luyia */
-/*{"lki",	HB_TAG('L','K','I',' ')},*/	/* Laki */
-  {"lko",	HB_TAG('L','U','H',' ')},	/* Khayo -> Luyia */
-  {"lks",	HB_TAG('L','U','H',' ')},	/* Kisa -> Luyia */
-  {"lld",	HB_TAG('L','A','D',' ')},	/* Ladin */
-  {"lma",	HB_TAG_NONE	       },	/* East Limba != Low Mari */
-  {"lmb",	HB_TAG_NONE	       },	/* Merei != Limbu */
-  {"lmn",	HB_TAG('L','A','M',' ')},	/* Lambadi -> Lambani */
-/*{"lmo",	HB_TAG('L','M','O',' ')},*/	/* Lombard */
-  {"lmw",	HB_TAG_NONE	       },	/* Lake Miwok != Lomwe */
-  {"ln",	HB_TAG('L','I','N',' ')},	/* Lingala */
-  {"lna",	HB_TAG('B','A','D','0')},	/* Langbashe -> Banda */
-  {"lnl",	HB_TAG('B','A','D','0')},	/* South Central Banda -> Banda */
-  {"lo",	HB_TAG('L','A','O',' ')},	/* Lao */
-/*{"lom",	HB_TAG('L','O','M',' ')},*/	/* Loma (Liberia) */
-  {"lou",	HB_TAG('C','P','P',' ')},	/* Louisiana Creole -> Creoles */
-/*{"lpo",	HB_TAG('L','P','O',' ')},*/	/* Lipo */
-/*{"lrc",	HB_TAG('L','R','C',' ')},*/	/* Northern Luri -> Luri */
-  {"lri",	HB_TAG('L','U','H',' ')},	/* Marachi -> Luyia */
-  {"lrm",	HB_TAG('L','U','H',' ')},	/* Marama -> Luyia */
-  {"lrt",	HB_TAG('C','P','P',' ')},	/* Larantuka Malay -> Creoles */
-  {"lsb",	HB_TAG_NONE	       },	/* Burundian Sign Language != Lower Sorbian */
-  {"lsm",	HB_TAG('L','U','H',' ')},	/* Saamia -> Luyia */
-  {"lt",	HB_TAG('L','T','H',' ')},	/* Lithuanian */
-  {"ltg",	HB_TAG('L','V','I',' ')},	/* Latgalian -> Latvian */
-  {"lth",	HB_TAG_NONE	       },	/* Thur != Lithuanian */
-  {"lto",	HB_TAG('L','U','H',' ')},	/* Tsotso -> Luyia */
-  {"lts",	HB_TAG('L','U','H',' ')},	/* Tachoni -> Luyia */
-  {"lu",	HB_TAG('L','U','B',' ')},	/* Luba-Katanga */
-/*{"lua",	HB_TAG('L','U','A',' ')},*/	/* Luba-Lulua */
-/*{"luo",	HB_TAG('L','U','O',' ')},*/	/* Luo (Kenya and Tanzania) */
-  {"lus",	HB_TAG('M','I','Z',' ')},	/* Lushai -> Mizo */
-  {"lus",	HB_TAG('Q','I','N',' ')},	/* Lushai -> Chin */
-  {"luy",	HB_TAG('L','U','H',' ')},	/* Luyia [macrolanguage] */
-  {"luz",	HB_TAG('L','R','C',' ')},	/* Southern Luri -> Luri */
-  {"lv",	HB_TAG('L','V','I',' ')},	/* Latvian [macrolanguage] */
-  {"lvi",	HB_TAG_NONE	       },	/* Lavi != Latvian */
-  {"lvs",	HB_TAG('L','V','I',' ')},	/* Standard Latvian -> Latvian */
-  {"lwg",	HB_TAG('L','U','H',' ')},	/* Wanga -> Luyia */
-  {"lzh",	HB_TAG('Z','H','T',' ')},	/* Literary Chinese -> Chinese, Traditional */
-  {"lzz",	HB_TAG('L','A','Z',' ')},	/* Laz */
-/*{"mad",	HB_TAG('M','A','D',' ')},*/	/* Madurese -> Madura */
-/*{"mag",	HB_TAG('M','A','G',' ')},*/	/* Magahi */
-  {"mai",	HB_TAG('M','T','H',' ')},	/* Maithili */
-  {"maj",	HB_TAG_NONE	       },	/* Jalapa De Díaz Mazatec != Majang */
-  {"mak",	HB_TAG('M','K','R',' ')},	/* Makasar */
-  {"mam",	HB_TAG('M','A','M',' ')},	/* Mam */
-  {"mam",	HB_TAG('M','Y','N',' ')},	/* Mam -> Mayan */
-  {"man",	HB_TAG('M','N','K',' ')},	/* Mandingo [macrolanguage] -> Maninka */
-  {"map",	HB_TAG_NONE	       },	/* Austronesian [collection] != Mapudungun */
-  {"maw",	HB_TAG_NONE	       },	/* Mampruli != Marwari */
-  {"max",	HB_TAG('M','L','Y',' ')},	/* North Moluccan Malay -> Malay */
-  {"max",	HB_TAG('C','P','P',' ')},	/* North Moluccan Malay -> Creoles */
-  {"mbf",	HB_TAG('C','P','P',' ')},	/* Baba Malay -> Creoles */
-  {"mbn",	HB_TAG_NONE	       },	/* Macaguán != Mbundu */
-/*{"mbo",	HB_TAG('M','B','O',' ')},*/	/* Mbo (Cameroon) */
-  {"mch",	HB_TAG_NONE	       },	/* Maquiritari != Manchu */
-  {"mcm",	HB_TAG('C','P','P',' ')},	/* Malaccan Creole Portuguese -> Creoles */
-  {"mcr",	HB_TAG_NONE	       },	/* Menya != Moose Cree */
-  {"mct",	HB_TAG('B','T','I',' ')},	/* Mengisa -> Beti */
-  {"mde",	HB_TAG_NONE	       },	/* Maba (Chad) != Mende */
-  {"mdf",	HB_TAG('M','O','K',' ')},	/* Moksha */
-/*{"mdr",	HB_TAG('M','D','R',' ')},*/	/* Mandar */
-  {"mdy",	HB_TAG('M','L','E',' ')},	/* Male (Ethiopia) */
-  {"men",	HB_TAG('M','D','E',' ')},	/* Mende (Sierra Leone) */
-  {"meo",	HB_TAG('M','L','Y',' ')},	/* Kedah Malay -> Malay */
-/*{"mer",	HB_TAG('M','E','R',' ')},*/	/* Meru */
-  {"mfa",	HB_TAG('M','F','A',' ')},	/* Pattani Malay */
-  {"mfa",	HB_TAG('M','L','Y',' ')},	/* Pattani Malay -> Malay */
-  {"mfb",	HB_TAG('M','L','Y',' ')},	/* Bangka -> Malay */
-  {"mfe",	HB_TAG('M','F','E',' ')},	/* Morisyen */
-  {"mfe",	HB_TAG('C','P','P',' ')},	/* Morisyen -> Creoles */
-  {"mfp",	HB_TAG('C','P','P',' ')},	/* Makassar Malay -> Creoles */
-  {"mg",	HB_TAG('M','L','G',' ')},	/* Malagasy [macrolanguage] */
-  {"mh",	HB_TAG('M','A','H',' ')},	/* Marshallese */
-  {"mhc",	HB_TAG('M','Y','N',' ')},	/* Mocho -> Mayan */
-  {"mhr",	HB_TAG('L','M','A',' ')},	/* Eastern Mari -> Low Mari */
-  {"mhv",	HB_TAG('A','R','K',' ')},	/* Arakanese (retired code) -> Rakhine */
-  {"mi",	HB_TAG('M','R','I',' ')},	/* Maori */
-  {"min",	HB_TAG('M','I','N',' ')},	/* Minangkabau */
-  {"min",	HB_TAG('M','L','Y',' ')},	/* Minangkabau -> Malay */
-  {"miz",	HB_TAG_NONE	       },	/* Coatzospan Mixtec != Mizo */
-  {"mk",	HB_TAG('M','K','D',' ')},	/* Macedonian */
-  {"mkn",	HB_TAG('C','P','P',' ')},	/* Kupang Malay -> Creoles */
-  {"mkr",	HB_TAG_NONE	       },	/* Malas != Makasar */
-  {"mku",	HB_TAG('M','N','K',' ')},	/* Konyanka Maninka -> Maninka */
-/*{"mkw",	HB_TAG('M','K','W',' ')},*/	/* Kituba (Congo) */
-  {"ml",	HB_TAG('M','A','L',' ')},	/* Malayalam -> Malayalam Traditional */
-  {"ml",	HB_TAG('M','L','R',' ')},	/* Malayalam -> Malayalam Reformed */
-  {"mle",	HB_TAG_NONE	       },	/* Manambu != Male */
-  {"mln",	HB_TAG_NONE	       },	/* Malango != Malinke */
-  {"mlq",	HB_TAG('M','L','N',' ')},	/* Western Maninkakan -> Malinke */
-  {"mlq",	HB_TAG('M','N','K',' ')},	/* Western Maninkakan -> Maninka */
-  {"mlr",	HB_TAG_NONE	       },	/* Vame != Malayalam Reformed */
-  {"mmr",	HB_TAG('H','M','N',' ')},	/* Western Xiangxi Miao -> Hmong */
-  {"mn",	HB_TAG('M','N','G',' ')},	/* Mongolian [macrolanguage] */
-  {"mnc",	HB_TAG('M','C','H',' ')},	/* Manchu */
-  {"mnd",	HB_TAG_NONE	       },	/* Mondé != Mandinka */
-  {"mng",	HB_TAG_NONE	       },	/* Eastern Mnong != Mongolian */
-  {"mnh",	HB_TAG('B','A','D','0')},	/* Mono (Democratic Republic of Congo) -> Banda */
-/*{"mni",	HB_TAG('M','N','I',' ')},*/	/* Manipuri */
-  {"mnk",	HB_TAG('M','N','D',' ')},	/* Mandinka */
-  {"mnk",	HB_TAG('M','N','K',' ')},	/* Mandinka -> Maninka */
-  {"mnp",	HB_TAG('Z','H','S',' ')},	/* Min Bei Chinese -> Chinese, Simplified */
-  {"mns",	HB_TAG('M','A','N',' ')},	/* Mansi */
-  {"mnw",	HB_TAG('M','O','N',' ')},	/* Mon */
-  {"mnw",	HB_TAG('M','O','N','T')},	/* Mon -> Thailand Mon */
-  {"mnx",	HB_TAG_NONE	       },	/* Manikion != Manx */
-  {"mo",	HB_TAG('M','O','L',' ')},	/* Moldavian (retired code) */
-  {"mo",	HB_TAG('R','O','M',' ')},	/* Moldavian (retired code) -> Romanian */
-  {"mod",	HB_TAG('C','P','P',' ')},	/* Mobilian -> Creoles */
-/*{"moh",	HB_TAG('M','O','H',' ')},*/	/* Mohawk */
-  {"mok",	HB_TAG_NONE	       },	/* Morori != Moksha */
-  {"mop",	HB_TAG('M','Y','N',' ')},	/* Mopán Maya -> Mayan */
-  {"mor",	HB_TAG_NONE	       },	/* Moro != Moroccan */
-/*{"mos",	HB_TAG('M','O','S',' ')},*/	/* Mossi */
-  {"mpe",	HB_TAG('M','A','J',' ')},	/* Majang */
-  {"mqg",	HB_TAG('M','L','Y',' ')},	/* Kota Bangun Kutai Malay -> Malay */
-  {"mr",	HB_TAG('M','A','R',' ')},	/* Marathi */
-  {"mrh",	HB_TAG('Q','I','N',' ')},	/* Mara Chin -> Chin */
-  {"mrj",	HB_TAG('H','M','A',' ')},	/* Western Mari -> High Mari */
-  {"ms",	HB_TAG('M','L','Y',' ')},	/* Malay [macrolanguage] */
-  {"msc",	HB_TAG('M','N','K',' ')},	/* Sankaran Maninka -> Maninka */
-  {"msh",	HB_TAG('M','L','G',' ')},	/* Masikoro Malagasy -> Malagasy */
-  {"msi",	HB_TAG('M','L','Y',' ')},	/* Sabah Malay -> Malay */
-  {"msi",	HB_TAG('C','P','P',' ')},	/* Sabah Malay -> Creoles */
-  {"mt",	HB_TAG('M','T','S',' ')},	/* Maltese */
-  {"mth",	HB_TAG_NONE	       },	/* Munggui != Maithili */
-  {"mtr",	HB_TAG('M','A','W',' ')},	/* Mewari -> Marwari */
-  {"mts",	HB_TAG_NONE	       },	/* Yora != Maltese */
-  {"mud",	HB_TAG('C','P','P',' ')},	/* Mednyj Aleut -> Creoles */
-  {"mui",	HB_TAG('M','L','Y',' ')},	/* Musi -> Malay */
-  {"mun",	HB_TAG_NONE	       },	/* Munda [collection] != Mundari */
-  {"mup",	HB_TAG('R','A','J',' ')},	/* Malvi -> Rajasthani */
-  {"muq",	HB_TAG('H','M','N',' ')},	/* Eastern Xiangxi Miao -> Hmong */
-/*{"mus",	HB_TAG('M','U','S',' ')},*/	/* Creek -> Muscogee */
-  {"mvb",	HB_TAG('A','T','H',' ')},	/* Mattole -> Athapaskan */
-  {"mve",	HB_TAG('M','A','W',' ')},	/* Marwari (Pakistan) */
-  {"mvf",	HB_TAG('M','N','G',' ')},	/* Peripheral Mongolian -> Mongolian */
-  {"mwk",	HB_TAG('M','N','K',' ')},	/* Kita Maninkakan -> Maninka */
-/*{"mwl",	HB_TAG('M','W','L',' ')},*/	/* Mirandese */
-  {"mwq",	HB_TAG('Q','I','N',' ')},	/* Mün Chin -> Chin */
-  {"mwr",	HB_TAG('M','A','W',' ')},	/* Marwari [macrolanguage] */
-  {"mww",	HB_TAG('M','W','W',' ')},	/* Hmong Daw */
-  {"mww",	HB_TAG('H','M','N',' ')},	/* Hmong Daw -> Hmong */
-  {"my",	HB_TAG('B','R','M',' ')},	/* Burmese */
-  {"mym",	HB_TAG('M','E','N',' ')},	/* Me’en */
-/*{"myn",	HB_TAG('M','Y','N',' ')},*/	/* Mayan [collection] */
-  {"myq",	HB_TAG('M','N','K',' ')},	/* Forest Maninka (retired code) -> Maninka */
-  {"myv",	HB_TAG('E','R','Z',' ')},	/* Erzya */
-  {"mzb",	HB_TAG('B','B','R',' ')},	/* Tumzabt -> Berber */
-/*{"mzn",	HB_TAG('M','Z','N',' ')},*/	/* Mazanderani */
-  {"mzs",	HB_TAG('C','P','P',' ')},	/* Macanese -> Creoles */
-  {"na",	HB_TAG('N','A','U',' ')},	/* Nauru -> Nauruan */
-  {"nag",	HB_TAG('N','A','G',' ')},	/* Naga Pidgin -> Naga-Assamese */
-  {"nag",	HB_TAG('C','P','P',' ')},	/* Naga Pidgin -> Creoles */
-/*{"nah",	HB_TAG('N','A','H',' ')},*/	/* Nahuatl [collection] */
-  {"nan",	HB_TAG('Z','H','S',' ')},	/* Min Nan Chinese -> Chinese, Simplified */
-/*{"nap",	HB_TAG('N','A','P',' ')},*/	/* Neapolitan */
-  {"nas",	HB_TAG_NONE	       },	/* Naasioi != Naskapi */
-  {"naz",	HB_TAG('N','A','H',' ')},	/* Coatepec Nahuatl -> Nahuatl */
-  {"nb",	HB_TAG('N','O','R',' ')},	/* Norwegian Bokmål -> Norwegian */
-  {"nch",	HB_TAG('N','A','H',' ')},	/* Central Huasteca Nahuatl -> Nahuatl */
-  {"nci",	HB_TAG('N','A','H',' ')},	/* Classical Nahuatl -> Nahuatl */
-  {"ncj",	HB_TAG('N','A','H',' ')},	/* Northern Puebla Nahuatl -> Nahuatl */
-  {"ncl",	HB_TAG('N','A','H',' ')},	/* Michoacán Nahuatl -> Nahuatl */
-  {"ncr",	HB_TAG_NONE	       },	/* Ncane != N-Cree */
-  {"ncx",	HB_TAG('N','A','H',' ')},	/* Central Puebla Nahuatl -> Nahuatl */
-  {"nd",	HB_TAG('N','D','B',' ')},	/* North Ndebele -> Ndebele */
-  {"ndb",	HB_TAG_NONE	       },	/* Kenswei Nsei != Ndebele */
-/*{"ndc",	HB_TAG('N','D','C',' ')},*/	/* Ndau */
-  {"ndg",	HB_TAG_NONE	       },	/* Ndengereko != Ndonga */
-/*{"nds",	HB_TAG('N','D','S',' ')},*/	/* Low Saxon */
-  {"ne",	HB_TAG('N','E','P',' ')},	/* Nepali [macrolanguage] */
-  {"nef",	HB_TAG('C','P','P',' ')},	/* Nefamese -> Creoles */
-/*{"new",	HB_TAG('N','E','W',' ')},*/	/* Newari */
-  {"ng",	HB_TAG('N','D','G',' ')},	/* Ndonga */
-/*{"nga",	HB_TAG('N','G','A',' ')},*/	/* Ngbaka */
-  {"ngl",	HB_TAG('L','M','W',' ')},	/* Lomwe */
-  {"ngm",	HB_TAG('C','P','P',' ')},	/* Ngatik Men's Creole -> Creoles */
-  {"ngo",	HB_TAG('S','X','T',' ')},	/* Ngoni (retired code) -> Sutu */
-  {"ngr",	HB_TAG_NONE	       },	/* Engdewu != Nagari */
-  {"ngu",	HB_TAG('N','A','H',' ')},	/* Guerrero Nahuatl -> Nahuatl */
-  {"nhc",	HB_TAG('N','A','H',' ')},	/* Tabasco Nahuatl -> Nahuatl */
-  {"nhd",	HB_TAG('G','U','A',' ')},	/* Chiripá -> Guarani */
-  {"nhe",	HB_TAG('N','A','H',' ')},	/* Eastern Huasteca Nahuatl -> Nahuatl */
-  {"nhg",	HB_TAG('N','A','H',' ')},	/* Tetelcingo Nahuatl -> Nahuatl */
-  {"nhi",	HB_TAG('N','A','H',' ')},	/* Zacatlán-Ahuacatlán-Tepetzintla Nahuatl -> Nahuatl */
-  {"nhk",	HB_TAG('N','A','H',' ')},	/* Isthmus-Cosoleacaque Nahuatl -> Nahuatl */
-  {"nhm",	HB_TAG('N','A','H',' ')},	/* Morelos Nahuatl -> Nahuatl */
-  {"nhn",	HB_TAG('N','A','H',' ')},	/* Central Nahuatl -> Nahuatl */
-  {"nhp",	HB_TAG('N','A','H',' ')},	/* Isthmus-Pajapan Nahuatl -> Nahuatl */
-  {"nhq",	HB_TAG('N','A','H',' ')},	/* Huaxcaleca Nahuatl -> Nahuatl */
-  {"nht",	HB_TAG('N','A','H',' ')},	/* Ometepec Nahuatl -> Nahuatl */
-  {"nhv",	HB_TAG('N','A','H',' ')},	/* Temascaltepec Nahuatl -> Nahuatl */
-  {"nhw",	HB_TAG('N','A','H',' ')},	/* Western Huasteca Nahuatl -> Nahuatl */
-  {"nhx",	HB_TAG('N','A','H',' ')},	/* Isthmus-Mecayapan Nahuatl -> Nahuatl */
-  {"nhy",	HB_TAG('N','A','H',' ')},	/* Northern Oaxaca Nahuatl -> Nahuatl */
-  {"nhz",	HB_TAG('N','A','H',' ')},	/* Santa María La Alta Nahuatl -> Nahuatl */
-  {"niq",	HB_TAG('K','A','L',' ')},	/* Nandi -> Kalenjin */
-  {"nis",	HB_TAG_NONE	       },	/* Nimi != Nisi */
-/*{"niu",	HB_TAG('N','I','U',' ')},*/	/* Niuean */
-  {"niv",	HB_TAG('G','I','L',' ')},	/* Gilyak */
-  {"njt",	HB_TAG('C','P','P',' ')},	/* Ndyuka-Trio Pidgin -> Creoles */
-  {"njz",	HB_TAG('N','I','S',' ')},	/* Nyishi -> Nisi */
-  {"nko",	HB_TAG_NONE	       },	/* Nkonya != N’Ko */
-  {"nkx",	HB_TAG('I','J','O',' ')},	/* Nkoroo -> Ijo */
-  {"nl",	HB_TAG('N','L','D',' ')},	/* Dutch */
-  {"nla",	HB_TAG('B','M','L',' ')},	/* Ngombale -> Bamileke */
-  {"nle",	HB_TAG('L','U','H',' ')},	/* East Nyala -> Luyia */
-  {"nln",	HB_TAG('N','A','H',' ')},	/* Durango Nahuatl (retired code) -> Nahuatl */
-  {"nlv",	HB_TAG('N','A','H',' ')},	/* Orizaba Nahuatl -> Nahuatl */
-  {"nn",	HB_TAG('N','Y','N',' ')},	/* Norwegian Nynorsk (Nynorsk, Norwegian) */
-  {"nnh",	HB_TAG('B','M','L',' ')},	/* Ngiemboon -> Bamileke */
-  {"nnz",	HB_TAG('B','M','L',' ')},	/* Nda'nda' -> Bamileke */
-  {"no",	HB_TAG('N','O','R',' ')},	/* Norwegian [macrolanguage] */
-  {"nod",	HB_TAG('N','T','A',' ')},	/* Northern Thai -> Northern Tai */
-/*{"noe",	HB_TAG('N','O','E',' ')},*/	/* Nimadi */
-/*{"nog",	HB_TAG('N','O','G',' ')},*/	/* Nogai */
-/*{"nov",	HB_TAG('N','O','V',' ')},*/	/* Novial */
-  {"npi",	HB_TAG('N','E','P',' ')},	/* Nepali */
-  {"npl",	HB_TAG('N','A','H',' ')},	/* Southeastern Puebla Nahuatl -> Nahuatl */
-  {"nqo",	HB_TAG('N','K','O',' ')},	/* N’Ko */
-  {"nr",	HB_TAG('N','D','B',' ')},	/* South Ndebele -> Ndebele */
-  {"nsk",	HB_TAG('N','A','S',' ')},	/* Naskapi */
-  {"nsm",	HB_TAG_NONE	       },	/* Sumi Naga != Northern Sami */
-/*{"nso",	HB_TAG('N','S','O',' ')},*/	/* Northern Sotho */
-  {"nsu",	HB_TAG('N','A','H',' ')},	/* Sierra Negra Nahuatl -> Nahuatl */
-  {"nto",	HB_TAG_NONE	       },	/* Ntomba != Esperanto */
-  {"nue",	HB_TAG('B','A','D','0')},	/* Ngundu -> Banda */
-  {"nuu",	HB_TAG('B','A','D','0')},	/* Ngbundu -> Banda */
-  {"nuz",	HB_TAG('N','A','H',' ')},	/* Tlamacazapa Nahuatl -> Nahuatl */
-  {"nv",	HB_TAG('N','A','V',' ')},	/* Navajo */
-  {"nv",	HB_TAG('A','T','H',' ')},	/* Navajo -> Athapaskan */
-  {"nwe",	HB_TAG('B','M','L',' ')},	/* Ngwe -> Bamileke */
-  {"ny",	HB_TAG('C','H','I',' ')},	/* Chichewa (Chewa, Nyanja) */
-  {"nyd",	HB_TAG('L','U','H',' ')},	/* Nyore -> Luyia */
-/*{"nym",	HB_TAG('N','Y','M',' ')},*/	/* Nyamwezi */
-  {"nyn",	HB_TAG('N','K','L',' ')},	/* Nyankole */
-/*{"nza",	HB_TAG('N','Z','A',' ')},*/	/* Tigon Mbembe -> Mbembe Tigon */
-  {"oc",	HB_TAG('O','C','I',' ')},	/* Occitan (post 1500) */
-  {"oj",	HB_TAG('O','J','B',' ')},	/* Ojibwa [macrolanguage] -> Ojibway */
-/*{"ojb",	HB_TAG('O','J','B',' ')},*/	/* Northwestern Ojibwa -> Ojibway */
-  {"ojc",	HB_TAG('O','J','B',' ')},	/* Central Ojibwa -> Ojibway */
-  {"ojg",	HB_TAG('O','J','B',' ')},	/* Eastern Ojibwa -> Ojibway */
-  {"ojs",	HB_TAG('O','C','R',' ')},	/* Severn Ojibwa -> Oji-Cree */
-  {"ojs",	HB_TAG('O','J','B',' ')},	/* Severn Ojibwa -> Ojibway */
-  {"ojw",	HB_TAG('O','J','B',' ')},	/* Western Ojibwa -> Ojibway */
-  {"okd",	HB_TAG('I','J','O',' ')},	/* Okodia -> Ijo */
-  {"oki",	HB_TAG('K','A','L',' ')},	/* Okiek -> Kalenjin */
-  {"okm",	HB_TAG('K','O','H',' ')},	/* Middle Korean (10th-16th cent.) -> Korean Old Hangul */
-  {"okr",	HB_TAG('I','J','O',' ')},	/* Kirike -> Ijo */
-  {"om",	HB_TAG('O','R','O',' ')},	/* Oromo [macrolanguage] */
-  {"onx",	HB_TAG('C','P','P',' ')},	/* Onin Based Pidgin -> Creoles */
-  {"oor",	HB_TAG('C','P','P',' ')},	/* Oorlams -> Creoles */
-  {"or",	HB_TAG('O','R','I',' ')},	/* Odia (formerly Oriya) [macrolanguage] */
-  {"orc",	HB_TAG('O','R','O',' ')},	/* Orma -> Oromo */
-  {"orn",	HB_TAG('M','L','Y',' ')},	/* Orang Kanaq -> Malay */
-  {"oro",	HB_TAG_NONE	       },	/* Orokolo != Oromo */
-  {"orr",	HB_TAG('I','J','O',' ')},	/* Oruma -> Ijo */
-  {"ors",	HB_TAG('M','L','Y',' ')},	/* Orang Seletar -> Malay */
-  {"ory",	HB_TAG('O','R','I',' ')},	/* Odia (formerly Oriya) */
-  {"os",	HB_TAG('O','S','S',' ')},	/* Ossetian */
-  {"otw",	HB_TAG('O','J','B',' ')},	/* Ottawa -> Ojibway */
-  {"oua",	HB_TAG('B','B','R',' ')},	/* Tagargrent -> Berber */
-  {"pa",	HB_TAG('P','A','N',' ')},	/* Punjabi */
-  {"paa",	HB_TAG_NONE	       },	/* Papuan [collection] != Palestinian Aramaic */
-/*{"pag",	HB_TAG('P','A','G',' ')},*/	/* Pangasinan */
-  {"pal",	HB_TAG_NONE	       },	/* Pahlavi != Pali */
-/*{"pam",	HB_TAG('P','A','M',' ')},*/	/* Pampanga -> Pampangan */
-  {"pap",	HB_TAG('P','A','P','0')},	/* Papiamento -> Papiamentu */
-  {"pap",	HB_TAG('C','P','P',' ')},	/* Papiamento -> Creoles */
-  {"pas",	HB_TAG_NONE	       },	/* Papasena != Pashto */
-/*{"pau",	HB_TAG('P','A','U',' ')},*/	/* Palauan */
-  {"pbt",	HB_TAG('P','A','S',' ')},	/* Southern Pashto -> Pashto */
-  {"pbu",	HB_TAG('P','A','S',' ')},	/* Northern Pashto -> Pashto */
-/*{"pcc",	HB_TAG('P','C','C',' ')},*/	/* Bouyei */
-/*{"pcd",	HB_TAG('P','C','D',' ')},*/	/* Picard */
-  {"pce",	HB_TAG('P','L','G',' ')},	/* Ruching Palaung -> Palaung */
-  {"pck",	HB_TAG('Q','I','N',' ')},	/* Paite Chin -> Chin */
-  {"pcm",	HB_TAG('C','P','P',' ')},	/* Nigerian Pidgin -> Creoles */
-/*{"pdc",	HB_TAG('P','D','C',' ')},*/	/* Pennsylvania German */
-  {"pdu",	HB_TAG('K','R','N',' ')},	/* Kayan -> Karen */
-  {"pea",	HB_TAG('C','P','P',' ')},	/* Peranakan Indonesian -> Creoles */
-  {"pel",	HB_TAG('M','L','Y',' ')},	/* Pekal -> Malay */
-  {"pes",	HB_TAG('F','A','R',' ')},	/* Iranian Persian -> Persian */
-  {"pey",	HB_TAG('C','P','P',' ')},	/* Petjo -> Creoles */
-  {"pga",	HB_TAG('A','R','A',' ')},	/* Sudanese Creole Arabic -> Arabic */
-  {"pga",	HB_TAG('C','P','P',' ')},	/* Sudanese Creole Arabic -> Creoles */
-/*{"phk",	HB_TAG('P','H','K',' ')},*/	/* Phake */
-  {"pi",	HB_TAG('P','A','L',' ')},	/* Pali */
-  {"pih",	HB_TAG('P','I','H',' ')},	/* Pitcairn-Norfolk -> Norfolk */
-  {"pih",	HB_TAG('C','P','P',' ')},	/* Pitcairn-Norfolk -> Creoles */
-  {"pil",	HB_TAG_NONE	       },	/* Yom != Filipino */
-  {"pis",	HB_TAG('C','P','P',' ')},	/* Pijin -> Creoles */
-  {"pkh",	HB_TAG('Q','I','N',' ')},	/* Pankhu -> Chin */
-  {"pko",	HB_TAG('K','A','L',' ')},	/* Pökoot -> Kalenjin */
-  {"pl",	HB_TAG('P','L','K',' ')},	/* Polish */
-  {"plg",	HB_TAG_NONE	       },	/* Pilagá != Palaung */
-  {"plk",	HB_TAG_NONE	       },	/* Kohistani Shina != Polish */
-  {"pll",	HB_TAG('P','L','G',' ')},	/* Shwe Palaung -> Palaung */
-  {"pln",	HB_TAG('C','P','P',' ')},	/* Palenquero -> Creoles */
-  {"plp",	HB_TAG('P','A','P',' ')},	/* Palpa (retired code) */
-  {"plt",	HB_TAG('M','L','G',' ')},	/* Plateau Malagasy -> Malagasy */
-  {"pml",	HB_TAG('C','P','P',' ')},	/* Lingua Franca -> Creoles */
-/*{"pms",	HB_TAG('P','M','S',' ')},*/	/* Piemontese */
-  {"pmy",	HB_TAG('C','P','P',' ')},	/* Papuan Malay -> Creoles */
-/*{"pnb",	HB_TAG('P','N','B',' ')},*/	/* Western Panjabi */
-  {"poc",	HB_TAG('M','Y','N',' ')},	/* Poqomam -> Mayan */
-  {"poh",	HB_TAG('P','O','H',' ')},	/* Poqomchi' -> Pocomchi */
-  {"poh",	HB_TAG('M','Y','N',' ')},	/* Poqomchi' -> Mayan */
-/*{"pon",	HB_TAG('P','O','N',' ')},*/	/* Pohnpeian */
-  {"pov",	HB_TAG('C','P','P',' ')},	/* Upper Guinea Crioulo -> Creoles */
-  {"ppa",	HB_TAG('B','A','G',' ')},	/* Pao (retired code) -> Baghelkhandi */
-  {"pre",	HB_TAG('C','P','P',' ')},	/* Principense -> Creoles */
-/*{"pro",	HB_TAG('P','R','O',' ')},*/	/* Old Provençal (to 1500) -> Provençal / Old Provençal */
-  {"prs",	HB_TAG('D','R','I',' ')},	/* Dari */
-  {"prs",	HB_TAG('F','A','R',' ')},	/* Dari -> Persian */
-  {"ps",	HB_TAG('P','A','S',' ')},	/* Pashto [macrolanguage] */
-  {"pse",	HB_TAG('M','L','Y',' ')},	/* Central Malay -> Malay */
-  {"pst",	HB_TAG('P','A','S',' ')},	/* Central Pashto -> Pashto */
-  {"pt",	HB_TAG('P','T','G',' ')},	/* Portuguese */
-  {"pub",	HB_TAG('Q','I','N',' ')},	/* Purum -> Chin */
-  {"puz",	HB_TAG('Q','I','N',' ')},	/* Purum Naga (retired code) -> Chin */
-  {"pwo",	HB_TAG('P','W','O',' ')},	/* Pwo Western Karen -> Western Pwo Karen */
-  {"pwo",	HB_TAG('K','R','N',' ')},	/* Pwo Western Karen -> Karen */
-  {"pww",	HB_TAG('K','R','N',' ')},	/* Pwo Northern Karen -> Karen */
-  {"qu",	HB_TAG('Q','U','Z',' ')},	/* Quechua [macrolanguage] */
-  {"qub",	HB_TAG('Q','W','H',' ')},	/* Huallaga Huánuco Quechua -> Quechua (Peru) */
-  {"qub",	HB_TAG('Q','U','Z',' ')},	/* Huallaga Huánuco Quechua -> Quechua */
-  {"quc",	HB_TAG('Q','U','C',' ')},	/* K’iche’ */
-  {"quc",	HB_TAG('M','Y','N',' ')},	/* K'iche' -> Mayan */
-  {"qud",	HB_TAG('Q','V','I',' ')},	/* Calderón Highland Quichua -> Quechua (Ecuador) */
-  {"qud",	HB_TAG('Q','U','Z',' ')},	/* Calderón Highland Quichua -> Quechua */
-  {"quf",	HB_TAG('Q','U','Z',' ')},	/* Lambayeque Quechua -> Quechua */
-  {"qug",	HB_TAG('Q','V','I',' ')},	/* Chimborazo Highland Quichua -> Quechua (Ecuador) */
-  {"qug",	HB_TAG('Q','U','Z',' ')},	/* Chimborazo Highland Quichua -> Quechua */
-  {"quh",	HB_TAG('Q','U','H',' ')},	/* South Bolivian Quechua -> Quechua (Bolivia) */
-  {"quh",	HB_TAG('Q','U','Z',' ')},	/* South Bolivian Quechua -> Quechua */
-  {"quk",	HB_TAG('Q','U','Z',' ')},	/* Chachapoyas Quechua -> Quechua */
-  {"qul",	HB_TAG('Q','U','H',' ')},	/* North Bolivian Quechua -> Quechua (Bolivia) */
-  {"qul",	HB_TAG('Q','U','Z',' ')},	/* North Bolivian Quechua -> Quechua */
-  {"qum",	HB_TAG('M','Y','N',' ')},	/* Sipacapense -> Mayan */
-  {"qup",	HB_TAG('Q','V','I',' ')},	/* Southern Pastaza Quechua -> Quechua (Ecuador) */
-  {"qup",	HB_TAG('Q','U','Z',' ')},	/* Southern Pastaza Quechua -> Quechua */
-  {"qur",	HB_TAG('Q','W','H',' ')},	/* Yanahuanca Pasco Quechua -> Quechua (Peru) */
-  {"qur",	HB_TAG('Q','U','Z',' ')},	/* Yanahuanca Pasco Quechua -> Quechua */
-  {"qus",	HB_TAG('Q','U','H',' ')},	/* Santiago del Estero Quichua -> Quechua (Bolivia) */
-  {"qus",	HB_TAG('Q','U','Z',' ')},	/* Santiago del Estero Quichua -> Quechua */
-  {"quv",	HB_TAG('M','Y','N',' ')},	/* Sacapulteco -> Mayan */
-  {"quw",	HB_TAG('Q','V','I',' ')},	/* Tena Lowland Quichua -> Quechua (Ecuador) */
-  {"quw",	HB_TAG('Q','U','Z',' ')},	/* Tena Lowland Quichua -> Quechua */
-  {"qux",	HB_TAG('Q','W','H',' ')},	/* Yauyos Quechua -> Quechua (Peru) */
-  {"qux",	HB_TAG('Q','U','Z',' ')},	/* Yauyos Quechua -> Quechua */
-  {"quy",	HB_TAG('Q','U','Z',' ')},	/* Ayacucho Quechua -> Quechua */
-/*{"quz",	HB_TAG('Q','U','Z',' ')},*/	/* Cusco Quechua -> Quechua */
-  {"qva",	HB_TAG('Q','W','H',' ')},	/* Ambo-Pasco Quechua -> Quechua (Peru) */
-  {"qva",	HB_TAG('Q','U','Z',' ')},	/* Ambo-Pasco Quechua -> Quechua */
-  {"qvc",	HB_TAG('Q','U','Z',' ')},	/* Cajamarca Quechua -> Quechua */
-  {"qve",	HB_TAG('Q','U','Z',' ')},	/* Eastern Apurímac Quechua -> Quechua */
-  {"qvh",	HB_TAG('Q','W','H',' ')},	/* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua (Peru) */
-  {"qvh",	HB_TAG('Q','U','Z',' ')},	/* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua */
-  {"qvi",	HB_TAG('Q','V','I',' ')},	/* Imbabura Highland Quichua -> Quechua (Ecuador) */
-  {"qvi",	HB_TAG('Q','U','Z',' ')},	/* Imbabura Highland Quichua -> Quechua */
-  {"qvj",	HB_TAG('Q','V','I',' ')},	/* Loja Highland Quichua -> Quechua (Ecuador) */
-  {"qvj",	HB_TAG('Q','U','Z',' ')},	/* Loja Highland Quichua -> Quechua */
-  {"qvl",	HB_TAG('Q','W','H',' ')},	/* Cajatambo North Lima Quechua -> Quechua (Peru) */
-  {"qvl",	HB_TAG('Q','U','Z',' ')},	/* Cajatambo North Lima Quechua -> Quechua */
-  {"qvm",	HB_TAG('Q','W','H',' ')},	/* Margos-Yarowilca-Lauricocha Quechua -> Quechua (Peru) */
-  {"qvm",	HB_TAG('Q','U','Z',' ')},	/* Margos-Yarowilca-Lauricocha Quechua -> Quechua */
-  {"qvn",	HB_TAG('Q','W','H',' ')},	/* North Junín Quechua -> Quechua (Peru) */
-  {"qvn",	HB_TAG('Q','U','Z',' ')},	/* North Junín Quechua -> Quechua */
-  {"qvo",	HB_TAG('Q','V','I',' ')},	/* Napo Lowland Quechua -> Quechua (Ecuador) */
-  {"qvo",	HB_TAG('Q','U','Z',' ')},	/* Napo Lowland Quechua -> Quechua */
-  {"qvp",	HB_TAG('Q','W','H',' ')},	/* Pacaraos Quechua -> Quechua (Peru) */
-  {"qvp",	HB_TAG('Q','U','Z',' ')},	/* Pacaraos Quechua -> Quechua */
-  {"qvs",	HB_TAG('Q','U','Z',' ')},	/* San Martín Quechua -> Quechua */
-  {"qvw",	HB_TAG('Q','W','H',' ')},	/* Huaylla Wanca Quechua -> Quechua (Peru) */
-  {"qvw",	HB_TAG('Q','U','Z',' ')},	/* Huaylla Wanca Quechua -> Quechua */
-  {"qvz",	HB_TAG('Q','V','I',' ')},	/* Northern Pastaza Quichua -> Quechua (Ecuador) */
-  {"qvz",	HB_TAG('Q','U','Z',' ')},	/* Northern Pastaza Quichua -> Quechua */
-  {"qwa",	HB_TAG('Q','W','H',' ')},	/* Corongo Ancash Quechua -> Quechua (Peru) */
-  {"qwa",	HB_TAG('Q','U','Z',' ')},	/* Corongo Ancash Quechua -> Quechua */
-  {"qwc",	HB_TAG('Q','U','Z',' ')},	/* Classical Quechua -> Quechua */
-  {"qwh",	HB_TAG('Q','W','H',' ')},	/* Huaylas Ancash Quechua -> Quechua (Peru) */
-  {"qwh",	HB_TAG('Q','U','Z',' ')},	/* Huaylas Ancash Quechua -> Quechua */
-  {"qws",	HB_TAG('Q','W','H',' ')},	/* Sihuas Ancash Quechua -> Quechua (Peru) */
-  {"qws",	HB_TAG('Q','U','Z',' ')},	/* Sihuas Ancash Quechua -> Quechua */
-  {"qwt",	HB_TAG('A','T','H',' ')},	/* Kwalhioqua-Tlatskanai -> Athapaskan */
-  {"qxa",	HB_TAG('Q','W','H',' ')},	/* Chiquián Ancash Quechua -> Quechua (Peru) */
-  {"qxa",	HB_TAG('Q','U','Z',' ')},	/* Chiquián Ancash Quechua -> Quechua */
-  {"qxc",	HB_TAG('Q','W','H',' ')},	/* Chincha Quechua -> Quechua (Peru) */
-  {"qxc",	HB_TAG('Q','U','Z',' ')},	/* Chincha Quechua -> Quechua */
-  {"qxh",	HB_TAG('Q','W','H',' ')},	/* Panao Huánuco Quechua -> Quechua (Peru) */
-  {"qxh",	HB_TAG('Q','U','Z',' ')},	/* Panao Huánuco Quechua -> Quechua */
-  {"qxl",	HB_TAG('Q','V','I',' ')},	/* Salasaca Highland Quichua -> Quechua (Ecuador) */
-  {"qxl",	HB_TAG('Q','U','Z',' ')},	/* Salasaca Highland Quichua -> Quechua */
-  {"qxn",	HB_TAG('Q','W','H',' ')},	/* Northern Conchucos Ancash Quechua -> Quechua (Peru) */
-  {"qxn",	HB_TAG('Q','U','Z',' ')},	/* Northern Conchucos Ancash Quechua -> Quechua */
-  {"qxo",	HB_TAG('Q','W','H',' ')},	/* Southern Conchucos Ancash Quechua -> Quechua (Peru) */
-  {"qxo",	HB_TAG('Q','U','Z',' ')},	/* Southern Conchucos Ancash Quechua -> Quechua */
-  {"qxp",	HB_TAG('Q','U','Z',' ')},	/* Puno Quechua -> Quechua */
-  {"qxr",	HB_TAG('Q','V','I',' ')},	/* Cañar Highland Quichua -> Quechua (Ecuador) */
-  {"qxr",	HB_TAG('Q','U','Z',' ')},	/* Cañar Highland Quichua -> Quechua */
-  {"qxt",	HB_TAG('Q','W','H',' ')},	/* Santa Ana de Tusi Pasco Quechua -> Quechua (Peru) */
-  {"qxt",	HB_TAG('Q','U','Z',' ')},	/* Santa Ana de Tusi Pasco Quechua -> Quechua */
-  {"qxu",	HB_TAG('Q','U','Z',' ')},	/* Arequipa-La Unión Quechua -> Quechua */
-  {"qxw",	HB_TAG('Q','W','H',' ')},	/* Jauja Wanca Quechua -> Quechua (Peru) */
-  {"qxw",	HB_TAG('Q','U','Z',' ')},	/* Jauja Wanca Quechua -> Quechua */
-  {"rag",	HB_TAG('L','U','H',' ')},	/* Logooli -> Luyia */
-/*{"raj",	HB_TAG('R','A','J',' ')},*/	/* Rajasthani [macrolanguage] */
-  {"ral",	HB_TAG('Q','I','N',' ')},	/* Ralte -> Chin */
-/*{"rar",	HB_TAG('R','A','R',' ')},*/	/* Rarotongan */
-  {"rbb",	HB_TAG('P','L','G',' ')},	/* Rumai Palaung -> Palaung */
-  {"rbl",	HB_TAG('B','I','K',' ')},	/* Miraya Bikol -> Bikol */
-  {"rcf",	HB_TAG('C','P','P',' ')},	/* Réunion Creole French -> Creoles */
-/*{"rej",	HB_TAG('R','E','J',' ')},*/	/* Rejang */
-/*{"rhg",	HB_TAG('R','H','G',' ')},*/	/* Rohingya */
-/*{"ria",	HB_TAG('R','I','A',' ')},*/	/* Riang (India) */
-  {"rif",	HB_TAG('R','I','F',' ')},	/* Tarifit */
-  {"rif",	HB_TAG('B','B','R',' ')},	/* Tarifit -> Berber */
-/*{"rit",	HB_TAG('R','I','T',' ')},*/	/* Ritharrngu -> Ritarungo */
-  {"rki",	HB_TAG('A','R','K',' ')},	/* Rakhine */
-/*{"rkw",	HB_TAG('R','K','W',' ')},*/	/* Arakwal */
-  {"rm",	HB_TAG('R','M','S',' ')},	/* Romansh */
-  {"rmc",	HB_TAG('R','O','Y',' ')},	/* Carpathian Romani -> Romany */
-  {"rmf",	HB_TAG('R','O','Y',' ')},	/* Kalo Finnish Romani -> Romany */
-  {"rml",	HB_TAG('R','O','Y',' ')},	/* Baltic Romani -> Romany */
-  {"rmn",	HB_TAG('R','O','Y',' ')},	/* Balkan Romani -> Romany */
-  {"rmo",	HB_TAG('R','O','Y',' ')},	/* Sinte Romani -> Romany */
-  {"rms",	HB_TAG_NONE	       },	/* Romanian Sign Language != Romansh */
-  {"rmw",	HB_TAG('R','O','Y',' ')},	/* Welsh Romani -> Romany */
-  {"rmy",	HB_TAG('R','M','Y',' ')},	/* Vlax Romani */
-  {"rmy",	HB_TAG('R','O','Y',' ')},	/* Vlax Romani -> Romany */
-  {"rmz",	HB_TAG('A','R','K',' ')},	/* Marma -> Rakhine */
-  {"rn",	HB_TAG('R','U','N',' ')},	/* Rundi */
-  {"ro",	HB_TAG('R','O','M',' ')},	/* Romanian */
-  {"rom",	HB_TAG('R','O','Y',' ')},	/* Romany [macrolanguage] */
-  {"rop",	HB_TAG('C','P','P',' ')},	/* Kriol -> Creoles */
-  {"rtc",	HB_TAG('Q','I','N',' ')},	/* Rungtu Chin -> Chin */
-/*{"rtm",	HB_TAG('R','T','M',' ')},*/	/* Rotuman */
-  {"ru",	HB_TAG('R','U','S',' ')},	/* Russian */
-  {"rue",	HB_TAG('R','S','Y',' ')},	/* Rusyn */
-/*{"rup",	HB_TAG('R','U','P',' ')},*/	/* Aromanian */
-  {"rw",	HB_TAG('R','U','A',' ')},	/* Kinyarwanda */
-  {"rwr",	HB_TAG('M','A','W',' ')},	/* Marwari (India) */
-  {"sa",	HB_TAG('S','A','N',' ')},	/* Sanskrit */
-  {"sad",	HB_TAG_NONE	       },	/* Sandawe != Sadri */
-  {"sah",	HB_TAG('Y','A','K',' ')},	/* Yakut -> Sakha */
-  {"sam",	HB_TAG('P','A','A',' ')},	/* Samaritan Aramaic -> Palestinian Aramaic */
-/*{"sas",	HB_TAG('S','A','S',' ')},*/	/* Sasak */
-/*{"sat",	HB_TAG('S','A','T',' ')},*/	/* Santali */
-  {"say",	HB_TAG_NONE	       },	/* Saya != Sayisi */
-  {"sc",	HB_TAG('S','R','D',' ')},	/* Sardinian [macrolanguage] */
-  {"scf",	HB_TAG('C','P','P',' ')},	/* San Miguel Creole French -> Creoles */
-  {"sch",	HB_TAG('Q','I','N',' ')},	/* Sakachep -> Chin */
-  {"sci",	HB_TAG('C','P','P',' ')},	/* Sri Lankan Creole Malay -> Creoles */
-  {"sck",	HB_TAG('S','A','D',' ')},	/* Sadri */
-/*{"scn",	HB_TAG('S','C','N',' ')},*/	/* Sicilian */
-/*{"sco",	HB_TAG('S','C','O',' ')},*/	/* Scots */
-  {"scs",	HB_TAG('S','C','S',' ')},	/* North Slavey */
-  {"scs",	HB_TAG('S','L','A',' ')},	/* North Slavey -> Slavey */
-  {"scs",	HB_TAG('A','T','H',' ')},	/* North Slavey -> Athapaskan */
-  {"sd",	HB_TAG('S','N','D',' ')},	/* Sindhi */
-  {"sdc",	HB_TAG('S','R','D',' ')},	/* Sassarese Sardinian -> Sardinian */
-  {"sdh",	HB_TAG('K','U','R',' ')},	/* Southern Kurdish -> Kurdish */
-  {"sdn",	HB_TAG('S','R','D',' ')},	/* Gallurese Sardinian -> Sardinian */
-  {"sds",	HB_TAG('B','B','R',' ')},	/* Sened -> Berber */
-  {"se",	HB_TAG('N','S','M',' ')},	/* Northern Sami */
-  {"seh",	HB_TAG('S','N','A',' ')},	/* Sena */
-  {"sek",	HB_TAG('A','T','H',' ')},	/* Sekani -> Athapaskan */
-/*{"sel",	HB_TAG('S','E','L',' ')},*/	/* Selkup */
-  {"sez",	HB_TAG('Q','I','N',' ')},	/* Senthang Chin -> Chin */
-  {"sfm",	HB_TAG('S','F','M',' ')},	/* Small Flowery Miao */
-  {"sfm",	HB_TAG('H','M','N',' ')},	/* Small Flowery Miao -> Hmong */
-  {"sg",	HB_TAG('S','G','O',' ')},	/* Sango */
-/*{"sga",	HB_TAG('S','G','A',' ')},*/	/* Old Irish (to 900) */
-  {"sgc",	HB_TAG('K','A','L',' ')},	/* Kipsigis -> Kalenjin */
-  {"sgo",	HB_TAG_NONE	       },	/* Songa (retired code) != Sango */
-/*{"sgs",	HB_TAG('S','G','S',' ')},*/	/* Samogitian */
-  {"sgw",	HB_TAG('C','H','G',' ')},	/* Sebat Bet Gurage -> Chaha Gurage */
-  {"sh",	HB_TAG('B','O','S',' ')},	/* Serbo-Croatian [macrolanguage] -> Bosnian */
-  {"sh",	HB_TAG('H','R','V',' ')},	/* Serbo-Croatian [macrolanguage] -> Croatian */
-  {"sh",	HB_TAG('S','R','B',' ')},	/* Serbo-Croatian [macrolanguage] -> Serbian */
-  {"shi",	HB_TAG('S','H','I',' ')},	/* Tachelhit */
-  {"shi",	HB_TAG('B','B','R',' ')},	/* Tachelhit -> Berber */
-  {"shl",	HB_TAG('Q','I','N',' ')},	/* Shendu -> Chin */
-/*{"shn",	HB_TAG('S','H','N',' ')},*/	/* Shan */
-  {"shu",	HB_TAG('A','R','A',' ')},	/* Chadian Arabic -> Arabic */
-  {"shy",	HB_TAG('B','B','R',' ')},	/* Tachawit -> Berber */
-  {"si",	HB_TAG('S','N','H',' ')},	/* Sinhala (Sinhalese) */
-  {"sib",	HB_TAG_NONE	       },	/* Sebop != Sibe */
-/*{"sid",	HB_TAG('S','I','D',' ')},*/	/* Sidamo */
-  {"sig",	HB_TAG_NONE	       },	/* Paasaal != Silte Gurage */
-  {"siz",	HB_TAG('B','B','R',' ')},	/* Siwi -> Berber */
-  {"sjd",	HB_TAG('K','S','M',' ')},	/* Kildin Sami */
-  {"sjo",	HB_TAG('S','I','B',' ')},	/* Xibe -> Sibe */
-  {"sjs",	HB_TAG('B','B','R',' ')},	/* Senhaja De Srair -> Berber */
-  {"sk",	HB_TAG('S','K','Y',' ')},	/* Slovak */
-  {"skg",	HB_TAG('M','L','G',' ')},	/* Sakalava Malagasy -> Malagasy */
-  {"skr",	HB_TAG('S','R','K',' ')},	/* Saraiki */
-  {"sks",	HB_TAG_NONE	       },	/* Maia != Skolt Sami */
-  {"skw",	HB_TAG('C','P','P',' ')},	/* Skepi Creole Dutch -> Creoles */
-  {"sky",	HB_TAG_NONE	       },	/* Sikaiana != Slovak */
-  {"sl",	HB_TAG('S','L','V',' ')},	/* Slovenian */
-  {"sla",	HB_TAG_NONE	       },	/* Slavic [collection] != Slavey */
-  {"sm",	HB_TAG('S','M','O',' ')},	/* Samoan */
-  {"sma",	HB_TAG('S','S','M',' ')},	/* Southern Sami */
-  {"smd",	HB_TAG('M','B','N',' ')},	/* Sama (retired code) -> Mbundu */
-  {"smj",	HB_TAG('L','S','M',' ')},	/* Lule Sami */
-  {"sml",	HB_TAG_NONE	       },	/* Central Sama != Somali */
-  {"smn",	HB_TAG('I','S','M',' ')},	/* Inari Sami */
-  {"sms",	HB_TAG('S','K','S',' ')},	/* Skolt Sami */
-  {"smt",	HB_TAG('Q','I','N',' ')},	/* Simte -> Chin */
-  {"sn",	HB_TAG('S','N','A','0')},	/* Shona */
-  {"snb",	HB_TAG('I','B','A',' ')},	/* Sebuyau (retired code) -> Iban */
-  {"snh",	HB_TAG_NONE	       },	/* Shinabo (retired code) != Sinhala (Sinhalese) */
-/*{"snk",	HB_TAG('S','N','K',' ')},*/	/* Soninke */
-  {"so",	HB_TAG('S','M','L',' ')},	/* Somali */
-  {"sog",	HB_TAG_NONE	       },	/* Sogdian != Sodo Gurage */
-/*{"sop",	HB_TAG('S','O','P',' ')},*/	/* Songe */
-  {"spv",	HB_TAG('O','R','I',' ')},	/* Sambalpuri -> Odia (formerly Oriya) */
-  {"spy",	HB_TAG('K','A','L',' ')},	/* Sabaot -> Kalenjin */
-  {"sq",	HB_TAG('S','Q','I',' ')},	/* Albanian [macrolanguage] */
-  {"sr",	HB_TAG('S','R','B',' ')},	/* Serbian */
-  {"srb",	HB_TAG_NONE	       },	/* Sora != Serbian */
-  {"src",	HB_TAG('S','R','D',' ')},	/* Logudorese Sardinian -> Sardinian */
-  {"srk",	HB_TAG_NONE	       },	/* Serudung Murut != Saraiki */
-  {"srm",	HB_TAG('C','P','P',' ')},	/* Saramaccan -> Creoles */
-  {"srn",	HB_TAG('C','P','P',' ')},	/* Sranan Tongo -> Creoles */
-  {"sro",	HB_TAG('S','R','D',' ')},	/* Campidanese Sardinian -> Sardinian */
-/*{"srr",	HB_TAG('S','R','R',' ')},*/	/* Serer */
-  {"srs",	HB_TAG('A','T','H',' ')},	/* Sarsi -> Athapaskan */
-  {"ss",	HB_TAG('S','W','Z',' ')},	/* Swati */
-  {"ssh",	HB_TAG('A','R','A',' ')},	/* Shihhi Arabic -> Arabic */
-  {"ssl",	HB_TAG_NONE	       },	/* Western Sisaala != South Slavey */
-  {"ssm",	HB_TAG_NONE	       },	/* Semnam != Southern Sami */
-  {"st",	HB_TAG('S','O','T',' ')},	/* Southern Sotho */
-  {"sta",	HB_TAG('C','P','P',' ')},	/* Settla -> Creoles */
-/*{"stq",	HB_TAG('S','T','Q',' ')},*/	/* Saterfriesisch -> Saterland Frisian */
-  {"stv",	HB_TAG('S','I','G',' ')},	/* Silt'e -> Silte Gurage */
-  {"su",	HB_TAG('S','U','N',' ')},	/* Sundanese */
-/*{"suk",	HB_TAG('S','U','K',' ')},*/	/* Sukuma */
-  {"suq",	HB_TAG('S','U','R',' ')},	/* Suri */
-  {"sur",	HB_TAG_NONE	       },	/* Mwaghavul != Suri */
-  {"sv",	HB_TAG('S','V','E',' ')},	/* Swedish */
-/*{"sva",	HB_TAG('S','V','A',' ')},*/	/* Svan */
-  {"svc",	HB_TAG('C','P','P',' ')},	/* Vincentian Creole English -> Creoles */
-  {"sve",	HB_TAG_NONE	       },	/* Serili != Swedish */
-  {"sw",	HB_TAG('S','W','K',' ')},	/* Swahili [macrolanguage] */
-  {"swb",	HB_TAG('C','M','R',' ')},	/* Maore Comorian -> Comorian */
-  {"swc",	HB_TAG('S','W','K',' ')},	/* Congo Swahili -> Swahili */
-  {"swh",	HB_TAG('S','W','K',' ')},	/* Swahili */
-  {"swk",	HB_TAG_NONE	       },	/* Malawi Sena != Swahili */
-  {"swn",	HB_TAG('B','B','R',' ')},	/* Sawknah -> Berber */
-  {"swv",	HB_TAG('M','A','W',' ')},	/* Shekhawati -> Marwari */
-/*{"sxu",	HB_TAG('S','X','U',' ')},*/	/* Upper Saxon */
-  {"syc",	HB_TAG('S','Y','R',' ')},	/* Classical Syriac -> Syriac */
-/*{"syl",	HB_TAG('S','Y','L',' ')},*/	/* Sylheti */
-/*{"syr",	HB_TAG('S','Y','R',' ')},*/	/* Syriac [macrolanguage] */
-/*{"szl",	HB_TAG('S','Z','L',' ')},*/	/* Silesian */
-  {"ta",	HB_TAG('T','A','M',' ')},	/* Tamil */
-  {"taa",	HB_TAG('A','T','H',' ')},	/* Lower Tanana -> Athapaskan */
-/*{"tab",	HB_TAG('T','A','B',' ')},*/	/* Tabassaran -> Tabasaran */
-  {"taj",	HB_TAG_NONE	       },	/* Eastern Tamang != Tajiki */
-  {"taq",	HB_TAG('T','M','H',' ')},	/* Tamasheq -> Tamashek */
-  {"taq",	HB_TAG('B','B','R',' ')},	/* Tamasheq -> Berber */
-  {"tas",	HB_TAG('C','P','P',' ')},	/* Tay Boi -> Creoles */
-  {"tau",	HB_TAG('A','T','H',' ')},	/* Upper Tanana -> Athapaskan */
-  {"tcb",	HB_TAG('A','T','H',' ')},	/* Tanacross -> Athapaskan */
-  {"tce",	HB_TAG('A','T','H',' ')},	/* Southern Tutchone -> Athapaskan */
-  {"tch",	HB_TAG('C','P','P',' ')},	/* Turks And Caicos Creole English -> Creoles */
-  {"tcp",	HB_TAG('Q','I','N',' ')},	/* Tawr Chin -> Chin */
-  {"tcs",	HB_TAG('C','P','P',' ')},	/* Torres Strait Creole -> Creoles */
-  {"tcy",	HB_TAG('T','U','L',' ')},	/* Tulu -> Tumbuka */
-  {"tcz",	HB_TAG('Q','I','N',' ')},	/* Thado Chin -> Chin */
-/*{"tdd",	HB_TAG('T','D','D',' ')},*/	/* Tai Nüa -> Dehong Dai */
-  {"tdx",	HB_TAG('M','L','G',' ')},	/* Tandroy-Mahafaly Malagasy -> Malagasy */
-  {"te",	HB_TAG('T','E','L',' ')},	/* Telugu */
-  {"tec",	HB_TAG('K','A','L',' ')},	/* Terik -> Kalenjin */
-  {"tem",	HB_TAG('T','M','N',' ')},	/* Timne -> Temne */
-/*{"tet",	HB_TAG('T','E','T',' ')},*/	/* Tetum */
-  {"tez",	HB_TAG('B','B','R',' ')},	/* Tetserret -> Berber */
-  {"tfn",	HB_TAG('A','T','H',' ')},	/* Tanaina -> Athapaskan */
-  {"tg",	HB_TAG('T','A','J',' ')},	/* Tajik -> Tajiki */
-  {"tgh",	HB_TAG('C','P','P',' ')},	/* Tobagonian Creole English -> Creoles */
-  {"tgj",	HB_TAG('N','I','S',' ')},	/* Tagin -> Nisi */
-  {"tgn",	HB_TAG_NONE	       },	/* Tandaganon != Tongan */
-  {"tgr",	HB_TAG_NONE	       },	/* Tareng != Tigre */
-  {"tgx",	HB_TAG('A','T','H',' ')},	/* Tagish -> Athapaskan */
-  {"tgy",	HB_TAG_NONE	       },	/* Togoyo != Tigrinya */
-  {"th",	HB_TAG('T','H','A',' ')},	/* Thai */
-  {"tht",	HB_TAG('A','T','H',' ')},	/* Tahltan -> Athapaskan */
-  {"thv",	HB_TAG('T','M','H',' ')},	/* Tahaggart Tamahaq -> Tamashek */
-  {"thv",	HB_TAG('B','B','R',' ')},	/* Tahaggart Tamahaq -> Berber */
-  {"thz",	HB_TAG('T','M','H',' ')},	/* Tayart Tamajeq -> Tamashek */
-  {"thz",	HB_TAG('B','B','R',' ')},	/* Tayart Tamajeq -> Berber */
-  {"ti",	HB_TAG('T','G','Y',' ')},	/* Tigrinya */
-  {"tia",	HB_TAG('B','B','R',' ')},	/* Tidikelt Tamazight -> Berber */
-  {"tig",	HB_TAG('T','G','R',' ')},	/* Tigre */
-/*{"tiv",	HB_TAG('T','I','V',' ')},*/	/* Tiv */
-/*{"tjl",	HB_TAG('T','J','L',' ')},*/	/* Tai Laing */
-  {"tjo",	HB_TAG('B','B','R',' ')},	/* Temacine Tamazight -> Berber */
-  {"tk",	HB_TAG('T','K','M',' ')},	/* Turkmen */
-  {"tkg",	HB_TAG('M','L','G',' ')},	/* Tesaka Malagasy -> Malagasy */
-  {"tkm",	HB_TAG_NONE	       },	/* Takelma != Turkmen */
-  {"tl",	HB_TAG('T','G','L',' ')},	/* Tagalog */
-/*{"tli",	HB_TAG('T','L','I',' ')},*/	/* Tlingit */
-  {"tmg",	HB_TAG('C','P','P',' ')},	/* Ternateño -> Creoles */
-  {"tmh",	HB_TAG('T','M','H',' ')},	/* Tamashek [macrolanguage] */
-  {"tmh",	HB_TAG('B','B','R',' ')},	/* Tamashek [macrolanguage] -> Berber */
-  {"tmn",	HB_TAG_NONE	       },	/* Taman (Indonesia) != Temne */
-  {"tmw",	HB_TAG('M','L','Y',' ')},	/* Temuan -> Malay */
-  {"tn",	HB_TAG('T','N','A',' ')},	/* Tswana */
-  {"tna",	HB_TAG_NONE	       },	/* Tacana != Tswana */
-  {"tne",	HB_TAG_NONE	       },	/* Tinoc Kallahan (retired code) != Tundra Enets */
-  {"tnf",	HB_TAG('D','R','I',' ')},	/* Tangshewi (retired code) -> Dari */
-  {"tnf",	HB_TAG('F','A','R',' ')},	/* Tangshewi (retired code) -> Persian */
-  {"tng",	HB_TAG_NONE	       },	/* Tobanga != Tonga */
-  {"to",	HB_TAG('T','G','N',' ')},	/* Tonga (Tonga Islands) -> Tongan */
-  {"tod",	HB_TAG('T','O','D','0')},	/* Toma */
-  {"toi",	HB_TAG('T','N','G',' ')},	/* Tonga (Zambia) */
-  {"toj",	HB_TAG('M','Y','N',' ')},	/* Tojolabal -> Mayan */
-  {"tol",	HB_TAG('A','T','H',' ')},	/* Tolowa -> Athapaskan */
-  {"tor",	HB_TAG('B','A','D','0')},	/* Togbo-Vara Banda -> Banda */
-  {"tpi",	HB_TAG('T','P','I',' ')},	/* Tok Pisin */
-  {"tpi",	HB_TAG('C','P','P',' ')},	/* Tok Pisin -> Creoles */
-  {"tr",	HB_TAG('T','R','K',' ')},	/* Turkish */
-  {"trf",	HB_TAG('C','P','P',' ')},	/* Trinidadian Creole English -> Creoles */
-  {"trk",	HB_TAG_NONE	       },	/* Turkic [collection] != Turkish */
-  {"tru",	HB_TAG('T','U','A',' ')},	/* Turoyo -> Turoyo Aramaic */
-  {"tru",	HB_TAG('S','Y','R',' ')},	/* Turoyo -> Syriac */
-  {"ts",	HB_TAG('T','S','G',' ')},	/* Tsonga */
-  {"tsg",	HB_TAG_NONE	       },	/* Tausug != Tsonga */
-/*{"tsj",	HB_TAG('T','S','J',' ')},*/	/* Tshangla */
-  {"tt",	HB_TAG('T','A','T',' ')},	/* Tatar */
-  {"ttc",	HB_TAG('M','Y','N',' ')},	/* Tektiteko -> Mayan */
-  {"ttm",	HB_TAG('A','T','H',' ')},	/* Northern Tutchone -> Athapaskan */
-  {"ttq",	HB_TAG('T','M','H',' ')},	/* Tawallammat Tamajaq -> Tamashek */
-  {"ttq",	HB_TAG('B','B','R',' ')},	/* Tawallammat Tamajaq -> Berber */
-  {"tua",	HB_TAG_NONE	       },	/* Wiarumus != Turoyo Aramaic */
-  {"tul",	HB_TAG_NONE	       },	/* Tula != Tumbuka */
-/*{"tum",	HB_TAG('T','U','M',' ')},*/	/* Tumbuka -> Tulu */
-  {"tuu",	HB_TAG('A','T','H',' ')},	/* Tututni -> Athapaskan */
-  {"tuv",	HB_TAG_NONE	       },	/* Turkana != Tuvin */
-  {"tuy",	HB_TAG('K','A','L',' ')},	/* Tugen -> Kalenjin */
-/*{"tvl",	HB_TAG('T','V','L',' ')},*/	/* Tuvalu */
-  {"tvy",	HB_TAG('C','P','P',' ')},	/* Timor Pidgin -> Creoles */
-  {"tw",	HB_TAG('T','W','I',' ')},	/* Twi */
-  {"tw",	HB_TAG('A','K','A',' ')},	/* Twi -> Akan */
-  {"txc",	HB_TAG('A','T','H',' ')},	/* Tsetsaut -> Athapaskan */
-  {"txy",	HB_TAG('M','L','G',' ')},	/* Tanosy Malagasy -> Malagasy */
-  {"ty",	HB_TAG('T','H','T',' ')},	/* Tahitian */
-  {"tyv",	HB_TAG('T','U','V',' ')},	/* Tuvinian -> Tuvin */
-/*{"tyz",	HB_TAG('T','Y','Z',' ')},*/	/* Tày */
-  {"tzh",	HB_TAG('M','Y','N',' ')},	/* Tzeltal -> Mayan */
-  {"tzj",	HB_TAG('M','Y','N',' ')},	/* Tz'utujil -> Mayan */
-  {"tzm",	HB_TAG('T','Z','M',' ')},	/* Central Atlas Tamazight -> Tamazight */
-  {"tzm",	HB_TAG('B','B','R',' ')},	/* Central Atlas Tamazight -> Berber */
-  {"tzo",	HB_TAG('T','Z','O',' ')},	/* Tzotzil */
-  {"tzo",	HB_TAG('M','Y','N',' ')},	/* Tzotzil -> Mayan */
-  {"ubl",	HB_TAG('B','I','K',' ')},	/* Buhi'non Bikol -> Bikol */
-/*{"udm",	HB_TAG('U','D','M',' ')},*/	/* Udmurt */
-  {"ug",	HB_TAG('U','Y','G',' ')},	/* Uyghur */
-  {"uk",	HB_TAG('U','K','R',' ')},	/* Ukrainian */
-  {"uki",	HB_TAG('K','U','I',' ')},	/* Kui (India) */
-  {"uln",	HB_TAG('C','P','P',' ')},	/* Unserdeutsch -> Creoles */
-/*{"umb",	HB_TAG('U','M','B',' ')},*/	/* Umbundu */
-  {"unr",	HB_TAG('M','U','N',' ')},	/* Mundari */
-  {"ur",	HB_TAG('U','R','D',' ')},	/* Urdu */
-  {"urk",	HB_TAG('M','L','Y',' ')},	/* Urak Lawoi' -> Malay */
-  {"usp",	HB_TAG('M','Y','N',' ')},	/* Uspanteco -> Mayan */
-  {"uz",	HB_TAG('U','Z','B',' ')},	/* Uzbek [macrolanguage] */
-  {"uzn",	HB_TAG('U','Z','B',' ')},	/* Northern Uzbek -> Uzbek */
-  {"uzs",	HB_TAG('U','Z','B',' ')},	/* Southern Uzbek -> Uzbek */
-  {"vap",	HB_TAG('Q','I','N',' ')},	/* Vaiphei -> Chin */
-  {"ve",	HB_TAG('V','E','N',' ')},	/* Venda */
-/*{"vec",	HB_TAG('V','E','C',' ')},*/	/* Venetian */
-  {"vi",	HB_TAG('V','I','T',' ')},	/* Vietnamese */
-  {"vic",	HB_TAG('C','P','P',' ')},	/* Virgin Islands Creole English -> Creoles */
-  {"vit",	HB_TAG_NONE	       },	/* Viti != Vietnamese */
-  {"vkk",	HB_TAG('M','L','Y',' ')},	/* Kaur -> Malay */
-  {"vkp",	HB_TAG('C','P','P',' ')},	/* Korlai Creole Portuguese -> Creoles */
-  {"vkt",	HB_TAG('M','L','Y',' ')},	/* Tenggarong Kutai Malay -> Malay */
-  {"vls",	HB_TAG('F','L','E',' ')},	/* Vlaams -> Dutch (Flemish) */
-  {"vmw",	HB_TAG('M','A','K',' ')},	/* Makhuwa */
-  {"vo",	HB_TAG('V','O','L',' ')},	/* Volapük */
-/*{"vro",	HB_TAG('V','R','O',' ')},*/	/* Võro */
-  {"wa",	HB_TAG('W','L','N',' ')},	/* Walloon */
-  {"wag",	HB_TAG_NONE	       },	/* Wa'ema != Wagdi */
-/*{"war",	HB_TAG('W','A','R',' ')},*/	/* Waray (Philippines) -> Waray-Waray */
-  {"wbm",	HB_TAG('W','A',' ',' ')},	/* Wa */
-  {"wbr",	HB_TAG('W','A','G',' ')},	/* Wagdi */
-  {"wbr",	HB_TAG('R','A','J',' ')},	/* Wagdi -> Rajasthani */
-/*{"wci",	HB_TAG('W','C','I',' ')},*/	/* Waci Gbe */
-  {"wea",	HB_TAG('K','R','N',' ')},	/* Wewaw -> Karen */
-  {"wes",	HB_TAG('C','P','P',' ')},	/* Cameroon Pidgin -> Creoles */
-  {"weu",	HB_TAG('Q','I','N',' ')},	/* Rawngtu Chin -> Chin */
-  {"wlc",	HB_TAG('C','M','R',' ')},	/* Mwali Comorian -> Comorian */
-  {"wle",	HB_TAG('S','I','G',' ')},	/* Wolane -> Silte Gurage */
-  {"wlk",	HB_TAG('A','T','H',' ')},	/* Wailaki -> Athapaskan */
-  {"wni",	HB_TAG('C','M','R',' ')},	/* Ndzwani Comorian -> Comorian */
-  {"wo",	HB_TAG('W','L','F',' ')},	/* Wolof */
-  {"wry",	HB_TAG('M','A','W',' ')},	/* Merwari -> Marwari */
-  {"wsg",	HB_TAG('G','O','N',' ')},	/* Adilabad Gondi -> Gondi */
-/*{"wtm",	HB_TAG('W','T','M',' ')},*/	/* Mewati */
-  {"wuu",	HB_TAG('Z','H','S',' ')},	/* Wu Chinese -> Chinese, Simplified */
-  {"xal",	HB_TAG('K','L','M',' ')},	/* Kalmyk */
-  {"xal",	HB_TAG('T','O','D',' ')},	/* Kalmyk -> Todo */
-  {"xan",	HB_TAG('S','E','K',' ')},	/* Xamtanga -> Sekota */
-  {"xbd",	HB_TAG_NONE	       },	/* Bindal != Lü */
-  {"xh",	HB_TAG('X','H','S',' ')},	/* Xhosa */
-/*{"xjb",	HB_TAG('X','J','B',' ')},*/	/* Minjungbal -> Minjangbal */
-/*{"xkf",	HB_TAG('X','K','F',' ')},*/	/* Khengkha */
-  {"xmg",	HB_TAG('B','M','L',' ')},	/* Mengaka -> Bamileke */
-  {"xmm",	HB_TAG('M','L','Y',' ')},	/* Manado Malay -> Malay */
-  {"xmm",	HB_TAG('C','P','P',' ')},	/* Manado Malay -> Creoles */
-  {"xmv",	HB_TAG('M','L','G',' ')},	/* Antankarana Malagasy -> Malagasy */
-  {"xmw",	HB_TAG('M','L','G',' ')},	/* Tsimihety Malagasy -> Malagasy */
-  {"xnj",	HB_TAG('S','X','T',' ')},	/* Ngoni (Tanzania) -> Sutu */
-  {"xnq",	HB_TAG('S','X','T',' ')},	/* Ngoni (Mozambique) -> Sutu */
-  {"xnr",	HB_TAG('D','G','R',' ')},	/* Kangri -> Dogri (macrolanguage) */
-/*{"xog",	HB_TAG('X','O','G',' ')},*/	/* Soga */
-  {"xpe",	HB_TAG('X','P','E',' ')},	/* Liberia Kpelle -> Kpelle (Liberia) */
-  {"xpe",	HB_TAG('K','P','L',' ')},	/* Liberia Kpelle -> Kpelle */
-  {"xsl",	HB_TAG('S','S','L',' ')},	/* South Slavey */
-  {"xsl",	HB_TAG('S','L','A',' ')},	/* South Slavey -> Slavey */
-  {"xsl",	HB_TAG('A','T','H',' ')},	/* South Slavey -> Athapaskan */
-  {"xst",	HB_TAG('S','I','G',' ')},	/* Silt'e (retired code) -> Silte Gurage */
-/*{"xub",	HB_TAG('X','U','B',' ')},*/	/* Betta Kurumba -> Bette Kuruma */
-/*{"xuj",	HB_TAG('X','U','J',' ')},*/	/* Jennu Kurumba -> Jennu Kuruma */
-  {"xup",	HB_TAG('A','T','H',' ')},	/* Upper Umpqua -> Athapaskan */
-  {"xwo",	HB_TAG('T','O','D',' ')},	/* Written Oirat -> Todo */
-  {"yaj",	HB_TAG('B','A','D','0')},	/* Banda-Yangere -> Banda */
-  {"yak",	HB_TAG_NONE	       },	/* Yakama != Sakha */
-/*{"yao",	HB_TAG('Y','A','O',' ')},*/	/* Yao */
-/*{"yap",	HB_TAG('Y','A','P',' ')},*/	/* Yapese */
-  {"yba",	HB_TAG_NONE	       },	/* Yala != Yoruba */
-  {"ybb",	HB_TAG('B','M','L',' ')},	/* Yemba -> Bamileke */
-  {"ybd",	HB_TAG('A','R','K',' ')},	/* Yangbye (retired code) -> Rakhine */
-  {"ydd",	HB_TAG('J','I','I',' ')},	/* Eastern Yiddish -> Yiddish */
-/*{"ygp",	HB_TAG('Y','G','P',' ')},*/	/* Gepo */
-  {"yi",	HB_TAG('J','I','I',' ')},	/* Yiddish [macrolanguage] */
-  {"yih",	HB_TAG('J','I','I',' ')},	/* Western Yiddish -> Yiddish */
-  {"yim",	HB_TAG_NONE	       },	/* Yimchungru Naga != Yi Modern */
-/*{"yna",	HB_TAG('Y','N','A',' ')},*/	/* Aluo */
-  {"yo",	HB_TAG('Y','B','A',' ')},	/* Yoruba */
-  {"yos",	HB_TAG('Q','I','N',' ')},	/* Yos (retired code) -> Chin */
-  {"yua",	HB_TAG('M','Y','N',' ')},	/* Yucateco -> Mayan */
-  {"yue",	HB_TAG('Z','H','H',' ')},	/* Yue Chinese -> Chinese, Traditional, Hong Kong SAR */
-/*{"ywq",	HB_TAG('Y','W','Q',' ')},*/	/* Wuding-Luquan Yi */
-  {"za",	HB_TAG('Z','H','A',' ')},	/* Zhuang [macrolanguage] */
-  {"zch",	HB_TAG('Z','H','A',' ')},	/* Central Hongshuihe Zhuang -> Zhuang */
-  {"zdj",	HB_TAG('C','M','R',' ')},	/* Ngazidja Comorian -> Comorian */
-/*{"zea",	HB_TAG('Z','E','A',' ')},*/	/* Zeeuws -> Zealandic */
-  {"zeh",	HB_TAG('Z','H','A',' ')},	/* Eastern Hongshuihe Zhuang -> Zhuang */
-  {"zen",	HB_TAG('B','B','R',' ')},	/* Zenaga -> Berber */
-  {"zgb",	HB_TAG('Z','H','A',' ')},	/* Guibei Zhuang -> Zhuang */
-  {"zgh",	HB_TAG('Z','G','H',' ')},	/* Standard Moroccan Tamazight */
-  {"zgh",	HB_TAG('B','B','R',' ')},	/* Standard Moroccan Tamazight -> Berber */
-  {"zgm",	HB_TAG('Z','H','A',' ')},	/* Minz Zhuang -> Zhuang */
-  {"zgn",	HB_TAG('Z','H','A',' ')},	/* Guibian Zhuang -> Zhuang */
-  {"zh",	HB_TAG('Z','H','S',' ')},	/* Chinese, Simplified [macrolanguage] */
-  {"zhd",	HB_TAG('Z','H','A',' ')},	/* Dai Zhuang -> Zhuang */
-  {"zhn",	HB_TAG('Z','H','A',' ')},	/* Nong Zhuang -> Zhuang */
-  {"zlj",	HB_TAG('Z','H','A',' ')},	/* Liujiang Zhuang -> Zhuang */
-  {"zlm",	HB_TAG('M','L','Y',' ')},	/* Malay */
-  {"zln",	HB_TAG('Z','H','A',' ')},	/* Lianshan Zhuang -> Zhuang */
-  {"zlq",	HB_TAG('Z','H','A',' ')},	/* Liuqian Zhuang -> Zhuang */
-  {"zmi",	HB_TAG('M','L','Y',' ')},	/* Negeri Sembilan Malay -> Malay */
-  {"zmz",	HB_TAG('B','A','D','0')},	/* Mbandja -> Banda */
-  {"znd",	HB_TAG_NONE	       },	/* Zande [collection] != Zande */
-  {"zne",	HB_TAG('Z','N','D',' ')},	/* Zande */
-  {"zom",	HB_TAG('Q','I','N',' ')},	/* Zou -> Chin */
-  {"zqe",	HB_TAG('Z','H','A',' ')},	/* Qiubei Zhuang -> Zhuang */
-  {"zsm",	HB_TAG('M','L','Y',' ')},	/* Standard Malay -> Malay */
-  {"zu",	HB_TAG('Z','U','L',' ')},	/* Zulu */
-  {"zum",	HB_TAG('L','R','C',' ')},	/* Kumzari -> Luri */
-  {"zyb",	HB_TAG('Z','H','A',' ')},	/* Yongbei Zhuang -> Zhuang */
-  {"zyg",	HB_TAG('Z','H','A',' ')},	/* Yang Zhuang -> Zhuang */
-  {"zyj",	HB_TAG('Z','H','A',' ')},	/* Youjiang Zhuang -> Zhuang */
-  {"zyn",	HB_TAG('Z','H','A',' ')},	/* Yongnan Zhuang -> Zhuang */
-  {"zyp",	HB_TAG('Q','I','N',' ')},	/* Zyphe Chin -> Chin */
-/*{"zza",	HB_TAG('Z','Z','A',' ')},*/	/* Zazaki [macrolanguage] */
-  {"zzj",	HB_TAG('Z','H','A',' ')},	/* Zuojiang Zhuang -> Zhuang */
+static const LangTag ot_languages2[] = {
+  {HB_TAG('a','a',' ',' '),	HB_TAG('A','F','R',' ')},	/* Afar */
+  {HB_TAG('a','b',' ',' '),	HB_TAG('A','B','K',' ')},	/* Abkhazian */
+  {HB_TAG('a','f',' ',' '),	HB_TAG('A','F','K',' ')},	/* Afrikaans */
+  {HB_TAG('a','k',' ',' '),	HB_TAG('A','K','A',' ')},	/* Akan [macrolanguage] */
+  {HB_TAG('a','m',' ',' '),	HB_TAG('A','M','H',' ')},	/* Amharic */
+  {HB_TAG('a','n',' ',' '),	HB_TAG('A','R','G',' ')},	/* Aragonese */
+  {HB_TAG('a','r',' ',' '),	HB_TAG('A','R','A',' ')},	/* Arabic [macrolanguage] */
+  {HB_TAG('a','s',' ',' '),	HB_TAG('A','S','M',' ')},	/* Assamese */
+  {HB_TAG('a','v',' ',' '),	HB_TAG('A','V','R',' ')},	/* Avaric -> Avar */
+  {HB_TAG('a','y',' ',' '),	HB_TAG('A','Y','M',' ')},	/* Aymara [macrolanguage] */
+  {HB_TAG('a','z',' ',' '),	HB_TAG('A','Z','E',' ')},	/* Azerbaijani [macrolanguage] */
+  {HB_TAG('b','a',' ',' '),	HB_TAG('B','S','H',' ')},	/* Bashkir */
+  {HB_TAG('b','e',' ',' '),	HB_TAG('B','E','L',' ')},	/* Belarusian -> Belarussian */
+  {HB_TAG('b','g',' ',' '),	HB_TAG('B','G','R',' ')},	/* Bulgarian */
+  {HB_TAG('b','i',' ',' '),	HB_TAG('B','I','S',' ')},	/* Bislama */
+  {HB_TAG('b','i',' ',' '),	HB_TAG('C','P','P',' ')},	/* Bislama -> Creoles */
+  {HB_TAG('b','m',' ',' '),	HB_TAG('B','M','B',' ')},	/* Bambara (Bamanankan) */
+  {HB_TAG('b','n',' ',' '),	HB_TAG('B','E','N',' ')},	/* Bengali */
+  {HB_TAG('b','o',' ',' '),	HB_TAG('T','I','B',' ')},	/* Tibetan */
+  {HB_TAG('b','r',' ',' '),	HB_TAG('B','R','E',' ')},	/* Breton */
+  {HB_TAG('b','s',' ',' '),	HB_TAG('B','O','S',' ')},	/* Bosnian */
+  {HB_TAG('c','a',' ',' '),	HB_TAG('C','A','T',' ')},	/* Catalan */
+  {HB_TAG('c','e',' ',' '),	HB_TAG('C','H','E',' ')},	/* Chechen */
+  {HB_TAG('c','h',' ',' '),	HB_TAG('C','H','A',' ')},	/* Chamorro */
+  {HB_TAG('c','o',' ',' '),	HB_TAG('C','O','S',' ')},	/* Corsican */
+  {HB_TAG('c','r',' ',' '),	HB_TAG('C','R','E',' ')},	/* Cree [macrolanguage] */
+  {HB_TAG('c','s',' ',' '),	HB_TAG('C','S','Y',' ')},	/* Czech */
+  {HB_TAG('c','u',' ',' '),	HB_TAG('C','S','L',' ')},	/* Church Slavonic */
+  {HB_TAG('c','v',' ',' '),	HB_TAG('C','H','U',' ')},	/* Chuvash */
+  {HB_TAG('c','y',' ',' '),	HB_TAG('W','E','L',' ')},	/* Welsh */
+  {HB_TAG('d','a',' ',' '),	HB_TAG('D','A','N',' ')},	/* Danish */
+  {HB_TAG('d','e',' ',' '),	HB_TAG('D','E','U',' ')},	/* German */
+  {HB_TAG('d','v',' ',' '),	HB_TAG('D','I','V',' ')},	/* Divehi (Dhivehi, Maldivian) */
+  {HB_TAG('d','v',' ',' '),	HB_TAG('D','H','V',' ')},	/* Divehi (Dhivehi, Maldivian) (deprecated) */
+  {HB_TAG('d','z',' ',' '),	HB_TAG('D','Z','N',' ')},	/* Dzongkha */
+  {HB_TAG('e','e',' ',' '),	HB_TAG('E','W','E',' ')},	/* Ewe */
+  {HB_TAG('e','l',' ',' '),	HB_TAG('E','L','L',' ')},	/* Modern Greek (1453-) -> Greek */
+  {HB_TAG('e','n',' ',' '),	HB_TAG('E','N','G',' ')},	/* English */
+  {HB_TAG('e','o',' ',' '),	HB_TAG('N','T','O',' ')},	/* Esperanto */
+  {HB_TAG('e','s',' ',' '),	HB_TAG('E','S','P',' ')},	/* Spanish */
+  {HB_TAG('e','t',' ',' '),	HB_TAG('E','T','I',' ')},	/* Estonian [macrolanguage] */
+  {HB_TAG('e','u',' ',' '),	HB_TAG('E','U','Q',' ')},	/* Basque */
+  {HB_TAG('f','a',' ',' '),	HB_TAG('F','A','R',' ')},	/* Persian [macrolanguage] */
+  {HB_TAG('f','f',' ',' '),	HB_TAG('F','U','L',' ')},	/* Fulah [macrolanguage] */
+  {HB_TAG('f','i',' ',' '),	HB_TAG('F','I','N',' ')},	/* Finnish */
+  {HB_TAG('f','j',' ',' '),	HB_TAG('F','J','I',' ')},	/* Fijian */
+  {HB_TAG('f','o',' ',' '),	HB_TAG('F','O','S',' ')},	/* Faroese */
+  {HB_TAG('f','r',' ',' '),	HB_TAG('F','R','A',' ')},	/* French */
+  {HB_TAG('f','y',' ',' '),	HB_TAG('F','R','I',' ')},	/* Western Frisian -> Frisian */
+  {HB_TAG('g','a',' ',' '),	HB_TAG('I','R','I',' ')},	/* Irish */
+  {HB_TAG('g','d',' ',' '),	HB_TAG('G','A','E',' ')},	/* Scottish Gaelic (Gaelic) */
+  {HB_TAG('g','l',' ',' '),	HB_TAG('G','A','L',' ')},	/* Galician */
+  {HB_TAG('g','n',' ',' '),	HB_TAG('G','U','A',' ')},	/* Guarani [macrolanguage] */
+  {HB_TAG('g','u',' ',' '),	HB_TAG('G','U','J',' ')},	/* Gujarati */
+  {HB_TAG('g','v',' ',' '),	HB_TAG('M','N','X',' ')},	/* Manx */
+  {HB_TAG('h','a',' ',' '),	HB_TAG('H','A','U',' ')},	/* Hausa */
+  {HB_TAG('h','e',' ',' '),	HB_TAG('I','W','R',' ')},	/* Hebrew */
+  {HB_TAG('h','i',' ',' '),	HB_TAG('H','I','N',' ')},	/* Hindi */
+  {HB_TAG('h','o',' ',' '),	HB_TAG('H','M','O',' ')},	/* Hiri Motu */
+  {HB_TAG('h','o',' ',' '),	HB_TAG('C','P','P',' ')},	/* Hiri Motu -> Creoles */
+  {HB_TAG('h','r',' ',' '),	HB_TAG('H','R','V',' ')},	/* Croatian */
+  {HB_TAG('h','t',' ',' '),	HB_TAG('H','A','I',' ')},	/* Haitian (Haitian Creole) */
+  {HB_TAG('h','t',' ',' '),	HB_TAG('C','P','P',' ')},	/* Haitian -> Creoles */
+  {HB_TAG('h','u',' ',' '),	HB_TAG('H','U','N',' ')},	/* Hungarian */
+  {HB_TAG('h','y',' ',' '),	HB_TAG('H','Y','E','0')},	/* Armenian -> Armenian East */
+  {HB_TAG('h','y',' ',' '),	HB_TAG('H','Y','E',' ')},	/* Armenian */
+  {HB_TAG('h','z',' ',' '),	HB_TAG('H','E','R',' ')},	/* Herero */
+  {HB_TAG('i','a',' ',' '),	HB_TAG('I','N','A',' ')},	/* Interlingua (International Auxiliary Language Association) */
+  {HB_TAG('i','d',' ',' '),	HB_TAG('I','N','D',' ')},	/* Indonesian */
+  {HB_TAG('i','d',' ',' '),	HB_TAG('M','L','Y',' ')},	/* Indonesian -> Malay */
+  {HB_TAG('i','e',' ',' '),	HB_TAG('I','L','E',' ')},	/* Interlingue */
+  {HB_TAG('i','g',' ',' '),	HB_TAG('I','B','O',' ')},	/* Igbo */
+  {HB_TAG('i','i',' ',' '),	HB_TAG('Y','I','M',' ')},	/* Sichuan Yi -> Yi Modern */
+  {HB_TAG('i','k',' ',' '),	HB_TAG('I','P','K',' ')},	/* Inupiaq [macrolanguage] -> Inupiat */
+  {HB_TAG('i','n',' ',' '),	HB_TAG('I','N','D',' ')},	/* Indonesian (retired code) */
+  {HB_TAG('i','n',' ',' '),	HB_TAG('M','L','Y',' ')},	/* Indonesian (retired code) -> Malay */
+  {HB_TAG('i','o',' ',' '),	HB_TAG('I','D','O',' ')},	/* Ido */
+  {HB_TAG('i','s',' ',' '),	HB_TAG('I','S','L',' ')},	/* Icelandic */
+  {HB_TAG('i','t',' ',' '),	HB_TAG('I','T','A',' ')},	/* Italian */
+  {HB_TAG('i','u',' ',' '),	HB_TAG('I','N','U',' ')},	/* Inuktitut [macrolanguage] */
+  {HB_TAG('i','u',' ',' '),	HB_TAG('I','N','U','K')},	/* Inuktitut [macrolanguage] -> Nunavik Inuktitut */
+  {HB_TAG('i','w',' ',' '),	HB_TAG('I','W','R',' ')},	/* Hebrew (retired code) */
+  {HB_TAG('j','a',' ',' '),	HB_TAG('J','A','N',' ')},	/* Japanese */
+  {HB_TAG('j','i',' ',' '),	HB_TAG('J','I','I',' ')},	/* Yiddish (retired code) */
+  {HB_TAG('j','v',' ',' '),	HB_TAG('J','A','V',' ')},	/* Javanese */
+  {HB_TAG('j','w',' ',' '),	HB_TAG('J','A','V',' ')},	/* Javanese (retired code) */
+  {HB_TAG('k','a',' ',' '),	HB_TAG('K','A','T',' ')},	/* Georgian */
+  {HB_TAG('k','g',' ',' '),	HB_TAG('K','O','N','0')},	/* Kongo [macrolanguage] */
+  {HB_TAG('k','i',' ',' '),	HB_TAG('K','I','K',' ')},	/* Kikuyu (Gikuyu) */
+  {HB_TAG('k','j',' ',' '),	HB_TAG('K','U','A',' ')},	/* Kuanyama */
+  {HB_TAG('k','k',' ',' '),	HB_TAG('K','A','Z',' ')},	/* Kazakh */
+  {HB_TAG('k','l',' ',' '),	HB_TAG('G','R','N',' ')},	/* Greenlandic */
+  {HB_TAG('k','m',' ',' '),	HB_TAG('K','H','M',' ')},	/* Khmer */
+  {HB_TAG('k','n',' ',' '),	HB_TAG('K','A','N',' ')},	/* Kannada */
+  {HB_TAG('k','o',' ',' '),	HB_TAG('K','O','R',' ')},	/* Korean */
+  {HB_TAG('k','o',' ',' '),	HB_TAG('K','O','H',' ')},	/* Korean -> Korean Old Hangul */
+  {HB_TAG('k','r',' ',' '),	HB_TAG('K','N','R',' ')},	/* Kanuri [macrolanguage] */
+  {HB_TAG('k','s',' ',' '),	HB_TAG('K','S','H',' ')},	/* Kashmiri */
+  {HB_TAG('k','u',' ',' '),	HB_TAG('K','U','R',' ')},	/* Kurdish [macrolanguage] */
+  {HB_TAG('k','v',' ',' '),	HB_TAG('K','O','M',' ')},	/* Komi [macrolanguage] */
+  {HB_TAG('k','w',' ',' '),	HB_TAG('C','O','R',' ')},	/* Cornish */
+  {HB_TAG('k','y',' ',' '),	HB_TAG('K','I','R',' ')},	/* Kirghiz (Kyrgyz) */
+  {HB_TAG('l','a',' ',' '),	HB_TAG('L','A','T',' ')},	/* Latin */
+  {HB_TAG('l','b',' ',' '),	HB_TAG('L','T','Z',' ')},	/* Luxembourgish */
+  {HB_TAG('l','g',' ',' '),	HB_TAG('L','U','G',' ')},	/* Ganda */
+  {HB_TAG('l','i',' ',' '),	HB_TAG('L','I','M',' ')},	/* Limburgish */
+  {HB_TAG('l','n',' ',' '),	HB_TAG('L','I','N',' ')},	/* Lingala */
+  {HB_TAG('l','o',' ',' '),	HB_TAG('L','A','O',' ')},	/* Lao */
+  {HB_TAG('l','t',' ',' '),	HB_TAG('L','T','H',' ')},	/* Lithuanian */
+  {HB_TAG('l','u',' ',' '),	HB_TAG('L','U','B',' ')},	/* Luba-Katanga */
+  {HB_TAG('l','v',' ',' '),	HB_TAG('L','V','I',' ')},	/* Latvian [macrolanguage] */
+  {HB_TAG('m','g',' ',' '),	HB_TAG('M','L','G',' ')},	/* Malagasy [macrolanguage] */
+  {HB_TAG('m','h',' ',' '),	HB_TAG('M','A','H',' ')},	/* Marshallese */
+  {HB_TAG('m','i',' ',' '),	HB_TAG('M','R','I',' ')},	/* Maori */
+  {HB_TAG('m','k',' ',' '),	HB_TAG('M','K','D',' ')},	/* Macedonian */
+  {HB_TAG('m','l',' ',' '),	HB_TAG('M','A','L',' ')},	/* Malayalam -> Malayalam Traditional */
+  {HB_TAG('m','l',' ',' '),	HB_TAG('M','L','R',' ')},	/* Malayalam -> Malayalam Reformed */
+  {HB_TAG('m','n',' ',' '),	HB_TAG('M','N','G',' ')},	/* Mongolian [macrolanguage] */
+  {HB_TAG('m','o',' ',' '),	HB_TAG('M','O','L',' ')},	/* Moldavian (retired code) */
+  {HB_TAG('m','o',' ',' '),	HB_TAG('R','O','M',' ')},	/* Moldavian (retired code) -> Romanian */
+  {HB_TAG('m','r',' ',' '),	HB_TAG('M','A','R',' ')},	/* Marathi */
+  {HB_TAG('m','s',' ',' '),	HB_TAG('M','L','Y',' ')},	/* Malay [macrolanguage] */
+  {HB_TAG('m','t',' ',' '),	HB_TAG('M','T','S',' ')},	/* Maltese */
+  {HB_TAG('m','y',' ',' '),	HB_TAG('B','R','M',' ')},	/* Burmese */
+  {HB_TAG('n','a',' ',' '),	HB_TAG('N','A','U',' ')},	/* Nauru -> Nauruan */
+  {HB_TAG('n','b',' ',' '),	HB_TAG('N','O','R',' ')},	/* Norwegian Bokmål -> Norwegian */
+  {HB_TAG('n','d',' ',' '),	HB_TAG('N','D','B',' ')},	/* North Ndebele -> Ndebele */
+  {HB_TAG('n','e',' ',' '),	HB_TAG('N','E','P',' ')},	/* Nepali [macrolanguage] */
+  {HB_TAG('n','g',' ',' '),	HB_TAG('N','D','G',' ')},	/* Ndonga */
+  {HB_TAG('n','l',' ',' '),	HB_TAG('N','L','D',' ')},	/* Dutch */
+  {HB_TAG('n','n',' ',' '),	HB_TAG('N','Y','N',' ')},	/* Norwegian Nynorsk (Nynorsk, Norwegian) */
+  {HB_TAG('n','o',' ',' '),	HB_TAG('N','O','R',' ')},	/* Norwegian [macrolanguage] */
+  {HB_TAG('n','r',' ',' '),	HB_TAG('N','D','B',' ')},	/* South Ndebele -> Ndebele */
+  {HB_TAG('n','v',' ',' '),	HB_TAG('N','A','V',' ')},	/* Navajo */
+  {HB_TAG('n','v',' ',' '),	HB_TAG('A','T','H',' ')},	/* Navajo -> Athapaskan */
+  {HB_TAG('n','y',' ',' '),	HB_TAG('C','H','I',' ')},	/* Chichewa (Chewa, Nyanja) */
+  {HB_TAG('o','c',' ',' '),	HB_TAG('O','C','I',' ')},	/* Occitan (post 1500) */
+  {HB_TAG('o','j',' ',' '),	HB_TAG('O','J','B',' ')},	/* Ojibwa [macrolanguage] -> Ojibway */
+  {HB_TAG('o','m',' ',' '),	HB_TAG('O','R','O',' ')},	/* Oromo [macrolanguage] */
+  {HB_TAG('o','r',' ',' '),	HB_TAG('O','R','I',' ')},	/* Odia (formerly Oriya) [macrolanguage] */
+  {HB_TAG('o','s',' ',' '),	HB_TAG('O','S','S',' ')},	/* Ossetian */
+  {HB_TAG('p','a',' ',' '),	HB_TAG('P','A','N',' ')},	/* Punjabi */
+  {HB_TAG('p','i',' ',' '),	HB_TAG('P','A','L',' ')},	/* Pali */
+  {HB_TAG('p','l',' ',' '),	HB_TAG('P','L','K',' ')},	/* Polish */
+  {HB_TAG('p','s',' ',' '),	HB_TAG('P','A','S',' ')},	/* Pashto [macrolanguage] */
+  {HB_TAG('p','t',' ',' '),	HB_TAG('P','T','G',' ')},	/* Portuguese */
+  {HB_TAG('q','u',' ',' '),	HB_TAG('Q','U','Z',' ')},	/* Quechua [macrolanguage] */
+  {HB_TAG('r','m',' ',' '),	HB_TAG('R','M','S',' ')},	/* Romansh */
+  {HB_TAG('r','n',' ',' '),	HB_TAG('R','U','N',' ')},	/* Rundi */
+  {HB_TAG('r','o',' ',' '),	HB_TAG('R','O','M',' ')},	/* Romanian */
+  {HB_TAG('r','u',' ',' '),	HB_TAG('R','U','S',' ')},	/* Russian */
+  {HB_TAG('r','w',' ',' '),	HB_TAG('R','U','A',' ')},	/* Kinyarwanda */
+  {HB_TAG('s','a',' ',' '),	HB_TAG('S','A','N',' ')},	/* Sanskrit */
+  {HB_TAG('s','c',' ',' '),	HB_TAG('S','R','D',' ')},	/* Sardinian [macrolanguage] */
+  {HB_TAG('s','d',' ',' '),	HB_TAG('S','N','D',' ')},	/* Sindhi */
+  {HB_TAG('s','e',' ',' '),	HB_TAG('N','S','M',' ')},	/* Northern Sami */
+  {HB_TAG('s','g',' ',' '),	HB_TAG('S','G','O',' ')},	/* Sango */
+  {HB_TAG('s','h',' ',' '),	HB_TAG('B','O','S',' ')},	/* Serbo-Croatian [macrolanguage] -> Bosnian */
+  {HB_TAG('s','h',' ',' '),	HB_TAG('H','R','V',' ')},	/* Serbo-Croatian [macrolanguage] -> Croatian */
+  {HB_TAG('s','h',' ',' '),	HB_TAG('S','R','B',' ')},	/* Serbo-Croatian [macrolanguage] -> Serbian */
+  {HB_TAG('s','i',' ',' '),	HB_TAG('S','N','H',' ')},	/* Sinhala (Sinhalese) */
+  {HB_TAG('s','k',' ',' '),	HB_TAG('S','K','Y',' ')},	/* Slovak */
+  {HB_TAG('s','l',' ',' '),	HB_TAG('S','L','V',' ')},	/* Slovenian */
+  {HB_TAG('s','m',' ',' '),	HB_TAG('S','M','O',' ')},	/* Samoan */
+  {HB_TAG('s','n',' ',' '),	HB_TAG('S','N','A','0')},	/* Shona */
+  {HB_TAG('s','o',' ',' '),	HB_TAG('S','M','L',' ')},	/* Somali */
+  {HB_TAG('s','q',' ',' '),	HB_TAG('S','Q','I',' ')},	/* Albanian [macrolanguage] */
+  {HB_TAG('s','r',' ',' '),	HB_TAG('S','R','B',' ')},	/* Serbian */
+  {HB_TAG('s','s',' ',' '),	HB_TAG('S','W','Z',' ')},	/* Swati */
+  {HB_TAG('s','t',' ',' '),	HB_TAG('S','O','T',' ')},	/* Southern Sotho */
+  {HB_TAG('s','u',' ',' '),	HB_TAG('S','U','N',' ')},	/* Sundanese */
+  {HB_TAG('s','v',' ',' '),	HB_TAG('S','V','E',' ')},	/* Swedish */
+  {HB_TAG('s','w',' ',' '),	HB_TAG('S','W','K',' ')},	/* Swahili [macrolanguage] */
+  {HB_TAG('t','a',' ',' '),	HB_TAG('T','A','M',' ')},	/* Tamil */
+  {HB_TAG('t','e',' ',' '),	HB_TAG('T','E','L',' ')},	/* Telugu */
+  {HB_TAG('t','g',' ',' '),	HB_TAG('T','A','J',' ')},	/* Tajik -> Tajiki */
+  {HB_TAG('t','h',' ',' '),	HB_TAG('T','H','A',' ')},	/* Thai */
+  {HB_TAG('t','i',' ',' '),	HB_TAG('T','G','Y',' ')},	/* Tigrinya */
+  {HB_TAG('t','k',' ',' '),	HB_TAG('T','K','M',' ')},	/* Turkmen */
+  {HB_TAG('t','l',' ',' '),	HB_TAG('T','G','L',' ')},	/* Tagalog */
+  {HB_TAG('t','n',' ',' '),	HB_TAG('T','N','A',' ')},	/* Tswana */
+  {HB_TAG('t','o',' ',' '),	HB_TAG('T','G','N',' ')},	/* Tonga (Tonga Islands) -> Tongan */
+  {HB_TAG('t','r',' ',' '),	HB_TAG('T','R','K',' ')},	/* Turkish */
+  {HB_TAG('t','s',' ',' '),	HB_TAG('T','S','G',' ')},	/* Tsonga */
+  {HB_TAG('t','t',' ',' '),	HB_TAG('T','A','T',' ')},	/* Tatar */
+  {HB_TAG('t','w',' ',' '),	HB_TAG('T','W','I',' ')},	/* Twi */
+  {HB_TAG('t','w',' ',' '),	HB_TAG('A','K','A',' ')},	/* Twi -> Akan */
+  {HB_TAG('t','y',' ',' '),	HB_TAG('T','H','T',' ')},	/* Tahitian */
+  {HB_TAG('u','g',' ',' '),	HB_TAG('U','Y','G',' ')},	/* Uyghur */
+  {HB_TAG('u','k',' ',' '),	HB_TAG('U','K','R',' ')},	/* Ukrainian */
+  {HB_TAG('u','r',' ',' '),	HB_TAG('U','R','D',' ')},	/* Urdu */
+  {HB_TAG('u','z',' ',' '),	HB_TAG('U','Z','B',' ')},	/* Uzbek [macrolanguage] */
+  {HB_TAG('v','e',' ',' '),	HB_TAG('V','E','N',' ')},	/* Venda */
+  {HB_TAG('v','i',' ',' '),	HB_TAG('V','I','T',' ')},	/* Vietnamese */
+  {HB_TAG('v','o',' ',' '),	HB_TAG('V','O','L',' ')},	/* Volapük */
+  {HB_TAG('w','a',' ',' '),	HB_TAG('W','L','N',' ')},	/* Walloon */
+  {HB_TAG('w','o',' ',' '),	HB_TAG('W','L','F',' ')},	/* Wolof */
+  {HB_TAG('x','h',' ',' '),	HB_TAG('X','H','S',' ')},	/* Xhosa */
+  {HB_TAG('y','i',' ',' '),	HB_TAG('J','I','I',' ')},	/* Yiddish [macrolanguage] */
+  {HB_TAG('y','o',' ',' '),	HB_TAG('Y','B','A',' ')},	/* Yoruba */
+  {HB_TAG('z','a',' ',' '),	HB_TAG('Z','H','A',' ')},	/* Zhuang [macrolanguage] */
+  {HB_TAG('z','h',' ',' '),	HB_TAG('Z','H','S',' ')},	/* Chinese, Simplified [macrolanguage] */
+  {HB_TAG('z','u',' ',' '),	HB_TAG('Z','U','L',' ')},	/* Zulu */
 };
 
+static const LangTag ot_languages3[] = {
+  {HB_TAG('a','a','e',' '),	HB_TAG('S','Q','I',' ')},	/* Arbëreshë Albanian -> Albanian */
+  {HB_TAG('a','a','o',' '),	HB_TAG('A','R','A',' ')},	/* Algerian Saharan Arabic -> Arabic */
+  {HB_TAG('a','a','t',' '),	HB_TAG('S','Q','I',' ')},	/* Arvanitika Albanian -> Albanian */
+  {HB_TAG('a','b','a',' '),	HB_TAG_NONE	       },	/* Abé != Abaza */
+  {HB_TAG('a','b','h',' '),	HB_TAG('A','R','A',' ')},	/* Tajiki Arabic -> Arabic */
+  {HB_TAG('a','b','q',' '),	HB_TAG('A','B','A',' ')},	/* Abaza */
+  {HB_TAG('a','b','s',' '),	HB_TAG('C','P','P',' ')},	/* Ambonese Malay -> Creoles */
+  {HB_TAG('a','b','v',' '),	HB_TAG('A','R','A',' ')},	/* Baharna Arabic -> Arabic */
+  {HB_TAG('a','c','f',' '),	HB_TAG('F','A','N',' ')},	/* Saint Lucian Creole French -> French Antillean */
+  {HB_TAG('a','c','f',' '),	HB_TAG('C','P','P',' ')},	/* Saint Lucian Creole French -> Creoles */
+/*{HB_TAG('a','c','h',' '),	HB_TAG('A','C','H',' ')},*/	/* Acoli -> Acholi */
+  {HB_TAG('a','c','m',' '),	HB_TAG('A','R','A',' ')},	/* Mesopotamian Arabic -> Arabic */
+  {HB_TAG('a','c','q',' '),	HB_TAG('A','R','A',' ')},	/* Ta'izzi-Adeni Arabic -> Arabic */
+  {HB_TAG('a','c','r',' '),	HB_TAG('A','C','R',' ')},	/* Achi */
+  {HB_TAG('a','c','r',' '),	HB_TAG('M','Y','N',' ')},	/* Achi -> Mayan */
+  {HB_TAG('a','c','w',' '),	HB_TAG('A','R','A',' ')},	/* Hijazi Arabic -> Arabic */
+  {HB_TAG('a','c','x',' '),	HB_TAG('A','R','A',' ')},	/* Omani Arabic -> Arabic */
+  {HB_TAG('a','c','y',' '),	HB_TAG('A','R','A',' ')},	/* Cypriot Arabic -> Arabic */
+  {HB_TAG('a','d','a',' '),	HB_TAG('D','N','G',' ')},	/* Adangme -> Dangme */
+  {HB_TAG('a','d','f',' '),	HB_TAG('A','R','A',' ')},	/* Dhofari Arabic -> Arabic */
+  {HB_TAG('a','d','p',' '),	HB_TAG('D','Z','N',' ')},	/* Adap (retired code) -> Dzongkha */
+/*{HB_TAG('a','d','y',' '),	HB_TAG('A','D','Y',' ')},*/	/* Adyghe */
+  {HB_TAG('a','e','b',' '),	HB_TAG('A','R','A',' ')},	/* Tunisian Arabic -> Arabic */
+  {HB_TAG('a','e','c',' '),	HB_TAG('A','R','A',' ')},	/* Saidi Arabic -> Arabic */
+  {HB_TAG('a','f','b',' '),	HB_TAG('A','R','A',' ')},	/* Gulf Arabic -> Arabic */
+  {HB_TAG('a','f','k',' '),	HB_TAG_NONE	       },	/* Nanubae != Afrikaans */
+  {HB_TAG('a','f','s',' '),	HB_TAG('C','P','P',' ')},	/* Afro-Seminole Creole -> Creoles */
+  {HB_TAG('a','g','u',' '),	HB_TAG('M','Y','N',' ')},	/* Aguacateco -> Mayan */
+  {HB_TAG('a','g','w',' '),	HB_TAG_NONE	       },	/* Kahua != Agaw */
+  {HB_TAG('a','h','g',' '),	HB_TAG('A','G','W',' ')},	/* Qimant -> Agaw */
+  {HB_TAG('a','h','t',' '),	HB_TAG('A','T','H',' ')},	/* Ahtena -> Athapaskan */
+  {HB_TAG('a','i','g',' '),	HB_TAG('C','P','P',' ')},	/* Antigua and Barbuda Creole English -> Creoles */
+  {HB_TAG('a','i','i',' '),	HB_TAG('S','W','A',' ')},	/* Assyrian Neo-Aramaic -> Swadaya Aramaic */
+  {HB_TAG('a','i','i',' '),	HB_TAG('S','Y','R',' ')},	/* Assyrian Neo-Aramaic -> Syriac */
+/*{HB_TAG('a','i','o',' '),	HB_TAG('A','I','O',' ')},*/	/* Aiton */
+  {HB_TAG('a','i','w',' '),	HB_TAG('A','R','I',' ')},	/* Aari */
+  {HB_TAG('a','j','p',' '),	HB_TAG('A','R','A',' ')},	/* South Levantine Arabic -> Arabic */
+  {HB_TAG('a','j','t',' '),	HB_TAG('A','R','A',' ')},	/* Judeo-Tunisian Arabic (retired code) -> Arabic */
+  {HB_TAG('a','k','b',' '),	HB_TAG('A','K','B',' ')},	/* Batak Angkola */
+  {HB_TAG('a','k','b',' '),	HB_TAG('B','T','K',' ')},	/* Batak Angkola -> Batak */
+  {HB_TAG('a','l','n',' '),	HB_TAG('S','Q','I',' ')},	/* Gheg Albanian -> Albanian */
+  {HB_TAG('a','l','s',' '),	HB_TAG('S','Q','I',' ')},	/* Tosk Albanian -> Albanian */
+/*{HB_TAG('a','l','t',' '),	HB_TAG('A','L','T',' ')},*/	/* Southern Altai -> Altai */
+  {HB_TAG('a','m','f',' '),	HB_TAG('H','B','N',' ')},	/* Hamer-Banna -> Hammer-Banna */
+  {HB_TAG('a','m','w',' '),	HB_TAG('S','Y','R',' ')},	/* Western Neo-Aramaic -> Syriac */
+/*{HB_TAG('a','n','g',' '),	HB_TAG('A','N','G',' ')},*/	/* Old English (ca. 450-1100) -> Anglo-Saxon */
+  {HB_TAG('a','o','a',' '),	HB_TAG('C','P','P',' ')},	/* Angolar -> Creoles */
+  {HB_TAG('a','p','a',' '),	HB_TAG('A','T','H',' ')},	/* Apache [collection] -> Athapaskan */
+  {HB_TAG('a','p','c',' '),	HB_TAG('A','R','A',' ')},	/* North Levantine Arabic -> Arabic */
+  {HB_TAG('a','p','d',' '),	HB_TAG('A','R','A',' ')},	/* Sudanese Arabic -> Arabic */
+  {HB_TAG('a','p','j',' '),	HB_TAG('A','T','H',' ')},	/* Jicarilla Apache -> Athapaskan */
+  {HB_TAG('a','p','k',' '),	HB_TAG('A','T','H',' ')},	/* Kiowa Apache -> Athapaskan */
+  {HB_TAG('a','p','l',' '),	HB_TAG('A','T','H',' ')},	/* Lipan Apache -> Athapaskan */
+  {HB_TAG('a','p','m',' '),	HB_TAG('A','T','H',' ')},	/* Mescalero-Chiricahua Apache -> Athapaskan */
+  {HB_TAG('a','p','w',' '),	HB_TAG('A','T','H',' ')},	/* Western Apache -> Athapaskan */
+  {HB_TAG('a','r','b',' '),	HB_TAG('A','R','A',' ')},	/* Standard Arabic -> Arabic */
+  {HB_TAG('a','r','i',' '),	HB_TAG_NONE	       },	/* Arikara != Aari */
+  {HB_TAG('a','r','k',' '),	HB_TAG_NONE	       },	/* Arikapú != Rakhine */
+  {HB_TAG('a','r','n',' '),	HB_TAG('M','A','P',' ')},	/* Mapudungun */
+  {HB_TAG('a','r','q',' '),	HB_TAG('A','R','A',' ')},	/* Algerian Arabic -> Arabic */
+  {HB_TAG('a','r','s',' '),	HB_TAG('A','R','A',' ')},	/* Najdi Arabic -> Arabic */
+  {HB_TAG('a','r','y',' '),	HB_TAG('M','O','R',' ')},	/* Moroccan Arabic -> Moroccan */
+  {HB_TAG('a','r','y',' '),	HB_TAG('A','R','A',' ')},	/* Moroccan Arabic -> Arabic */
+  {HB_TAG('a','r','z',' '),	HB_TAG('A','R','A',' ')},	/* Egyptian Arabic -> Arabic */
+/*{HB_TAG('a','s','t',' '),	HB_TAG('A','S','T',' ')},*/	/* Asturian */
+/*{HB_TAG('a','t','h',' '),	HB_TAG('A','T','H',' ')},*/	/* Athapascan [collection] -> Athapaskan */
+  {HB_TAG('a','t','j',' '),	HB_TAG('R','C','R',' ')},	/* Atikamekw -> R-Cree */
+  {HB_TAG('a','t','v',' '),	HB_TAG('A','L','T',' ')},	/* Northern Altai -> Altai */
+  {HB_TAG('a','u','j',' '),	HB_TAG('B','B','R',' ')},	/* Awjilah -> Berber */
+  {HB_TAG('a','u','z',' '),	HB_TAG('A','R','A',' ')},	/* Uzbeki Arabic -> Arabic */
+  {HB_TAG('a','v','l',' '),	HB_TAG('A','R','A',' ')},	/* Eastern Egyptian Bedawi Arabic -> Arabic */
+/*{HB_TAG('a','v','n',' '),	HB_TAG('A','V','N',' ')},*/	/* Avatime */
+/*{HB_TAG('a','w','a',' '),	HB_TAG('A','W','A',' ')},*/	/* Awadhi */
+  {HB_TAG('a','y','c',' '),	HB_TAG('A','Y','M',' ')},	/* Southern Aymara -> Aymara */
+  {HB_TAG('a','y','h',' '),	HB_TAG('A','R','A',' ')},	/* Hadrami Arabic -> Arabic */
+  {HB_TAG('a','y','l',' '),	HB_TAG('A','R','A',' ')},	/* Libyan Arabic -> Arabic */
+  {HB_TAG('a','y','n',' '),	HB_TAG('A','R','A',' ')},	/* Sanaani Arabic -> Arabic */
+  {HB_TAG('a','y','p',' '),	HB_TAG('A','R','A',' ')},	/* North Mesopotamian Arabic -> Arabic */
+  {HB_TAG('a','y','r',' '),	HB_TAG('A','Y','M',' ')},	/* Central Aymara -> Aymara */
+  {HB_TAG('a','z','b',' '),	HB_TAG('A','Z','B',' ')},	/* South Azerbaijani -> Torki */
+  {HB_TAG('a','z','b',' '),	HB_TAG('A','Z','E',' ')},	/* South Azerbaijani -> Azerbaijani */
+  {HB_TAG('a','z','d',' '),	HB_TAG('N','A','H',' ')},	/* Eastern Durango Nahuatl -> Nahuatl */
+  {HB_TAG('a','z','j',' '),	HB_TAG('A','Z','E',' ')},	/* North Azerbaijani -> Azerbaijani */
+  {HB_TAG('a','z','n',' '),	HB_TAG('N','A','H',' ')},	/* Western Durango Nahuatl -> Nahuatl */
+  {HB_TAG('a','z','z',' '),	HB_TAG('N','A','H',' ')},	/* Highland Puebla Nahuatl -> Nahuatl */
+  {HB_TAG('b','a','d',' '),	HB_TAG('B','A','D','0')},	/* Banda [collection] */
+  {HB_TAG('b','a','g',' '),	HB_TAG_NONE	       },	/* Tuki != Baghelkhandi */
+  {HB_TAG('b','a','h',' '),	HB_TAG('C','P','P',' ')},	/* Bahamas Creole English -> Creoles */
+  {HB_TAG('b','a','i',' '),	HB_TAG('B','M','L',' ')},	/* Bamileke [collection] */
+  {HB_TAG('b','a','l',' '),	HB_TAG('B','L','I',' ')},	/* Baluchi [macrolanguage] */
+/*{HB_TAG('b','a','n',' '),	HB_TAG('B','A','N',' ')},*/	/* Balinese */
+/*{HB_TAG('b','a','r',' '),	HB_TAG('B','A','R',' ')},*/	/* Bavarian */
+  {HB_TAG('b','a','u',' '),	HB_TAG_NONE	       },	/* Bada (Nigeria) != Baulé */
+  {HB_TAG('b','b','c',' '),	HB_TAG('B','B','C',' ')},	/* Batak Toba */
+  {HB_TAG('b','b','c',' '),	HB_TAG('B','T','K',' ')},	/* Batak Toba -> Batak */
+  {HB_TAG('b','b','j',' '),	HB_TAG('B','M','L',' ')},	/* Ghomálá' -> Bamileke */
+  {HB_TAG('b','b','p',' '),	HB_TAG('B','A','D','0')},	/* West Central Banda -> Banda */
+  {HB_TAG('b','b','r',' '),	HB_TAG_NONE	       },	/* Girawa != Berber */
+  {HB_TAG('b','b','z',' '),	HB_TAG('A','R','A',' ')},	/* Babalia Creole Arabic (retired code) -> Arabic */
+  {HB_TAG('b','c','c',' '),	HB_TAG('B','L','I',' ')},	/* Southern Balochi -> Baluchi */
+  {HB_TAG('b','c','h',' '),	HB_TAG_NONE	       },	/* Bariai != Bench */
+  {HB_TAG('b','c','i',' '),	HB_TAG('B','A','U',' ')},	/* Baoulé -> Baulé */
+  {HB_TAG('b','c','l',' '),	HB_TAG('B','I','K',' ')},	/* Central Bikol -> Bikol */
+  {HB_TAG('b','c','q',' '),	HB_TAG('B','C','H',' ')},	/* Bench */
+  {HB_TAG('b','c','r',' '),	HB_TAG('A','T','H',' ')},	/* Babine -> Athapaskan */
+/*{HB_TAG('b','d','y',' '),	HB_TAG('B','D','Y',' ')},*/	/* Bandjalang */
+  {HB_TAG('b','e','a',' '),	HB_TAG('A','T','H',' ')},	/* Beaver -> Athapaskan */
+  {HB_TAG('b','e','b',' '),	HB_TAG('B','T','I',' ')},	/* Bebele -> Beti */
+/*{HB_TAG('b','e','m',' '),	HB_TAG('B','E','M',' ')},*/	/* Bemba (Zambia) */
+  {HB_TAG('b','e','r',' '),	HB_TAG('B','B','R',' ')},	/* Berber [collection] */
+  {HB_TAG('b','e','w',' '),	HB_TAG('C','P','P',' ')},	/* Betawi -> Creoles */
+  {HB_TAG('b','f','l',' '),	HB_TAG('B','A','D','0')},	/* Banda-Ndélé -> Banda */
+  {HB_TAG('b','f','q',' '),	HB_TAG('B','A','D',' ')},	/* Badaga */
+  {HB_TAG('b','f','t',' '),	HB_TAG('B','L','T',' ')},	/* Balti */
+  {HB_TAG('b','f','u',' '),	HB_TAG('L','A','H',' ')},	/* Gahri -> Lahuli */
+  {HB_TAG('b','f','y',' '),	HB_TAG('B','A','G',' ')},	/* Bagheli -> Baghelkhandi */
+/*{HB_TAG('b','g','c',' '),	HB_TAG('B','G','C',' ')},*/	/* Haryanvi */
+  {HB_TAG('b','g','n',' '),	HB_TAG('B','L','I',' ')},	/* Western Balochi -> Baluchi */
+  {HB_TAG('b','g','p',' '),	HB_TAG('B','L','I',' ')},	/* Eastern Balochi -> Baluchi */
+  {HB_TAG('b','g','q',' '),	HB_TAG('B','G','Q',' ')},	/* Bagri */
+  {HB_TAG('b','g','q',' '),	HB_TAG('R','A','J',' ')},	/* Bagri -> Rajasthani */
+  {HB_TAG('b','g','r',' '),	HB_TAG('Q','I','N',' ')},	/* Bawm Chin -> Chin */
+  {HB_TAG('b','h','b',' '),	HB_TAG('B','H','I',' ')},	/* Bhili */
+/*{HB_TAG('b','h','i',' '),	HB_TAG('B','H','I',' ')},*/	/* Bhilali -> Bhili */
+  {HB_TAG('b','h','k',' '),	HB_TAG('B','I','K',' ')},	/* Albay Bicolano (retired code) -> Bikol */
+/*{HB_TAG('b','h','o',' '),	HB_TAG('B','H','O',' ')},*/	/* Bhojpuri */
+  {HB_TAG('b','h','r',' '),	HB_TAG('M','L','G',' ')},	/* Bara Malagasy -> Malagasy */
+/*{HB_TAG('b','i','k',' '),	HB_TAG('B','I','K',' ')},*/	/* Bikol [macrolanguage] */
+  {HB_TAG('b','i','l',' '),	HB_TAG_NONE	       },	/* Bile != Bilen */
+  {HB_TAG('b','i','n',' '),	HB_TAG('E','D','O',' ')},	/* Edo */
+  {HB_TAG('b','i','u',' '),	HB_TAG('Q','I','N',' ')},	/* Biete -> Chin */
+/*{HB_TAG('b','j','j',' '),	HB_TAG('B','J','J',' ')},*/	/* Kanauji */
+  {HB_TAG('b','j','n',' '),	HB_TAG('M','L','Y',' ')},	/* Banjar -> Malay */
+  {HB_TAG('b','j','o',' '),	HB_TAG('B','A','D','0')},	/* Mid-Southern Banda -> Banda */
+  {HB_TAG('b','j','q',' '),	HB_TAG('M','L','G',' ')},	/* Southern Betsimisaraka Malagasy (retired code) -> Malagasy */
+  {HB_TAG('b','j','s',' '),	HB_TAG('C','P','P',' ')},	/* Bajan -> Creoles */
+  {HB_TAG('b','j','t',' '),	HB_TAG('B','L','N',' ')},	/* Balanta-Ganja -> Balante */
+  {HB_TAG('b','k','f',' '),	HB_TAG_NONE	       },	/* Beeke != Blackfoot */
+  {HB_TAG('b','k','o',' '),	HB_TAG('B','M','L',' ')},	/* Kwa' -> Bamileke */
+  {HB_TAG('b','l','a',' '),	HB_TAG('B','K','F',' ')},	/* Siksika -> Blackfoot */
+  {HB_TAG('b','l','e',' '),	HB_TAG('B','L','N',' ')},	/* Balanta-Kentohe -> Balante */
+  {HB_TAG('b','l','g',' '),	HB_TAG('I','B','A',' ')},	/* Balau (retired code) -> Iban */
+  {HB_TAG('b','l','i',' '),	HB_TAG_NONE	       },	/* Bolia != Baluchi */
+  {HB_TAG('b','l','k',' '),	HB_TAG('B','L','K',' ')},	/* Pa’o Karen */
+  {HB_TAG('b','l','k',' '),	HB_TAG('K','R','N',' ')},	/* Pa'o Karen -> Karen */
+  {HB_TAG('b','l','n',' '),	HB_TAG('B','I','K',' ')},	/* Southern Catanduanes Bikol -> Bikol */
+  {HB_TAG('b','l','t',' '),	HB_TAG_NONE	       },	/* Tai Dam != Balti */
+  {HB_TAG('b','m','b',' '),	HB_TAG_NONE	       },	/* Bembe != Bambara (Bamanankan) */
+  {HB_TAG('b','m','l',' '),	HB_TAG_NONE	       },	/* Bomboli != Bamileke */
+  {HB_TAG('b','m','m',' '),	HB_TAG('M','L','G',' ')},	/* Northern Betsimisaraka Malagasy -> Malagasy */
+  {HB_TAG('b','p','d',' '),	HB_TAG('B','A','D','0')},	/* Banda-Banda -> Banda */
+  {HB_TAG('b','p','l',' '),	HB_TAG('C','P','P',' ')},	/* Broome Pearling Lugger Pidgin -> Creoles */
+  {HB_TAG('b','p','q',' '),	HB_TAG('C','P','P',' ')},	/* Banda Malay -> Creoles */
+/*{HB_TAG('b','p','y',' '),	HB_TAG('B','P','Y',' ')},*/	/* Bishnupriya -> Bishnupriya Manipuri */
+  {HB_TAG('b','q','i',' '),	HB_TAG('L','R','C',' ')},	/* Bakhtiari -> Luri */
+  {HB_TAG('b','q','k',' '),	HB_TAG('B','A','D','0')},	/* Banda-Mbrès -> Banda */
+  {HB_TAG('b','r','a',' '),	HB_TAG('B','R','I',' ')},	/* Braj -> Braj Bhasha */
+  {HB_TAG('b','r','c',' '),	HB_TAG('C','P','P',' ')},	/* Berbice Creole Dutch -> Creoles */
+/*{HB_TAG('b','r','h',' '),	HB_TAG('B','R','H',' ')},*/	/* Brahui */
+  {HB_TAG('b','r','i',' '),	HB_TAG_NONE	       },	/* Mokpwe != Braj Bhasha */
+  {HB_TAG('b','r','m',' '),	HB_TAG_NONE	       },	/* Barambu != Burmese */
+/*{HB_TAG('b','r','x',' '),	HB_TAG('B','R','X',' ')},*/	/* Bodo (India) */
+  {HB_TAG('b','s','h',' '),	HB_TAG_NONE	       },	/* Kati != Bashkir */
+/*{HB_TAG('b','s','k',' '),	HB_TAG('B','S','K',' ')},*/	/* Burushaski */
+  {HB_TAG('b','t','b',' '),	HB_TAG('B','T','I',' ')},	/* Beti (Cameroon) (retired code) */
+  {HB_TAG('b','t','d',' '),	HB_TAG('B','T','D',' ')},	/* Batak Dairi (Pakpak) */
+  {HB_TAG('b','t','d',' '),	HB_TAG('B','T','K',' ')},	/* Batak Dairi -> Batak */
+  {HB_TAG('b','t','i',' '),	HB_TAG_NONE	       },	/* Burate != Beti */
+  {HB_TAG('b','t','j',' '),	HB_TAG('M','L','Y',' ')},	/* Bacanese Malay -> Malay */
+/*{HB_TAG('b','t','k',' '),	HB_TAG('B','T','K',' ')},*/	/* Batak [collection] */
+  {HB_TAG('b','t','m',' '),	HB_TAG('B','T','M',' ')},	/* Batak Mandailing */
+  {HB_TAG('b','t','m',' '),	HB_TAG('B','T','K',' ')},	/* Batak Mandailing -> Batak */
+  {HB_TAG('b','t','o',' '),	HB_TAG('B','I','K',' ')},	/* Rinconada Bikol -> Bikol */
+  {HB_TAG('b','t','s',' '),	HB_TAG('B','T','S',' ')},	/* Batak Simalungun */
+  {HB_TAG('b','t','s',' '),	HB_TAG('B','T','K',' ')},	/* Batak Simalungun -> Batak */
+  {HB_TAG('b','t','x',' '),	HB_TAG('B','T','X',' ')},	/* Batak Karo */
+  {HB_TAG('b','t','x',' '),	HB_TAG('B','T','K',' ')},	/* Batak Karo -> Batak */
+  {HB_TAG('b','t','z',' '),	HB_TAG('B','T','Z',' ')},	/* Batak Alas-Kluet */
+  {HB_TAG('b','t','z',' '),	HB_TAG('B','T','K',' ')},	/* Batak Alas-Kluet -> Batak */
+/*{HB_TAG('b','u','g',' '),	HB_TAG('B','U','G',' ')},*/	/* Buginese -> Bugis */
+  {HB_TAG('b','u','m',' '),	HB_TAG('B','T','I',' ')},	/* Bulu (Cameroon) -> Beti */
+  {HB_TAG('b','v','e',' '),	HB_TAG('M','L','Y',' ')},	/* Berau Malay -> Malay */
+  {HB_TAG('b','v','u',' '),	HB_TAG('M','L','Y',' ')},	/* Bukit Malay -> Malay */
+  {HB_TAG('b','w','e',' '),	HB_TAG('K','R','N',' ')},	/* Bwe Karen -> Karen */
+  {HB_TAG('b','x','k',' '),	HB_TAG('L','U','H',' ')},	/* Bukusu -> Luyia */
+  {HB_TAG('b','x','o',' '),	HB_TAG('C','P','P',' ')},	/* Barikanchi -> Creoles */
+  {HB_TAG('b','x','p',' '),	HB_TAG('B','T','I',' ')},	/* Bebil -> Beti */
+  {HB_TAG('b','x','r',' '),	HB_TAG('R','B','U',' ')},	/* Russia Buriat -> Russian Buriat */
+  {HB_TAG('b','y','n',' '),	HB_TAG('B','I','L',' ')},	/* Bilin -> Bilen */
+  {HB_TAG('b','y','v',' '),	HB_TAG('B','Y','V',' ')},	/* Medumba */
+  {HB_TAG('b','y','v',' '),	HB_TAG('B','M','L',' ')},	/* Medumba -> Bamileke */
+  {HB_TAG('b','z','c',' '),	HB_TAG('M','L','G',' ')},	/* Southern Betsimisaraka Malagasy -> Malagasy */
+  {HB_TAG('b','z','j',' '),	HB_TAG('C','P','P',' ')},	/* Belize Kriol English -> Creoles */
+  {HB_TAG('b','z','k',' '),	HB_TAG('C','P','P',' ')},	/* Nicaragua Creole English -> Creoles */
+  {HB_TAG('c','a','a',' '),	HB_TAG('M','Y','N',' ')},	/* Chortí -> Mayan */
+  {HB_TAG('c','a','c',' '),	HB_TAG('M','Y','N',' ')},	/* Chuj -> Mayan */
+  {HB_TAG('c','a','f',' '),	HB_TAG('C','R','R',' ')},	/* Southern Carrier -> Carrier */
+  {HB_TAG('c','a','f',' '),	HB_TAG('A','T','H',' ')},	/* Southern Carrier -> Athapaskan */
+  {HB_TAG('c','a','k',' '),	HB_TAG('C','A','K',' ')},	/* Kaqchikel */
+  {HB_TAG('c','a','k',' '),	HB_TAG('M','Y','N',' ')},	/* Kaqchikel -> Mayan */
+  {HB_TAG('c','b','k',' '),	HB_TAG('C','B','K',' ')},	/* Chavacano -> Zamboanga Chavacano */
+  {HB_TAG('c','b','k',' '),	HB_TAG('C','P','P',' ')},	/* Chavacano -> Creoles */
+  {HB_TAG('c','b','l',' '),	HB_TAG('Q','I','N',' ')},	/* Bualkhaw Chin -> Chin */
+  {HB_TAG('c','c','l',' '),	HB_TAG('C','P','P',' ')},	/* Cutchi-Swahili -> Creoles */
+  {HB_TAG('c','c','m',' '),	HB_TAG('C','P','P',' ')},	/* Malaccan Creole Malay -> Creoles */
+  {HB_TAG('c','c','o',' '),	HB_TAG('C','C','H','N')},	/* Comaltepec Chinantec -> Chinantec */
+  {HB_TAG('c','c','q',' '),	HB_TAG('A','R','K',' ')},	/* Chaungtha (retired code) -> Rakhine */
+  {HB_TAG('c','d','o',' '),	HB_TAG('Z','H','S',' ')},	/* Min Dong Chinese -> Chinese, Simplified */
+/*{HB_TAG('c','e','b',' '),	HB_TAG('C','E','B',' ')},*/	/* Cebuano */
+  {HB_TAG('c','e','k',' '),	HB_TAG('Q','I','N',' ')},	/* Eastern Khumi Chin -> Chin */
+  {HB_TAG('c','e','y',' '),	HB_TAG('Q','I','N',' ')},	/* Ekai Chin -> Chin */
+  {HB_TAG('c','f','m',' '),	HB_TAG('H','A','L',' ')},	/* Halam (Falam Chin) */
+  {HB_TAG('c','f','m',' '),	HB_TAG('Q','I','N',' ')},	/* Falam Chin -> Chin */
+/*{HB_TAG('c','g','g',' '),	HB_TAG('C','G','G',' ')},*/	/* Chiga */
+  {HB_TAG('c','h','f',' '),	HB_TAG('M','Y','N',' ')},	/* Tabasco Chontal -> Mayan */
+  {HB_TAG('c','h','g',' '),	HB_TAG_NONE	       },	/* Chagatai != Chaha Gurage */
+  {HB_TAG('c','h','h',' '),	HB_TAG_NONE	       },	/* Chinook != Chattisgarhi */
+  {HB_TAG('c','h','j',' '),	HB_TAG('C','C','H','N')},	/* Ojitlán Chinantec -> Chinantec */
+  {HB_TAG('c','h','k',' '),	HB_TAG('C','H','K','0')},	/* Chuukese */
+  {HB_TAG('c','h','m',' '),	HB_TAG('H','M','A',' ')},	/* Mari (Russia) [macrolanguage] -> High Mari */
+  {HB_TAG('c','h','m',' '),	HB_TAG('L','M','A',' ')},	/* Mari (Russia) [macrolanguage] -> Low Mari */
+  {HB_TAG('c','h','n',' '),	HB_TAG('C','P','P',' ')},	/* Chinook jargon -> Creoles */
+/*{HB_TAG('c','h','o',' '),	HB_TAG('C','H','O',' ')},*/	/* Choctaw */
+  {HB_TAG('c','h','p',' '),	HB_TAG('C','H','P',' ')},	/* Chipewyan */
+  {HB_TAG('c','h','p',' '),	HB_TAG('S','A','Y',' ')},	/* Chipewyan -> Sayisi */
+  {HB_TAG('c','h','p',' '),	HB_TAG('A','T','H',' ')},	/* Chipewyan -> Athapaskan */
+  {HB_TAG('c','h','q',' '),	HB_TAG('C','C','H','N')},	/* Quiotepec Chinantec -> Chinantec */
+/*{HB_TAG('c','h','r',' '),	HB_TAG('C','H','R',' ')},*/	/* Cherokee */
+/*{HB_TAG('c','h','y',' '),	HB_TAG('C','H','Y',' ')},*/	/* Cheyenne */
+  {HB_TAG('c','h','z',' '),	HB_TAG('C','C','H','N')},	/* Ozumacín Chinantec -> Chinantec */
+  {HB_TAG('c','i','w',' '),	HB_TAG('O','J','B',' ')},	/* Chippewa -> Ojibway */
+/*{HB_TAG('c','j','a',' '),	HB_TAG('C','J','A',' ')},*/	/* Western Cham */
+/*{HB_TAG('c','j','m',' '),	HB_TAG('C','J','M',' ')},*/	/* Eastern Cham */
+  {HB_TAG('c','j','y',' '),	HB_TAG('Z','H','S',' ')},	/* Jinyu Chinese -> Chinese, Simplified */
+  {HB_TAG('c','k','a',' '),	HB_TAG('Q','I','N',' ')},	/* Khumi Awa Chin (retired code) -> Chin */
+  {HB_TAG('c','k','b',' '),	HB_TAG('K','U','R',' ')},	/* Central Kurdish -> Kurdish */
+  {HB_TAG('c','k','n',' '),	HB_TAG('Q','I','N',' ')},	/* Kaang Chin -> Chin */
+  {HB_TAG('c','k','s',' '),	HB_TAG('C','P','P',' ')},	/* Tayo -> Creoles */
+  {HB_TAG('c','k','t',' '),	HB_TAG('C','H','K',' ')},	/* Chukot -> Chukchi */
+  {HB_TAG('c','k','z',' '),	HB_TAG('M','Y','N',' ')},	/* Cakchiquel-Quiché Mixed Language -> Mayan */
+  {HB_TAG('c','l','c',' '),	HB_TAG('A','T','H',' ')},	/* Chilcotin -> Athapaskan */
+  {HB_TAG('c','l','d',' '),	HB_TAG('S','Y','R',' ')},	/* Chaldean Neo-Aramaic -> Syriac */
+  {HB_TAG('c','l','e',' '),	HB_TAG('C','C','H','N')},	/* Lealao Chinantec -> Chinantec */
+  {HB_TAG('c','l','j',' '),	HB_TAG('Q','I','N',' ')},	/* Laitu Chin -> Chin */
+  {HB_TAG('c','l','t',' '),	HB_TAG('Q','I','N',' ')},	/* Lautu Chin -> Chin */
+  {HB_TAG('c','m','n',' '),	HB_TAG('Z','H','S',' ')},	/* Mandarin Chinese -> Chinese, Simplified */
+  {HB_TAG('c','m','r',' '),	HB_TAG('Q','I','N',' ')},	/* Mro-Khimi Chin -> Chin */
+  {HB_TAG('c','n','b',' '),	HB_TAG('Q','I','N',' ')},	/* Chinbon Chin -> Chin */
+  {HB_TAG('c','n','h',' '),	HB_TAG('Q','I','N',' ')},	/* Hakha Chin -> Chin */
+  {HB_TAG('c','n','k',' '),	HB_TAG('Q','I','N',' ')},	/* Khumi Chin -> Chin */
+  {HB_TAG('c','n','l',' '),	HB_TAG('C','C','H','N')},	/* Lalana Chinantec -> Chinantec */
+  {HB_TAG('c','n','p',' '),	HB_TAG('Z','H','S',' ')},	/* Northern Ping Chinese -> Chinese, Simplified */
+  {HB_TAG('c','n','r',' '),	HB_TAG('S','R','B',' ')},	/* Montenegrin -> Serbian */
+  {HB_TAG('c','n','t',' '),	HB_TAG('C','C','H','N')},	/* Tepetotutla Chinantec -> Chinantec */
+  {HB_TAG('c','n','u',' '),	HB_TAG('B','B','R',' ')},	/* Chenoua -> Berber */
+  {HB_TAG('c','n','w',' '),	HB_TAG('Q','I','N',' ')},	/* Ngawn Chin -> Chin */
+  {HB_TAG('c','o','a',' '),	HB_TAG('M','L','Y',' ')},	/* Cocos Islands Malay -> Malay */
+  {HB_TAG('c','o','b',' '),	HB_TAG('M','Y','N',' ')},	/* Chicomuceltec -> Mayan */
+/*{HB_TAG('c','o','p',' '),	HB_TAG('C','O','P',' ')},*/	/* Coptic */
+  {HB_TAG('c','o','q',' '),	HB_TAG('A','T','H',' ')},	/* Coquille -> Athapaskan */
+  {HB_TAG('c','p','a',' '),	HB_TAG('C','C','H','N')},	/* Palantla Chinantec -> Chinantec */
+  {HB_TAG('c','p','e',' '),	HB_TAG('C','P','P',' ')},	/* English-based creoles and pidgins [collection] -> Creoles */
+  {HB_TAG('c','p','f',' '),	HB_TAG('C','P','P',' ')},	/* French-based creoles and pidgins [collection] -> Creoles */
+  {HB_TAG('c','p','i',' '),	HB_TAG('C','P','P',' ')},	/* Chinese Pidgin English -> Creoles */
+/*{HB_TAG('c','p','p',' '),	HB_TAG('C','P','P',' ')},*/	/* Portuguese-based creoles and pidgins [collection] -> Creoles */
+  {HB_TAG('c','p','x',' '),	HB_TAG('Z','H','S',' ')},	/* Pu-Xian Chinese -> Chinese, Simplified */
+  {HB_TAG('c','q','d',' '),	HB_TAG('H','M','N',' ')},	/* Chuanqiandian Cluster Miao -> Hmong */
+  {HB_TAG('c','q','u',' '),	HB_TAG('Q','U','H',' ')},	/* Chilean Quechua (retired code) -> Quechua (Bolivia) */
+  {HB_TAG('c','q','u',' '),	HB_TAG('Q','U','Z',' ')},	/* Chilean Quechua (retired code) -> Quechua */
+  {HB_TAG('c','r','h',' '),	HB_TAG('C','R','T',' ')},	/* Crimean Tatar */
+  {HB_TAG('c','r','i',' '),	HB_TAG('C','P','P',' ')},	/* Sãotomense -> Creoles */
+  {HB_TAG('c','r','j',' '),	HB_TAG('E','C','R',' ')},	/* Southern East Cree -> Eastern Cree */
+  {HB_TAG('c','r','j',' '),	HB_TAG('Y','C','R',' ')},	/* Southern East Cree -> Y-Cree */
+  {HB_TAG('c','r','j',' '),	HB_TAG('C','R','E',' ')},	/* Southern East Cree -> Cree */
+  {HB_TAG('c','r','k',' '),	HB_TAG('W','C','R',' ')},	/* Plains Cree -> West-Cree */
+  {HB_TAG('c','r','k',' '),	HB_TAG('Y','C','R',' ')},	/* Plains Cree -> Y-Cree */
+  {HB_TAG('c','r','k',' '),	HB_TAG('C','R','E',' ')},	/* Plains Cree -> Cree */
+  {HB_TAG('c','r','l',' '),	HB_TAG('E','C','R',' ')},	/* Northern East Cree -> Eastern Cree */
+  {HB_TAG('c','r','l',' '),	HB_TAG('Y','C','R',' ')},	/* Northern East Cree -> Y-Cree */
+  {HB_TAG('c','r','l',' '),	HB_TAG('C','R','E',' ')},	/* Northern East Cree -> Cree */
+  {HB_TAG('c','r','m',' '),	HB_TAG('M','C','R',' ')},	/* Moose Cree */
+  {HB_TAG('c','r','m',' '),	HB_TAG('L','C','R',' ')},	/* Moose Cree -> L-Cree */
+  {HB_TAG('c','r','m',' '),	HB_TAG('C','R','E',' ')},	/* Moose Cree -> Cree */
+  {HB_TAG('c','r','p',' '),	HB_TAG('C','P','P',' ')},	/* Creoles and pidgins [collection] -> Creoles */
+  {HB_TAG('c','r','r',' '),	HB_TAG_NONE	       },	/* Carolina Algonquian != Carrier */
+  {HB_TAG('c','r','s',' '),	HB_TAG('C','P','P',' ')},	/* Seselwa Creole French -> Creoles */
+  {HB_TAG('c','r','t',' '),	HB_TAG_NONE	       },	/* Iyojwa'ja Chorote != Crimean Tatar */
+  {HB_TAG('c','r','x',' '),	HB_TAG('C','R','R',' ')},	/* Carrier */
+  {HB_TAG('c','r','x',' '),	HB_TAG('A','T','H',' ')},	/* Carrier -> Athapaskan */
+  {HB_TAG('c','s','a',' '),	HB_TAG('C','C','H','N')},	/* Chiltepec Chinantec -> Chinantec */
+/*{HB_TAG('c','s','b',' '),	HB_TAG('C','S','B',' ')},*/	/* Kashubian */
+  {HB_TAG('c','s','h',' '),	HB_TAG('Q','I','N',' ')},	/* Asho Chin -> Chin */
+  {HB_TAG('c','s','j',' '),	HB_TAG('Q','I','N',' ')},	/* Songlai Chin -> Chin */
+  {HB_TAG('c','s','l',' '),	HB_TAG_NONE	       },	/* Chinese Sign Language != Church Slavonic */
+  {HB_TAG('c','s','o',' '),	HB_TAG('C','C','H','N')},	/* Sochiapam Chinantec -> Chinantec */
+  {HB_TAG('c','s','p',' '),	HB_TAG('Z','H','S',' ')},	/* Southern Ping Chinese -> Chinese, Simplified */
+  {HB_TAG('c','s','v',' '),	HB_TAG('Q','I','N',' ')},	/* Sumtu Chin -> Chin */
+  {HB_TAG('c','s','w',' '),	HB_TAG('N','C','R',' ')},	/* Swampy Cree -> N-Cree */
+  {HB_TAG('c','s','w',' '),	HB_TAG('N','H','C',' ')},	/* Swampy Cree -> Norway House Cree */
+  {HB_TAG('c','s','w',' '),	HB_TAG('C','R','E',' ')},	/* Swampy Cree -> Cree */
+  {HB_TAG('c','s','y',' '),	HB_TAG('Q','I','N',' ')},	/* Siyin Chin -> Chin */
+  {HB_TAG('c','t','c',' '),	HB_TAG('A','T','H',' ')},	/* Chetco -> Athapaskan */
+  {HB_TAG('c','t','d',' '),	HB_TAG('Q','I','N',' ')},	/* Tedim Chin -> Chin */
+  {HB_TAG('c','t','e',' '),	HB_TAG('C','C','H','N')},	/* Tepinapa Chinantec -> Chinantec */
+/*{HB_TAG('c','t','g',' '),	HB_TAG('C','T','G',' ')},*/	/* Chittagonian */
+  {HB_TAG('c','t','h',' '),	HB_TAG('Q','I','N',' ')},	/* Thaiphum Chin -> Chin */
+  {HB_TAG('c','t','l',' '),	HB_TAG('C','C','H','N')},	/* Tlacoatzintepec Chinantec -> Chinantec */
+  {HB_TAG('c','t','s',' '),	HB_TAG('B','I','K',' ')},	/* Northern Catanduanes Bikol -> Bikol */
+/*{HB_TAG('c','t','t',' '),	HB_TAG('C','T','T',' ')},*/	/* Wayanad Chetti */
+  {HB_TAG('c','t','u',' '),	HB_TAG('M','Y','N',' ')},	/* Chol -> Mayan */
+  {HB_TAG('c','u','c',' '),	HB_TAG('C','C','H','N')},	/* Usila Chinantec -> Chinantec */
+/*{HB_TAG('c','u','k',' '),	HB_TAG('C','U','K',' ')},*/	/* San Blas Kuna */
+  {HB_TAG('c','v','n',' '),	HB_TAG('C','C','H','N')},	/* Valle Nacional Chinantec -> Chinantec */
+  {HB_TAG('c','w','d',' '),	HB_TAG('D','C','R',' ')},	/* Woods Cree */
+  {HB_TAG('c','w','d',' '),	HB_TAG('T','C','R',' ')},	/* Woods Cree -> TH-Cree */
+  {HB_TAG('c','w','d',' '),	HB_TAG('C','R','E',' ')},	/* Woods Cree -> Cree */
+  {HB_TAG('c','z','h',' '),	HB_TAG('Z','H','S',' ')},	/* Huizhou Chinese -> Chinese, Simplified */
+  {HB_TAG('c','z','o',' '),	HB_TAG('Z','H','S',' ')},	/* Min Zhong Chinese -> Chinese, Simplified */
+  {HB_TAG('c','z','t',' '),	HB_TAG('Q','I','N',' ')},	/* Zotung Chin -> Chin */
+/*{HB_TAG('d','a','g',' '),	HB_TAG('D','A','G',' ')},*/	/* Dagbani */
+  {HB_TAG('d','a','o',' '),	HB_TAG('Q','I','N',' ')},	/* Daai Chin -> Chin */
+  {HB_TAG('d','a','p',' '),	HB_TAG('N','I','S',' ')},	/* Nisi (India) (retired code) */
+/*{HB_TAG('d','a','r',' '),	HB_TAG('D','A','R',' ')},*/	/* Dargwa */
+/*{HB_TAG('d','a','x',' '),	HB_TAG('D','A','X',' ')},*/	/* Dayi */
+  {HB_TAG('d','c','r',' '),	HB_TAG('C','P','P',' ')},	/* Negerhollands -> Creoles */
+  {HB_TAG('d','e','n',' '),	HB_TAG('S','L','A',' ')},	/* Slave (Athapascan) [macrolanguage] -> Slavey */
+  {HB_TAG('d','e','n',' '),	HB_TAG('A','T','H',' ')},	/* Slave (Athapascan) [macrolanguage] -> Athapaskan */
+  {HB_TAG('d','e','p',' '),	HB_TAG('C','P','P',' ')},	/* Pidgin Delaware -> Creoles */
+  {HB_TAG('d','g','o',' '),	HB_TAG('D','G','O',' ')},	/* Dogri (individual language) */
+  {HB_TAG('d','g','o',' '),	HB_TAG('D','G','R',' ')},	/* Dogri (macrolanguage) */
+  {HB_TAG('d','g','r',' '),	HB_TAG('A','T','H',' ')},	/* Dogrib -> Athapaskan */
+  {HB_TAG('d','h','d',' '),	HB_TAG('M','A','W',' ')},	/* Dhundari -> Marwari */
+/*{HB_TAG('d','h','g',' '),	HB_TAG('D','H','G',' ')},*/	/* Dhangu */
+  {HB_TAG('d','h','v',' '),	HB_TAG_NONE	       },	/* Dehu != Divehi (Dhivehi, Maldivian) (deprecated) */
+  {HB_TAG('d','i','b',' '),	HB_TAG('D','N','K',' ')},	/* South Central Dinka -> Dinka */
+  {HB_TAG('d','i','k',' '),	HB_TAG('D','N','K',' ')},	/* Southwestern Dinka -> Dinka */
+  {HB_TAG('d','i','n',' '),	HB_TAG('D','N','K',' ')},	/* Dinka [macrolanguage] */
+  {HB_TAG('d','i','p',' '),	HB_TAG('D','N','K',' ')},	/* Northeastern Dinka -> Dinka */
+  {HB_TAG('d','i','q',' '),	HB_TAG('D','I','Q',' ')},	/* Dimli */
+  {HB_TAG('d','i','q',' '),	HB_TAG('Z','Z','A',' ')},	/* Dimli -> Zazaki */
+  {HB_TAG('d','i','w',' '),	HB_TAG('D','N','K',' ')},	/* Northwestern Dinka -> Dinka */
+  {HB_TAG('d','j','e',' '),	HB_TAG('D','J','R',' ')},	/* Zarma */
+  {HB_TAG('d','j','k',' '),	HB_TAG('C','P','P',' ')},	/* Eastern Maroon Creole -> Creoles */
+  {HB_TAG('d','j','r',' '),	HB_TAG('D','J','R','0')},	/* Djambarrpuyngu */
+  {HB_TAG('d','k','s',' '),	HB_TAG('D','N','K',' ')},	/* Southeastern Dinka -> Dinka */
+  {HB_TAG('d','n','g',' '),	HB_TAG('D','U','N',' ')},	/* Dungan */
+/*{HB_TAG('d','n','j',' '),	HB_TAG('D','N','J',' ')},*/	/* Dan */
+  {HB_TAG('d','n','k',' '),	HB_TAG_NONE	       },	/* Dengka != Dinka */
+  {HB_TAG('d','o','i',' '),	HB_TAG('D','G','R',' ')},	/* Dogri (macrolanguage) [macrolanguage] */
+  {HB_TAG('d','r','h',' '),	HB_TAG('M','N','G',' ')},	/* Darkhat (retired code) -> Mongolian */
+  {HB_TAG('d','r','i',' '),	HB_TAG_NONE	       },	/* C'Lela != Dari */
+  {HB_TAG('d','r','w',' '),	HB_TAG('D','R','I',' ')},	/* Darwazi (retired code) -> Dari */
+  {HB_TAG('d','r','w',' '),	HB_TAG('F','A','R',' ')},	/* Darwazi (retired code) -> Persian */
+  {HB_TAG('d','s','b',' '),	HB_TAG('L','S','B',' ')},	/* Lower Sorbian */
+  {HB_TAG('d','t','y',' '),	HB_TAG('N','E','P',' ')},	/* Dotyali -> Nepali */
+/*{HB_TAG('d','u','j',' '),	HB_TAG('D','U','J',' ')},*/	/* Dhuwal (retired code) */
+  {HB_TAG('d','u','n',' '),	HB_TAG_NONE	       },	/* Dusun Deyah != Dungan */
+  {HB_TAG('d','u','p',' '),	HB_TAG('M','L','Y',' ')},	/* Duano -> Malay */
+  {HB_TAG('d','w','k',' '),	HB_TAG('K','U','I',' ')},	/* Dawik Kui -> Kui */
+  {HB_TAG('d','w','u',' '),	HB_TAG('D','U','J',' ')},	/* Dhuwal */
+  {HB_TAG('d','w','y',' '),	HB_TAG('D','U','J',' ')},	/* Dhuwaya -> Dhuwal */
+  {HB_TAG('d','y','u',' '),	HB_TAG('J','U','L',' ')},	/* Dyula -> Jula */
+  {HB_TAG('d','z','n',' '),	HB_TAG_NONE	       },	/* Dzando != Dzongkha */
+  {HB_TAG('e','c','r',' '),	HB_TAG_NONE	       },	/* Eteocretan != Eastern Cree */
+/*{HB_TAG('e','f','i',' '),	HB_TAG('E','F','I',' ')},*/	/* Efik */
+  {HB_TAG('e','k','k',' '),	HB_TAG('E','T','I',' ')},	/* Standard Estonian -> Estonian */
+  {HB_TAG('e','k','y',' '),	HB_TAG('K','R','N',' ')},	/* Eastern Kayah -> Karen */
+  {HB_TAG('e','m','k',' '),	HB_TAG('E','M','K',' ')},	/* Eastern Maninkakan */
+  {HB_TAG('e','m','k',' '),	HB_TAG('M','N','K',' ')},	/* Eastern Maninkakan -> Maninka */
+  {HB_TAG('e','m','y',' '),	HB_TAG('M','Y','N',' ')},	/* Epigraphic Mayan -> Mayan */
+  {HB_TAG('e','n','b',' '),	HB_TAG('K','A','L',' ')},	/* Markweeta -> Kalenjin */
+  {HB_TAG('e','n','f',' '),	HB_TAG('F','N','E',' ')},	/* Forest Enets */
+  {HB_TAG('e','n','h',' '),	HB_TAG('T','N','E',' ')},	/* Tundra Enets */
+  {HB_TAG('e','s','g',' '),	HB_TAG('G','O','N',' ')},	/* Aheri Gondi -> Gondi */
+  {HB_TAG('e','s','i',' '),	HB_TAG('I','P','K',' ')},	/* North Alaskan Inupiatun -> Inupiat */
+  {HB_TAG('e','s','k',' '),	HB_TAG('I','P','K',' ')},	/* Northwest Alaska Inupiatun -> Inupiat */
+/*{HB_TAG('e','s','u',' '),	HB_TAG('E','S','U',' ')},*/	/* Central Yupik */
+  {HB_TAG('e','t','o',' '),	HB_TAG('B','T','I',' ')},	/* Eton (Cameroon) -> Beti */
+  {HB_TAG('e','u','q',' '),	HB_TAG_NONE	       },	/* Basque [collection] != Basque */
+  {HB_TAG('e','v','e',' '),	HB_TAG('E','V','N',' ')},	/* Even */
+  {HB_TAG('e','v','n',' '),	HB_TAG('E','V','K',' ')},	/* Evenki */
+  {HB_TAG('e','w','o',' '),	HB_TAG('B','T','I',' ')},	/* Ewondo -> Beti */
+  {HB_TAG('e','y','o',' '),	HB_TAG('K','A','L',' ')},	/* Keiyo -> Kalenjin */
+  {HB_TAG('f','a','b',' '),	HB_TAG('C','P','P',' ')},	/* Fa d'Ambu -> Creoles */
+  {HB_TAG('f','a','n',' '),	HB_TAG('F','A','N','0')},	/* Fang (Equatorial Guinea) */
+  {HB_TAG('f','a','n',' '),	HB_TAG('B','T','I',' ')},	/* Fang (Equatorial Guinea) -> Beti */
+  {HB_TAG('f','a','r',' '),	HB_TAG_NONE	       },	/* Fataleka != Persian */
+  {HB_TAG('f','a','t',' '),	HB_TAG('F','A','T',' ')},	/* Fanti */
+  {HB_TAG('f','a','t',' '),	HB_TAG('A','K','A',' ')},	/* Fanti -> Akan */
+  {HB_TAG('f','b','l',' '),	HB_TAG('B','I','K',' ')},	/* West Albay Bikol -> Bikol */
+  {HB_TAG('f','f','m',' '),	HB_TAG('F','U','L',' ')},	/* Maasina Fulfulde -> Fulah */
+  {HB_TAG('f','i','l',' '),	HB_TAG('P','I','L',' ')},	/* Filipino */
+  {HB_TAG('f','l','m',' '),	HB_TAG('H','A','L',' ')},	/* Halam (Falam Chin) (retired code) */
+  {HB_TAG('f','l','m',' '),	HB_TAG('Q','I','N',' ')},	/* Falam Chin (retired code) -> Chin */
+  {HB_TAG('f','m','p',' '),	HB_TAG('F','M','P',' ')},	/* Fe’fe’ */
+  {HB_TAG('f','m','p',' '),	HB_TAG('B','M','L',' ')},	/* Fe'fe' -> Bamileke */
+  {HB_TAG('f','n','g',' '),	HB_TAG('C','P','P',' ')},	/* Fanagalo -> Creoles */
+/*{HB_TAG('f','o','n',' '),	HB_TAG('F','O','N',' ')},*/	/* Fon */
+  {HB_TAG('f','o','s',' '),	HB_TAG_NONE	       },	/* Siraya != Faroese */
+  {HB_TAG('f','p','e',' '),	HB_TAG('C','P','P',' ')},	/* Fernando Po Creole English -> Creoles */
+/*{HB_TAG('f','r','c',' '),	HB_TAG('F','R','C',' ')},*/	/* Cajun French */
+/*{HB_TAG('f','r','p',' '),	HB_TAG('F','R','P',' ')},*/	/* Arpitan */
+  {HB_TAG('f','u','b',' '),	HB_TAG('F','U','L',' ')},	/* Adamawa Fulfulde -> Fulah */
+  {HB_TAG('f','u','c',' '),	HB_TAG('F','U','L',' ')},	/* Pulaar -> Fulah */
+  {HB_TAG('f','u','e',' '),	HB_TAG('F','U','L',' ')},	/* Borgu Fulfulde -> Fulah */
+  {HB_TAG('f','u','f',' '),	HB_TAG('F','T','A',' ')},	/* Pular -> Futa */
+  {HB_TAG('f','u','f',' '),	HB_TAG('F','U','L',' ')},	/* Pular -> Fulah */
+  {HB_TAG('f','u','h',' '),	HB_TAG('F','U','L',' ')},	/* Western Niger Fulfulde -> Fulah */
+  {HB_TAG('f','u','i',' '),	HB_TAG('F','U','L',' ')},	/* Bagirmi Fulfulde -> Fulah */
+  {HB_TAG('f','u','q',' '),	HB_TAG('F','U','L',' ')},	/* Central-Eastern Niger Fulfulde -> Fulah */
+  {HB_TAG('f','u','r',' '),	HB_TAG('F','R','L',' ')},	/* Friulian */
+  {HB_TAG('f','u','v',' '),	HB_TAG('F','U','V',' ')},	/* Nigerian Fulfulde */
+  {HB_TAG('f','u','v',' '),	HB_TAG('F','U','L',' ')},	/* Nigerian Fulfulde -> Fulah */
+  {HB_TAG('g','a','a',' '),	HB_TAG('G','A','D',' ')},	/* Ga */
+  {HB_TAG('g','a','c',' '),	HB_TAG('C','P','P',' ')},	/* Mixed Great Andamanese -> Creoles */
+  {HB_TAG('g','a','d',' '),	HB_TAG_NONE	       },	/* Gaddang != Ga */
+  {HB_TAG('g','a','e',' '),	HB_TAG_NONE	       },	/* Guarequena != Scottish Gaelic (Gaelic) */
+/*{HB_TAG('g','a','g',' '),	HB_TAG('G','A','G',' ')},*/	/* Gagauz */
+  {HB_TAG('g','a','l',' '),	HB_TAG_NONE	       },	/* Galolen != Galician */
+  {HB_TAG('g','a','n',' '),	HB_TAG('Z','H','S',' ')},	/* Gan Chinese -> Chinese, Simplified */
+  {HB_TAG('g','a','r',' '),	HB_TAG_NONE	       },	/* Galeya != Garshuni */
+  {HB_TAG('g','a','w',' '),	HB_TAG_NONE	       },	/* Nobonob != Garhwali */
+  {HB_TAG('g','a','x',' '),	HB_TAG('O','R','O',' ')},	/* Borana-Arsi-Guji Oromo -> Oromo */
+  {HB_TAG('g','a','z',' '),	HB_TAG('O','R','O',' ')},	/* West Central Oromo -> Oromo */
+  {HB_TAG('g','b','m',' '),	HB_TAG('G','A','W',' ')},	/* Garhwali */
+  {HB_TAG('g','c','e',' '),	HB_TAG('A','T','H',' ')},	/* Galice -> Athapaskan */
+  {HB_TAG('g','c','f',' '),	HB_TAG('C','P','P',' ')},	/* Guadeloupean Creole French -> Creoles */
+  {HB_TAG('g','c','l',' '),	HB_TAG('C','P','P',' ')},	/* Grenadian Creole English -> Creoles */
+  {HB_TAG('g','c','r',' '),	HB_TAG('C','P','P',' ')},	/* Guianese Creole French -> Creoles */
+  {HB_TAG('g','d','a',' '),	HB_TAG('R','A','J',' ')},	/* Gade Lohar -> Rajasthani */
+/*{HB_TAG('g','e','z',' '),	HB_TAG('G','E','Z',' ')},*/	/* Geez */
+  {HB_TAG('g','g','o',' '),	HB_TAG('G','O','N',' ')},	/* Southern Gondi (retired code) -> Gondi */
+  {HB_TAG('g','h','a',' '),	HB_TAG('B','B','R',' ')},	/* Ghadamès -> Berber */
+  {HB_TAG('g','h','k',' '),	HB_TAG('K','R','N',' ')},	/* Geko Karen -> Karen */
+  {HB_TAG('g','h','o',' '),	HB_TAG('B','B','R',' ')},	/* Ghomara -> Berber */
+  {HB_TAG('g','i','b',' '),	HB_TAG('C','P','P',' ')},	/* Gibanawa -> Creoles */
+/*{HB_TAG('g','i','h',' '),	HB_TAG('G','I','H',' ')},*/	/* Githabul */
+  {HB_TAG('g','i','l',' '),	HB_TAG('G','I','L','0')},	/* Kiribati (Gilbertese) */
+  {HB_TAG('g','j','u',' '),	HB_TAG('R','A','J',' ')},	/* Gujari -> Rajasthani */
+  {HB_TAG('g','k','p',' '),	HB_TAG('G','K','P',' ')},	/* Guinea Kpelle -> Kpelle (Guinea) */
+  {HB_TAG('g','k','p',' '),	HB_TAG('K','P','L',' ')},	/* Guinea Kpelle -> Kpelle */
+  {HB_TAG('g','l','d',' '),	HB_TAG('N','A','N',' ')},	/* Nanai */
+/*{HB_TAG('g','l','k',' '),	HB_TAG('G','L','K',' ')},*/	/* Gilaki */
+  {HB_TAG('g','m','z',' '),	HB_TAG_NONE	       },	/* Mgbolizhia != Gumuz */
+  {HB_TAG('g','n','b',' '),	HB_TAG('Q','I','N',' ')},	/* Gangte -> Chin */
+/*{HB_TAG('g','n','n',' '),	HB_TAG('G','N','N',' ')},*/	/* Gumatj */
+  {HB_TAG('g','n','o',' '),	HB_TAG('G','O','N',' ')},	/* Northern Gondi -> Gondi */
+  {HB_TAG('g','n','w',' '),	HB_TAG('G','U','A',' ')},	/* Western Bolivian Guaraní -> Guarani */
+/*{HB_TAG('g','o','g',' '),	HB_TAG('G','O','G',' ')},*/	/* Gogo */
+  {HB_TAG('g','o','m',' '),	HB_TAG('K','O','K',' ')},	/* Goan Konkani -> Konkani */
+/*{HB_TAG('g','o','n',' '),	HB_TAG('G','O','N',' ')},*/	/* Gondi [macrolanguage] */
+  {HB_TAG('g','o','q',' '),	HB_TAG('C','P','P',' ')},	/* Gorap -> Creoles */
+  {HB_TAG('g','o','x',' '),	HB_TAG('B','A','D','0')},	/* Gobu -> Banda */
+  {HB_TAG('g','p','e',' '),	HB_TAG('C','P','P',' ')},	/* Ghanaian Pidgin English -> Creoles */
+  {HB_TAG('g','r','o',' '),	HB_TAG_NONE	       },	/* Groma != Garo */
+  {HB_TAG('g','r','r',' '),	HB_TAG('B','B','R',' ')},	/* Taznatit -> Berber */
+  {HB_TAG('g','r','t',' '),	HB_TAG('G','R','O',' ')},	/* Garo */
+  {HB_TAG('g','r','u',' '),	HB_TAG('S','O','G',' ')},	/* Kistane -> Sodo Gurage */
+  {HB_TAG('g','s','w',' '),	HB_TAG('A','L','S',' ')},	/* Alsatian */
+  {HB_TAG('g','u','a',' '),	HB_TAG_NONE	       },	/* Shiki != Guarani */
+/*{HB_TAG('g','u','c',' '),	HB_TAG('G','U','C',' ')},*/	/* Wayuu */
+/*{HB_TAG('g','u','f',' '),	HB_TAG('G','U','F',' ')},*/	/* Gupapuyngu */
+  {HB_TAG('g','u','g',' '),	HB_TAG('G','U','A',' ')},	/* Paraguayan Guaraní -> Guarani */
+  {HB_TAG('g','u','i',' '),	HB_TAG('G','U','A',' ')},	/* Eastern Bolivian Guaraní -> Guarani */
+  {HB_TAG('g','u','k',' '),	HB_TAG('G','M','Z',' ')},	/* Gumuz */
+  {HB_TAG('g','u','l',' '),	HB_TAG('C','P','P',' ')},	/* Sea Island Creole English -> Creoles */
+  {HB_TAG('g','u','n',' '),	HB_TAG('G','U','A',' ')},	/* Mbyá Guaraní -> Guarani */
+/*{HB_TAG('g','u','z',' '),	HB_TAG('G','U','Z',' ')},*/	/* Gusii */
+  {HB_TAG('g','w','i',' '),	HB_TAG('A','T','H',' ')},	/* Gwichʼin -> Athapaskan */
+  {HB_TAG('g','y','n',' '),	HB_TAG('C','P','P',' ')},	/* Guyanese Creole English -> Creoles */
+  {HB_TAG('h','a','a',' '),	HB_TAG('A','T','H',' ')},	/* Han -> Athapaskan */
+  {HB_TAG('h','a','e',' '),	HB_TAG('O','R','O',' ')},	/* Eastern Oromo -> Oromo */
+  {HB_TAG('h','a','i',' '),	HB_TAG('H','A','I','0')},	/* Haida [macrolanguage] */
+  {HB_TAG('h','a','k',' '),	HB_TAG('Z','H','S',' ')},	/* Hakka Chinese -> Chinese, Simplified */
+  {HB_TAG('h','a','l',' '),	HB_TAG_NONE	       },	/* Halang != Halam (Falam Chin) */
+  {HB_TAG('h','a','r',' '),	HB_TAG('H','R','I',' ')},	/* Harari */
+/*{HB_TAG('h','a','w',' '),	HB_TAG('H','A','W',' ')},*/	/* Hawaiian */
+  {HB_TAG('h','a','x',' '),	HB_TAG('H','A','I','0')},	/* Southern Haida -> Haida */
+/*{HB_TAG('h','a','y',' '),	HB_TAG('H','A','Y',' ')},*/	/* Haya */
+/*{HB_TAG('h','a','z',' '),	HB_TAG('H','A','Z',' ')},*/	/* Hazaragi */
+  {HB_TAG('h','b','n',' '),	HB_TAG_NONE	       },	/* Heiban != Hammer-Banna */
+  {HB_TAG('h','c','a',' '),	HB_TAG('C','P','P',' ')},	/* Andaman Creole Hindi -> Creoles */
+  {HB_TAG('h','d','n',' '),	HB_TAG('H','A','I','0')},	/* Northern Haida -> Haida */
+  {HB_TAG('h','e','a',' '),	HB_TAG('H','M','N',' ')},	/* Northern Qiandong Miao -> Hmong */
+/*{HB_TAG('h','e','i',' '),	HB_TAG('H','E','I',' ')},*/	/* Heiltsuk */
+/*{HB_TAG('h','i','l',' '),	HB_TAG('H','I','L',' ')},*/	/* Hiligaynon */
+  {HB_TAG('h','j','i',' '),	HB_TAG('M','L','Y',' ')},	/* Haji -> Malay */
+  {HB_TAG('h','l','t',' '),	HB_TAG('Q','I','N',' ')},	/* Matu Chin -> Chin */
+  {HB_TAG('h','m','a',' '),	HB_TAG('H','M','N',' ')},	/* Southern Mashan Hmong -> Hmong */
+  {HB_TAG('h','m','c',' '),	HB_TAG('H','M','N',' ')},	/* Central Huishui Hmong -> Hmong */
+  {HB_TAG('h','m','d',' '),	HB_TAG('H','M','D',' ')},	/* Large Flowery Miao -> A-Hmao */
+  {HB_TAG('h','m','d',' '),	HB_TAG('H','M','N',' ')},	/* Large Flowery Miao -> Hmong */
+  {HB_TAG('h','m','e',' '),	HB_TAG('H','M','N',' ')},	/* Eastern Huishui Hmong -> Hmong */
+  {HB_TAG('h','m','g',' '),	HB_TAG('H','M','N',' ')},	/* Southwestern Guiyang Hmong -> Hmong */
+  {HB_TAG('h','m','h',' '),	HB_TAG('H','M','N',' ')},	/* Southwestern Huishui Hmong -> Hmong */
+  {HB_TAG('h','m','i',' '),	HB_TAG('H','M','N',' ')},	/* Northern Huishui Hmong -> Hmong */
+  {HB_TAG('h','m','j',' '),	HB_TAG('H','M','N',' ')},	/* Ge -> Hmong */
+  {HB_TAG('h','m','l',' '),	HB_TAG('H','M','N',' ')},	/* Luopohe Hmong -> Hmong */
+  {HB_TAG('h','m','m',' '),	HB_TAG('H','M','N',' ')},	/* Central Mashan Hmong -> Hmong */
+/*{HB_TAG('h','m','n',' '),	HB_TAG('H','M','N',' ')},*/	/* Hmong [macrolanguage] */
+  {HB_TAG('h','m','p',' '),	HB_TAG('H','M','N',' ')},	/* Northern Mashan Hmong -> Hmong */
+  {HB_TAG('h','m','q',' '),	HB_TAG('H','M','N',' ')},	/* Eastern Qiandong Miao -> Hmong */
+  {HB_TAG('h','m','r',' '),	HB_TAG('Q','I','N',' ')},	/* Hmar -> Chin */
+  {HB_TAG('h','m','s',' '),	HB_TAG('H','M','N',' ')},	/* Southern Qiandong Miao -> Hmong */
+  {HB_TAG('h','m','w',' '),	HB_TAG('H','M','N',' ')},	/* Western Mashan Hmong -> Hmong */
+  {HB_TAG('h','m','y',' '),	HB_TAG('H','M','N',' ')},	/* Southern Guiyang Hmong -> Hmong */
+  {HB_TAG('h','m','z',' '),	HB_TAG('H','M','Z',' ')},	/* Hmong Shua -> Hmong Shuat */
+  {HB_TAG('h','m','z',' '),	HB_TAG('H','M','N',' ')},	/* Hmong Shua -> Hmong */
+/*{HB_TAG('h','n','d',' '),	HB_TAG('H','N','D',' ')},*/	/* Southern Hindko -> Hindko */
+  {HB_TAG('h','n','e',' '),	HB_TAG('C','H','H',' ')},	/* Chhattisgarhi -> Chattisgarhi */
+  {HB_TAG('h','n','j',' '),	HB_TAG('H','M','N',' ')},	/* Hmong Njua -> Hmong */
+  {HB_TAG('h','n','o',' '),	HB_TAG('H','N','D',' ')},	/* Northern Hindko -> Hindko */
+  {HB_TAG('h','o','c',' '),	HB_TAG('H','O',' ',' ')},	/* Ho */
+  {HB_TAG('h','o','i',' '),	HB_TAG('A','T','H',' ')},	/* Holikachuk -> Athapaskan */
+  {HB_TAG('h','o','j',' '),	HB_TAG('H','A','R',' ')},	/* Hadothi -> Harauti */
+  {HB_TAG('h','o','j',' '),	HB_TAG('R','A','J',' ')},	/* Hadothi -> Rajasthani */
+  {HB_TAG('h','r','a',' '),	HB_TAG('Q','I','N',' ')},	/* Hrangkhol -> Chin */
+  {HB_TAG('h','r','m',' '),	HB_TAG('H','M','N',' ')},	/* Horned Miao -> Hmong */
+  {HB_TAG('h','s','b',' '),	HB_TAG('U','S','B',' ')},	/* Upper Sorbian */
+  {HB_TAG('h','s','n',' '),	HB_TAG('Z','H','S',' ')},	/* Xiang Chinese -> Chinese, Simplified */
+  {HB_TAG('h','u','j',' '),	HB_TAG('H','M','N',' ')},	/* Northern Guiyang Hmong -> Hmong */
+  {HB_TAG('h','u','p',' '),	HB_TAG('A','T','H',' ')},	/* Hupa -> Athapaskan */
+  {HB_TAG('h','u','s',' '),	HB_TAG('M','Y','N',' ')},	/* Huastec -> Mayan */
+  {HB_TAG('h','w','c',' '),	HB_TAG('C','P','P',' ')},	/* Hawai'i Creole English -> Creoles */
+  {HB_TAG('h','y','w',' '),	HB_TAG('H','Y','E',' ')},	/* Western Armenian -> Armenian */
+/*{HB_TAG('i','b','a',' '),	HB_TAG('I','B','A',' ')},*/	/* Iban */
+/*{HB_TAG('i','b','b',' '),	HB_TAG('I','B','B',' ')},*/	/* Ibibio */
+  {HB_TAG('i','b','y',' '),	HB_TAG('I','J','O',' ')},	/* Ibani -> Ijo */
+  {HB_TAG('i','c','r',' '),	HB_TAG('C','P','P',' ')},	/* Islander Creole English -> Creoles */
+  {HB_TAG('i','d','a',' '),	HB_TAG('L','U','H',' ')},	/* Idakho-Isukha-Tiriki -> Luyia */
+  {HB_TAG('i','d','b',' '),	HB_TAG('C','P','P',' ')},	/* Indo-Portuguese -> Creoles */
+  {HB_TAG('i','g','b',' '),	HB_TAG('E','B','I',' ')},	/* Ebira */
+  {HB_TAG('i','h','b',' '),	HB_TAG('C','P','P',' ')},	/* Iha Based Pidgin -> Creoles */
+  {HB_TAG('i','j','c',' '),	HB_TAG('I','J','O',' ')},	/* Izon -> Ijo */
+  {HB_TAG('i','j','e',' '),	HB_TAG('I','J','O',' ')},	/* Biseni -> Ijo */
+  {HB_TAG('i','j','n',' '),	HB_TAG('I','J','O',' ')},	/* Kalabari -> Ijo */
+/*{HB_TAG('i','j','o',' '),	HB_TAG('I','J','O',' ')},*/	/* Ijo [collection] */
+  {HB_TAG('i','j','s',' '),	HB_TAG('I','J','O',' ')},	/* Southeast Ijo -> Ijo */
+  {HB_TAG('i','k','e',' '),	HB_TAG('I','N','U',' ')},	/* Eastern Canadian Inuktitut -> Inuktitut */
+  {HB_TAG('i','k','e',' '),	HB_TAG('I','N','U','K')},	/* Eastern Canadian Inuktitut -> Nunavik Inuktitut */
+  {HB_TAG('i','k','t',' '),	HB_TAG('I','N','U',' ')},	/* Inuinnaqtun -> Inuktitut */
+/*{HB_TAG('i','l','o',' '),	HB_TAG('I','L','O',' ')},*/	/* Iloko -> Ilokano */
+  {HB_TAG('i','n','g',' '),	HB_TAG('A','T','H',' ')},	/* Degexit'an -> Athapaskan */
+  {HB_TAG('i','n','h',' '),	HB_TAG('I','N','G',' ')},	/* Ingush */
+  {HB_TAG('i','r','i',' '),	HB_TAG_NONE	       },	/* Rigwe != Irish */
+/*{HB_TAG('i','r','u',' '),	HB_TAG('I','R','U',' ')},*/	/* Irula */
+  {HB_TAG('i','s','m',' '),	HB_TAG_NONE	       },	/* Masimasi != Inari Sami */
+  {HB_TAG('i','t','z',' '),	HB_TAG('M','Y','N',' ')},	/* Itzá -> Mayan */
+  {HB_TAG('i','x','l',' '),	HB_TAG('M','Y','N',' ')},	/* Ixil -> Mayan */
+  {HB_TAG('j','a','c',' '),	HB_TAG('M','Y','N',' ')},	/* Popti' -> Mayan */
+  {HB_TAG('j','a','k',' '),	HB_TAG('M','L','Y',' ')},	/* Jakun -> Malay */
+  {HB_TAG('j','a','m',' '),	HB_TAG('J','A','M',' ')},	/* Jamaican Creole English -> Jamaican Creole */
+  {HB_TAG('j','a','m',' '),	HB_TAG('C','P','P',' ')},	/* Jamaican Creole English -> Creoles */
+  {HB_TAG('j','a','n',' '),	HB_TAG_NONE	       },	/* Jandai != Japanese */
+  {HB_TAG('j','a','x',' '),	HB_TAG('M','L','Y',' ')},	/* Jambi Malay -> Malay */
+  {HB_TAG('j','b','e',' '),	HB_TAG('B','B','R',' ')},	/* Judeo-Berber -> Berber */
+  {HB_TAG('j','b','n',' '),	HB_TAG('B','B','R',' ')},	/* Nafusi -> Berber */
+/*{HB_TAG('j','b','o',' '),	HB_TAG('J','B','O',' ')},*/	/* Lojban */
+/*{HB_TAG('j','c','t',' '),	HB_TAG('J','C','T',' ')},*/	/* Krymchak */
+  {HB_TAG('j','g','o',' '),	HB_TAG('B','M','L',' ')},	/* Ngomba -> Bamileke */
+  {HB_TAG('j','i','i',' '),	HB_TAG_NONE	       },	/* Jiiddu != Yiddish */
+  {HB_TAG('j','k','m',' '),	HB_TAG('K','R','N',' ')},	/* Mobwa Karen -> Karen */
+  {HB_TAG('j','k','p',' '),	HB_TAG('K','R','N',' ')},	/* Paku Karen -> Karen */
+  {HB_TAG('j','u','d',' '),	HB_TAG_NONE	       },	/* Worodougou != Ladino */
+  {HB_TAG('j','u','l',' '),	HB_TAG_NONE	       },	/* Jirel != Jula */
+  {HB_TAG('j','v','d',' '),	HB_TAG('C','P','P',' ')},	/* Javindo -> Creoles */
+  {HB_TAG('k','a','a',' '),	HB_TAG('K','R','K',' ')},	/* Karakalpak */
+  {HB_TAG('k','a','b',' '),	HB_TAG('K','A','B','0')},	/* Kabyle */
+  {HB_TAG('k','a','b',' '),	HB_TAG('B','B','R',' ')},	/* Kabyle -> Berber */
+  {HB_TAG('k','a','c',' '),	HB_TAG_NONE	       },	/* Kachin != Kachchi */
+  {HB_TAG('k','a','m',' '),	HB_TAG('K','M','B',' ')},	/* Kamba (Kenya) */
+  {HB_TAG('k','a','r',' '),	HB_TAG('K','R','N',' ')},	/* Karen [collection] */
+/*{HB_TAG('k','a','w',' '),	HB_TAG('K','A','W',' ')},*/	/* Kawi (Old Javanese) */
+  {HB_TAG('k','b','d',' '),	HB_TAG('K','A','B',' ')},	/* Kabardian */
+  {HB_TAG('k','b','y',' '),	HB_TAG('K','N','R',' ')},	/* Manga Kanuri -> Kanuri */
+  {HB_TAG('k','c','a',' '),	HB_TAG('K','H','K',' ')},	/* Khanty -> Khanty-Kazim */
+  {HB_TAG('k','c','a',' '),	HB_TAG('K','H','S',' ')},	/* Khanty -> Khanty-Shurishkar */
+  {HB_TAG('k','c','a',' '),	HB_TAG('K','H','V',' ')},	/* Khanty -> Khanty-Vakhi */
+  {HB_TAG('k','c','n',' '),	HB_TAG('C','P','P',' ')},	/* Nubi -> Creoles */
+/*{HB_TAG('k','d','e',' '),	HB_TAG('K','D','E',' ')},*/	/* Makonde */
+  {HB_TAG('k','d','r',' '),	HB_TAG('K','R','M',' ')},	/* Karaim */
+  {HB_TAG('k','d','t',' '),	HB_TAG('K','U','Y',' ')},	/* Kuy */
+  {HB_TAG('k','e','a',' '),	HB_TAG('K','E','A',' ')},	/* Kabuverdianu (Crioulo) */
+  {HB_TAG('k','e','a',' '),	HB_TAG('C','P','P',' ')},	/* Kabuverdianu -> Creoles */
+  {HB_TAG('k','e','b',' '),	HB_TAG_NONE	       },	/* Kélé != Kebena */
+  {HB_TAG('k','e','k',' '),	HB_TAG('K','E','K',' ')},	/* Kekchi */
+  {HB_TAG('k','e','k',' '),	HB_TAG('M','Y','N',' ')},	/* Kekchí -> Mayan */
+  {HB_TAG('k','e','x',' '),	HB_TAG('K','K','N',' ')},	/* Kukna -> Kokni */
+  {HB_TAG('k','f','a',' '),	HB_TAG('K','O','D',' ')},	/* Kodava -> Kodagu */
+  {HB_TAG('k','f','r',' '),	HB_TAG('K','A','C',' ')},	/* Kachhi -> Kachchi */
+  {HB_TAG('k','f','x',' '),	HB_TAG('K','U','L',' ')},	/* Kullu Pahari -> Kulvi */
+  {HB_TAG('k','f','y',' '),	HB_TAG('K','M','N',' ')},	/* Kumaoni */
+  {HB_TAG('k','g','e',' '),	HB_TAG_NONE	       },	/* Komering != Khutsuri Georgian */
+  {HB_TAG('k','h','a',' '),	HB_TAG('K','S','I',' ')},	/* Khasi */
+  {HB_TAG('k','h','b',' '),	HB_TAG('X','B','D',' ')},	/* Lü */
+  {HB_TAG('k','h','k',' '),	HB_TAG('M','N','G',' ')},	/* Halh Mongolian -> Mongolian */
+  {HB_TAG('k','h','n',' '),	HB_TAG_NONE	       },	/* Khandesi != Khamti Shan (Microsoft fonts) */
+  {HB_TAG('k','h','s',' '),	HB_TAG_NONE	       },	/* Kasua != Khanty-Shurishkar */
+  {HB_TAG('k','h','t',' '),	HB_TAG('K','H','T',' ')},	/* Khamti -> Khamti Shan */
+  {HB_TAG('k','h','t',' '),	HB_TAG('K','H','N',' ')},	/* Khamti -> Khamti Shan (Microsoft fonts) */
+  {HB_TAG('k','h','v',' '),	HB_TAG_NONE	       },	/* Khvarshi != Khanty-Vakhi */
+/*{HB_TAG('k','h','w',' '),	HB_TAG('K','H','W',' ')},*/	/* Khowar */
+  {HB_TAG('k','i','s',' '),	HB_TAG_NONE	       },	/* Kis != Kisii */
+  {HB_TAG('k','i','u',' '),	HB_TAG('K','I','U',' ')},	/* Kirmanjki */
+  {HB_TAG('k','i','u',' '),	HB_TAG('Z','Z','A',' ')},	/* Kirmanjki -> Zazaki */
+  {HB_TAG('k','j','b',' '),	HB_TAG('M','Y','N',' ')},	/* Q'anjob'al -> Mayan */
+/*{HB_TAG('k','j','d',' '),	HB_TAG('K','J','D',' ')},*/	/* Southern Kiwai */
+  {HB_TAG('k','j','h',' '),	HB_TAG('K','H','A',' ')},	/* Khakas -> Khakass */
+  {HB_TAG('k','j','p',' '),	HB_TAG('K','J','P',' ')},	/* Pwo Eastern Karen -> Eastern Pwo Karen */
+  {HB_TAG('k','j','p',' '),	HB_TAG('K','R','N',' ')},	/* Pwo Eastern Karen -> Karen */
+  {HB_TAG('k','j','t',' '),	HB_TAG('K','R','N',' ')},	/* Phrae Pwo Karen -> Karen */
+/*{HB_TAG('k','j','z',' '),	HB_TAG('K','J','Z',' ')},*/	/* Bumthangkha */
+  {HB_TAG('k','k','n',' '),	HB_TAG_NONE	       },	/* Kon Keu != Kokni */
+  {HB_TAG('k','k','z',' '),	HB_TAG('A','T','H',' ')},	/* Kaska -> Athapaskan */
+  {HB_TAG('k','l','m',' '),	HB_TAG_NONE	       },	/* Migum != Kalmyk */
+  {HB_TAG('k','l','n',' '),	HB_TAG('K','A','L',' ')},	/* Kalenjin [macrolanguage] */
+  {HB_TAG('k','m','b',' '),	HB_TAG('M','B','N',' ')},	/* Kimbundu -> Mbundu */
+  {HB_TAG('k','m','n',' '),	HB_TAG_NONE	       },	/* Awtuw != Kumaoni */
+  {HB_TAG('k','m','o',' '),	HB_TAG_NONE	       },	/* Kwoma != Komo */
+  {HB_TAG('k','m','r',' '),	HB_TAG('K','U','R',' ')},	/* Northern Kurdish -> Kurdish */
+  {HB_TAG('k','m','s',' '),	HB_TAG_NONE	       },	/* Kamasau != Komso */
+  {HB_TAG('k','m','v',' '),	HB_TAG('C','P','P',' ')},	/* Karipúna Creole French -> Creoles */
+  {HB_TAG('k','m','w',' '),	HB_TAG('K','M','O',' ')},	/* Komo (Democratic Republic of Congo) */
+/*{HB_TAG('k','m','z',' '),	HB_TAG('K','M','Z',' ')},*/	/* Khorasani Turkish -> Khorasani Turkic */
+  {HB_TAG('k','n','c',' '),	HB_TAG('K','N','R',' ')},	/* Central Kanuri -> Kanuri */
+  {HB_TAG('k','n','g',' '),	HB_TAG('K','O','N','0')},	/* Koongo -> Kongo */
+  {HB_TAG('k','n','j',' '),	HB_TAG('M','Y','N',' ')},	/* Western Kanjobal -> Mayan */
+  {HB_TAG('k','n','n',' '),	HB_TAG('K','O','K',' ')},	/* Konkani */
+  {HB_TAG('k','n','r',' '),	HB_TAG_NONE	       },	/* Kaningra != Kanuri */
+  {HB_TAG('k','o','d',' '),	HB_TAG_NONE	       },	/* Kodi != Kodagu */
+  {HB_TAG('k','o','h',' '),	HB_TAG_NONE	       },	/* Koyo != Korean Old Hangul */
+  {HB_TAG('k','o','i',' '),	HB_TAG('K','O','P',' ')},	/* Komi-Permyak */
+  {HB_TAG('k','o','i',' '),	HB_TAG('K','O','M',' ')},	/* Komi-Permyak -> Komi */
+/*{HB_TAG('k','o','k',' '),	HB_TAG('K','O','K',' ')},*/	/* Konkani [macrolanguage] */
+  {HB_TAG('k','o','p',' '),	HB_TAG_NONE	       },	/* Waube != Komi-Permyak */
+/*{HB_TAG('k','o','s',' '),	HB_TAG('K','O','S',' ')},*/	/* Kosraean */
+  {HB_TAG('k','o','y',' '),	HB_TAG('A','T','H',' ')},	/* Koyukon -> Athapaskan */
+  {HB_TAG('k','o','z',' '),	HB_TAG_NONE	       },	/* Korak != Komi-Zyrian */
+  {HB_TAG('k','p','e',' '),	HB_TAG('K','P','L',' ')},	/* Kpelle [macrolanguage] */
+  {HB_TAG('k','p','l',' '),	HB_TAG_NONE	       },	/* Kpala != Kpelle */
+  {HB_TAG('k','p','p',' '),	HB_TAG('K','R','N',' ')},	/* Paku Karen (retired code) -> Karen */
+  {HB_TAG('k','p','v',' '),	HB_TAG('K','O','Z',' ')},	/* Komi-Zyrian */
+  {HB_TAG('k','p','v',' '),	HB_TAG('K','O','M',' ')},	/* Komi-Zyrian -> Komi */
+  {HB_TAG('k','p','y',' '),	HB_TAG('K','Y','K',' ')},	/* Koryak */
+  {HB_TAG('k','q','s',' '),	HB_TAG('K','I','S',' ')},	/* Northern Kissi -> Kisii */
+  {HB_TAG('k','q','y',' '),	HB_TAG('K','R','T',' ')},	/* Koorete */
+  {HB_TAG('k','r','c',' '),	HB_TAG('K','A','R',' ')},	/* Karachay-Balkar -> Karachay */
+  {HB_TAG('k','r','c',' '),	HB_TAG('B','A','L',' ')},	/* Karachay-Balkar -> Balkar */
+  {HB_TAG('k','r','i',' '),	HB_TAG('K','R','I',' ')},	/* Krio */
+  {HB_TAG('k','r','i',' '),	HB_TAG('C','P','P',' ')},	/* Krio -> Creoles */
+  {HB_TAG('k','r','k',' '),	HB_TAG_NONE	       },	/* Kerek != Karakalpak */
+/*{HB_TAG('k','r','l',' '),	HB_TAG('K','R','L',' ')},*/	/* Karelian */
+  {HB_TAG('k','r','m',' '),	HB_TAG_NONE	       },	/* Krim (retired code) != Karaim */
+  {HB_TAG('k','r','n',' '),	HB_TAG_NONE	       },	/* Sapo != Karen */
+  {HB_TAG('k','r','t',' '),	HB_TAG('K','N','R',' ')},	/* Tumari Kanuri -> Kanuri */
+  {HB_TAG('k','r','u',' '),	HB_TAG('K','U','U',' ')},	/* Kurukh */
+  {HB_TAG('k','s','h',' '),	HB_TAG('K','S','H','0')},	/* Kölsch -> Ripuarian */
+  {HB_TAG('k','s','i',' '),	HB_TAG_NONE	       },	/* Krisa != Khasi */
+  {HB_TAG('k','s','m',' '),	HB_TAG_NONE	       },	/* Kumba != Kildin Sami */
+  {HB_TAG('k','s','s',' '),	HB_TAG('K','I','S',' ')},	/* Southern Kisi -> Kisii */
+  {HB_TAG('k','s','w',' '),	HB_TAG('K','S','W',' ')},	/* S’gaw Karen */
+  {HB_TAG('k','s','w',' '),	HB_TAG('K','R','N',' ')},	/* S'gaw Karen -> Karen */
+  {HB_TAG('k','t','b',' '),	HB_TAG('K','E','B',' ')},	/* Kambaata -> Kebena */
+  {HB_TAG('k','t','u',' '),	HB_TAG('K','O','N',' ')},	/* Kituba (Democratic Republic of Congo) -> Kikongo */
+  {HB_TAG('k','t','w',' '),	HB_TAG('A','T','H',' ')},	/* Kato -> Athapaskan */
+  {HB_TAG('k','u','i',' '),	HB_TAG_NONE	       },	/* Kuikúro-Kalapálo != Kui */
+  {HB_TAG('k','u','l',' '),	HB_TAG_NONE	       },	/* Kulere != Kulvi */
+/*{HB_TAG('k','u','m',' '),	HB_TAG('K','U','M',' ')},*/	/* Kumyk */
+  {HB_TAG('k','u','u',' '),	HB_TAG('A','T','H',' ')},	/* Upper Kuskokwim -> Athapaskan */
+  {HB_TAG('k','u','w',' '),	HB_TAG('B','A','D','0')},	/* Kpagua -> Banda */
+  {HB_TAG('k','u','y',' '),	HB_TAG_NONE	       },	/* Kuuku-Ya'u != Kuy */
+  {HB_TAG('k','v','b',' '),	HB_TAG('M','L','Y',' ')},	/* Kubu -> Malay */
+  {HB_TAG('k','v','l',' '),	HB_TAG('K','R','N',' ')},	/* Kayaw -> Karen */
+  {HB_TAG('k','v','q',' '),	HB_TAG('K','R','N',' ')},	/* Geba Karen -> Karen */
+  {HB_TAG('k','v','r',' '),	HB_TAG('M','L','Y',' ')},	/* Kerinci -> Malay */
+  {HB_TAG('k','v','t',' '),	HB_TAG('K','R','N',' ')},	/* Lahta Karen -> Karen */
+  {HB_TAG('k','v','u',' '),	HB_TAG('K','R','N',' ')},	/* Yinbaw Karen -> Karen */
+  {HB_TAG('k','v','y',' '),	HB_TAG('K','R','N',' ')},	/* Yintale Karen -> Karen */
+/*{HB_TAG('k','w','k',' '),	HB_TAG('K','W','K',' ')},*/	/* Kwakiutl -> Kwakʼwala */
+  {HB_TAG('k','w','w',' '),	HB_TAG('C','P','P',' ')},	/* Kwinti -> Creoles */
+  {HB_TAG('k','w','y',' '),	HB_TAG('K','O','N','0')},	/* San Salvador Kongo -> Kongo */
+  {HB_TAG('k','x','c',' '),	HB_TAG('K','M','S',' ')},	/* Konso -> Komso */
+  {HB_TAG('k','x','d',' '),	HB_TAG('M','L','Y',' ')},	/* Brunei -> Malay */
+  {HB_TAG('k','x','f',' '),	HB_TAG('K','R','N',' ')},	/* Manumanaw Karen -> Karen */
+  {HB_TAG('k','x','k',' '),	HB_TAG('K','R','N',' ')},	/* Zayein Karen -> Karen */
+  {HB_TAG('k','x','l',' '),	HB_TAG('K','U','U',' ')},	/* Nepali Kurux (retired code) -> Kurukh */
+  {HB_TAG('k','x','u',' '),	HB_TAG('K','U','I',' ')},	/* Kui (India) (retired code) */
+  {HB_TAG('k','y','k',' '),	HB_TAG_NONE	       },	/* Kamayo != Koryak */
+  {HB_TAG('k','y','u',' '),	HB_TAG('K','Y','U',' ')},	/* Western Kayah */
+  {HB_TAG('k','y','u',' '),	HB_TAG('K','R','N',' ')},	/* Western Kayah -> Karen */
+  {HB_TAG('l','a','c',' '),	HB_TAG('M','Y','N',' ')},	/* Lacandon -> Mayan */
+  {HB_TAG('l','a','d',' '),	HB_TAG('J','U','D',' ')},	/* Ladino */
+  {HB_TAG('l','a','h',' '),	HB_TAG_NONE	       },	/* Lahnda [macrolanguage] != Lahuli */
+  {HB_TAG('l','a','k',' '),	HB_TAG_NONE	       },	/* Laka (Nigeria) (retired code) != Lak */
+  {HB_TAG('l','a','m',' '),	HB_TAG_NONE	       },	/* Lamba != Lambani */
+  {HB_TAG('l','a','z',' '),	HB_TAG_NONE	       },	/* Aribwatsa != Laz */
+  {HB_TAG('l','b','e',' '),	HB_TAG('L','A','K',' ')},	/* Lak */
+  {HB_TAG('l','b','j',' '),	HB_TAG('L','D','K',' ')},	/* Ladakhi */
+  {HB_TAG('l','b','l',' '),	HB_TAG('B','I','K',' ')},	/* Libon Bikol -> Bikol */
+  {HB_TAG('l','c','e',' '),	HB_TAG('M','L','Y',' ')},	/* Loncong -> Malay */
+  {HB_TAG('l','c','f',' '),	HB_TAG('M','L','Y',' ')},	/* Lubu -> Malay */
+  {HB_TAG('l','d','i',' '),	HB_TAG('K','O','N','0')},	/* Laari -> Kongo */
+  {HB_TAG('l','d','k',' '),	HB_TAG_NONE	       },	/* Leelau != Ladakhi */
+/*{HB_TAG('l','e','f',' '),	HB_TAG('L','E','F',' ')},*/	/* Lelemi */
+/*{HB_TAG('l','e','z',' '),	HB_TAG('L','E','Z',' ')},*/	/* Lezghian -> Lezgi */
+  {HB_TAG('l','i','f',' '),	HB_TAG('L','M','B',' ')},	/* Limbu */
+/*{HB_TAG('l','i','j',' '),	HB_TAG('L','I','J',' ')},*/	/* Ligurian */
+  {HB_TAG('l','i','r',' '),	HB_TAG('C','P','P',' ')},	/* Liberian English -> Creoles */
+/*{HB_TAG('l','i','s',' '),	HB_TAG('L','I','S',' ')},*/	/* Lisu */
+  {HB_TAG('l','i','w',' '),	HB_TAG('M','L','Y',' ')},	/* Col -> Malay */
+  {HB_TAG('l','i','y',' '),	HB_TAG('B','A','D','0')},	/* Banda-Bambari -> Banda */
+/*{HB_TAG('l','j','p',' '),	HB_TAG('L','J','P',' ')},*/	/* Lampung Api -> Lampung */
+  {HB_TAG('l','k','b',' '),	HB_TAG('L','U','H',' ')},	/* Kabras -> Luyia */
+/*{HB_TAG('l','k','i',' '),	HB_TAG('L','K','I',' ')},*/	/* Laki */
+  {HB_TAG('l','k','o',' '),	HB_TAG('L','U','H',' ')},	/* Khayo -> Luyia */
+  {HB_TAG('l','k','s',' '),	HB_TAG('L','U','H',' ')},	/* Kisa -> Luyia */
+  {HB_TAG('l','l','d',' '),	HB_TAG('L','A','D',' ')},	/* Ladin */
+  {HB_TAG('l','m','a',' '),	HB_TAG_NONE	       },	/* East Limba != Low Mari */
+  {HB_TAG('l','m','b',' '),	HB_TAG_NONE	       },	/* Merei != Limbu */
+  {HB_TAG('l','m','n',' '),	HB_TAG('L','A','M',' ')},	/* Lambadi -> Lambani */
+/*{HB_TAG('l','m','o',' '),	HB_TAG('L','M','O',' ')},*/	/* Lombard */
+  {HB_TAG('l','m','w',' '),	HB_TAG_NONE	       },	/* Lake Miwok != Lomwe */
+  {HB_TAG('l','n','a',' '),	HB_TAG('B','A','D','0')},	/* Langbashe -> Banda */
+  {HB_TAG('l','n','l',' '),	HB_TAG('B','A','D','0')},	/* South Central Banda -> Banda */
+/*{HB_TAG('l','o','m',' '),	HB_TAG('L','O','M',' ')},*/	/* Loma (Liberia) */
+  {HB_TAG('l','o','u',' '),	HB_TAG('C','P','P',' ')},	/* Louisiana Creole -> Creoles */
+/*{HB_TAG('l','p','o',' '),	HB_TAG('L','P','O',' ')},*/	/* Lipo */
+/*{HB_TAG('l','r','c',' '),	HB_TAG('L','R','C',' ')},*/	/* Northern Luri -> Luri */
+  {HB_TAG('l','r','i',' '),	HB_TAG('L','U','H',' ')},	/* Marachi -> Luyia */
+  {HB_TAG('l','r','m',' '),	HB_TAG('L','U','H',' ')},	/* Marama -> Luyia */
+  {HB_TAG('l','r','t',' '),	HB_TAG('C','P','P',' ')},	/* Larantuka Malay -> Creoles */
+  {HB_TAG('l','s','b',' '),	HB_TAG_NONE	       },	/* Burundian Sign Language != Lower Sorbian */
+  {HB_TAG('l','s','m',' '),	HB_TAG('L','U','H',' ')},	/* Saamia -> Luyia */
+  {HB_TAG('l','t','g',' '),	HB_TAG('L','V','I',' ')},	/* Latgalian -> Latvian */
+  {HB_TAG('l','t','h',' '),	HB_TAG_NONE	       },	/* Thur != Lithuanian */
+  {HB_TAG('l','t','o',' '),	HB_TAG('L','U','H',' ')},	/* Tsotso -> Luyia */
+  {HB_TAG('l','t','s',' '),	HB_TAG('L','U','H',' ')},	/* Tachoni -> Luyia */
+/*{HB_TAG('l','u','a',' '),	HB_TAG('L','U','A',' ')},*/	/* Luba-Lulua */
+/*{HB_TAG('l','u','o',' '),	HB_TAG('L','U','O',' ')},*/	/* Luo (Kenya and Tanzania) */
+  {HB_TAG('l','u','s',' '),	HB_TAG('M','I','Z',' ')},	/* Lushai -> Mizo */
+  {HB_TAG('l','u','s',' '),	HB_TAG('Q','I','N',' ')},	/* Lushai -> Chin */
+  {HB_TAG('l','u','y',' '),	HB_TAG('L','U','H',' ')},	/* Luyia [macrolanguage] */
+  {HB_TAG('l','u','z',' '),	HB_TAG('L','R','C',' ')},	/* Southern Luri -> Luri */
+  {HB_TAG('l','v','i',' '),	HB_TAG_NONE	       },	/* Lavi != Latvian */
+  {HB_TAG('l','v','s',' '),	HB_TAG('L','V','I',' ')},	/* Standard Latvian -> Latvian */
+  {HB_TAG('l','w','g',' '),	HB_TAG('L','U','H',' ')},	/* Wanga -> Luyia */
+  {HB_TAG('l','z','h',' '),	HB_TAG('Z','H','T',' ')},	/* Literary Chinese -> Chinese, Traditional */
+  {HB_TAG('l','z','z',' '),	HB_TAG('L','A','Z',' ')},	/* Laz */
+/*{HB_TAG('m','a','d',' '),	HB_TAG('M','A','D',' ')},*/	/* Madurese -> Madura */
+/*{HB_TAG('m','a','g',' '),	HB_TAG('M','A','G',' ')},*/	/* Magahi */
+  {HB_TAG('m','a','i',' '),	HB_TAG('M','T','H',' ')},	/* Maithili */
+  {HB_TAG('m','a','j',' '),	HB_TAG_NONE	       },	/* Jalapa De Díaz Mazatec != Majang */
+  {HB_TAG('m','a','k',' '),	HB_TAG('M','K','R',' ')},	/* Makasar */
+  {HB_TAG('m','a','m',' '),	HB_TAG('M','A','M',' ')},	/* Mam */
+  {HB_TAG('m','a','m',' '),	HB_TAG('M','Y','N',' ')},	/* Mam -> Mayan */
+  {HB_TAG('m','a','n',' '),	HB_TAG('M','N','K',' ')},	/* Mandingo [macrolanguage] -> Maninka */
+  {HB_TAG('m','a','p',' '),	HB_TAG_NONE	       },	/* Austronesian [collection] != Mapudungun */
+  {HB_TAG('m','a','w',' '),	HB_TAG_NONE	       },	/* Mampruli != Marwari */
+  {HB_TAG('m','a','x',' '),	HB_TAG('M','L','Y',' ')},	/* North Moluccan Malay -> Malay */
+  {HB_TAG('m','a','x',' '),	HB_TAG('C','P','P',' ')},	/* North Moluccan Malay -> Creoles */
+  {HB_TAG('m','b','f',' '),	HB_TAG('C','P','P',' ')},	/* Baba Malay -> Creoles */
+  {HB_TAG('m','b','n',' '),	HB_TAG_NONE	       },	/* Macaguán != Mbundu */
+/*{HB_TAG('m','b','o',' '),	HB_TAG('M','B','O',' ')},*/	/* Mbo (Cameroon) */
+  {HB_TAG('m','c','h',' '),	HB_TAG_NONE	       },	/* Maquiritari != Manchu */
+  {HB_TAG('m','c','m',' '),	HB_TAG('C','P','P',' ')},	/* Malaccan Creole Portuguese -> Creoles */
+  {HB_TAG('m','c','r',' '),	HB_TAG_NONE	       },	/* Menya != Moose Cree */
+  {HB_TAG('m','c','t',' '),	HB_TAG('B','T','I',' ')},	/* Mengisa -> Beti */
+  {HB_TAG('m','d','e',' '),	HB_TAG_NONE	       },	/* Maba (Chad) != Mende */
+  {HB_TAG('m','d','f',' '),	HB_TAG('M','O','K',' ')},	/* Moksha */
+/*{HB_TAG('m','d','r',' '),	HB_TAG('M','D','R',' ')},*/	/* Mandar */
+  {HB_TAG('m','d','y',' '),	HB_TAG('M','L','E',' ')},	/* Male (Ethiopia) */
+  {HB_TAG('m','e','n',' '),	HB_TAG('M','D','E',' ')},	/* Mende (Sierra Leone) */
+  {HB_TAG('m','e','o',' '),	HB_TAG('M','L','Y',' ')},	/* Kedah Malay -> Malay */
+/*{HB_TAG('m','e','r',' '),	HB_TAG('M','E','R',' ')},*/	/* Meru */
+  {HB_TAG('m','f','a',' '),	HB_TAG('M','F','A',' ')},	/* Pattani Malay */
+  {HB_TAG('m','f','a',' '),	HB_TAG('M','L','Y',' ')},	/* Pattani Malay -> Malay */
+  {HB_TAG('m','f','b',' '),	HB_TAG('M','L','Y',' ')},	/* Bangka -> Malay */
+  {HB_TAG('m','f','e',' '),	HB_TAG('M','F','E',' ')},	/* Morisyen */
+  {HB_TAG('m','f','e',' '),	HB_TAG('C','P','P',' ')},	/* Morisyen -> Creoles */
+  {HB_TAG('m','f','p',' '),	HB_TAG('C','P','P',' ')},	/* Makassar Malay -> Creoles */
+  {HB_TAG('m','h','c',' '),	HB_TAG('M','Y','N',' ')},	/* Mocho -> Mayan */
+  {HB_TAG('m','h','r',' '),	HB_TAG('L','M','A',' ')},	/* Eastern Mari -> Low Mari */
+  {HB_TAG('m','h','v',' '),	HB_TAG('A','R','K',' ')},	/* Arakanese (retired code) -> Rakhine */
+  {HB_TAG('m','i','n',' '),	HB_TAG('M','I','N',' ')},	/* Minangkabau */
+  {HB_TAG('m','i','n',' '),	HB_TAG('M','L','Y',' ')},	/* Minangkabau -> Malay */
+  {HB_TAG('m','i','z',' '),	HB_TAG_NONE	       },	/* Coatzospan Mixtec != Mizo */
+  {HB_TAG('m','k','n',' '),	HB_TAG('C','P','P',' ')},	/* Kupang Malay -> Creoles */
+  {HB_TAG('m','k','r',' '),	HB_TAG_NONE	       },	/* Malas != Makasar */
+  {HB_TAG('m','k','u',' '),	HB_TAG('M','N','K',' ')},	/* Konyanka Maninka -> Maninka */
+/*{HB_TAG('m','k','w',' '),	HB_TAG('M','K','W',' ')},*/	/* Kituba (Congo) */
+  {HB_TAG('m','l','e',' '),	HB_TAG_NONE	       },	/* Manambu != Male */
+  {HB_TAG('m','l','n',' '),	HB_TAG_NONE	       },	/* Malango != Malinke */
+  {HB_TAG('m','l','q',' '),	HB_TAG('M','L','N',' ')},	/* Western Maninkakan -> Malinke */
+  {HB_TAG('m','l','q',' '),	HB_TAG('M','N','K',' ')},	/* Western Maninkakan -> Maninka */
+  {HB_TAG('m','l','r',' '),	HB_TAG_NONE	       },	/* Vame != Malayalam Reformed */
+  {HB_TAG('m','m','r',' '),	HB_TAG('H','M','N',' ')},	/* Western Xiangxi Miao -> Hmong */
+  {HB_TAG('m','n','c',' '),	HB_TAG('M','C','H',' ')},	/* Manchu */
+  {HB_TAG('m','n','d',' '),	HB_TAG_NONE	       },	/* Mondé != Mandinka */
+  {HB_TAG('m','n','g',' '),	HB_TAG_NONE	       },	/* Eastern Mnong != Mongolian */
+  {HB_TAG('m','n','h',' '),	HB_TAG('B','A','D','0')},	/* Mono (Democratic Republic of Congo) -> Banda */
+/*{HB_TAG('m','n','i',' '),	HB_TAG('M','N','I',' ')},*/	/* Manipuri */
+  {HB_TAG('m','n','k',' '),	HB_TAG('M','N','D',' ')},	/* Mandinka */
+  {HB_TAG('m','n','k',' '),	HB_TAG('M','N','K',' ')},	/* Mandinka -> Maninka */
+  {HB_TAG('m','n','p',' '),	HB_TAG('Z','H','S',' ')},	/* Min Bei Chinese -> Chinese, Simplified */
+  {HB_TAG('m','n','s',' '),	HB_TAG('M','A','N',' ')},	/* Mansi */
+  {HB_TAG('m','n','w',' '),	HB_TAG('M','O','N',' ')},	/* Mon */
+  {HB_TAG('m','n','w',' '),	HB_TAG('M','O','N','T')},	/* Mon -> Thailand Mon */
+  {HB_TAG('m','n','x',' '),	HB_TAG_NONE	       },	/* Manikion != Manx */
+  {HB_TAG('m','o','d',' '),	HB_TAG('C','P','P',' ')},	/* Mobilian -> Creoles */
+/*{HB_TAG('m','o','h',' '),	HB_TAG('M','O','H',' ')},*/	/* Mohawk */
+  {HB_TAG('m','o','k',' '),	HB_TAG_NONE	       },	/* Morori != Moksha */
+  {HB_TAG('m','o','p',' '),	HB_TAG('M','Y','N',' ')},	/* Mopán Maya -> Mayan */
+  {HB_TAG('m','o','r',' '),	HB_TAG_NONE	       },	/* Moro != Moroccan */
+/*{HB_TAG('m','o','s',' '),	HB_TAG('M','O','S',' ')},*/	/* Mossi */
+  {HB_TAG('m','p','e',' '),	HB_TAG('M','A','J',' ')},	/* Majang */
+  {HB_TAG('m','q','g',' '),	HB_TAG('M','L','Y',' ')},	/* Kota Bangun Kutai Malay -> Malay */
+  {HB_TAG('m','r','h',' '),	HB_TAG('Q','I','N',' ')},	/* Mara Chin -> Chin */
+  {HB_TAG('m','r','j',' '),	HB_TAG('H','M','A',' ')},	/* Western Mari -> High Mari */
+  {HB_TAG('m','s','c',' '),	HB_TAG('M','N','K',' ')},	/* Sankaran Maninka -> Maninka */
+  {HB_TAG('m','s','h',' '),	HB_TAG('M','L','G',' ')},	/* Masikoro Malagasy -> Malagasy */
+  {HB_TAG('m','s','i',' '),	HB_TAG('M','L','Y',' ')},	/* Sabah Malay -> Malay */
+  {HB_TAG('m','s','i',' '),	HB_TAG('C','P','P',' ')},	/* Sabah Malay -> Creoles */
+  {HB_TAG('m','t','h',' '),	HB_TAG_NONE	       },	/* Munggui != Maithili */
+  {HB_TAG('m','t','r',' '),	HB_TAG('M','A','W',' ')},	/* Mewari -> Marwari */
+  {HB_TAG('m','t','s',' '),	HB_TAG_NONE	       },	/* Yora != Maltese */
+  {HB_TAG('m','u','d',' '),	HB_TAG('C','P','P',' ')},	/* Mednyj Aleut -> Creoles */
+  {HB_TAG('m','u','i',' '),	HB_TAG('M','L','Y',' ')},	/* Musi -> Malay */
+  {HB_TAG('m','u','n',' '),	HB_TAG_NONE	       },	/* Munda [collection] != Mundari */
+  {HB_TAG('m','u','p',' '),	HB_TAG('R','A','J',' ')},	/* Malvi -> Rajasthani */
+  {HB_TAG('m','u','q',' '),	HB_TAG('H','M','N',' ')},	/* Eastern Xiangxi Miao -> Hmong */
+/*{HB_TAG('m','u','s',' '),	HB_TAG('M','U','S',' ')},*/	/* Creek -> Muscogee */
+  {HB_TAG('m','v','b',' '),	HB_TAG('A','T','H',' ')},	/* Mattole -> Athapaskan */
+  {HB_TAG('m','v','e',' '),	HB_TAG('M','A','W',' ')},	/* Marwari (Pakistan) */
+  {HB_TAG('m','v','f',' '),	HB_TAG('M','N','G',' ')},	/* Peripheral Mongolian -> Mongolian */
+  {HB_TAG('m','w','k',' '),	HB_TAG('M','N','K',' ')},	/* Kita Maninkakan -> Maninka */
+/*{HB_TAG('m','w','l',' '),	HB_TAG('M','W','L',' ')},*/	/* Mirandese */
+  {HB_TAG('m','w','q',' '),	HB_TAG('Q','I','N',' ')},	/* Mün Chin -> Chin */
+  {HB_TAG('m','w','r',' '),	HB_TAG('M','A','W',' ')},	/* Marwari [macrolanguage] */
+  {HB_TAG('m','w','w',' '),	HB_TAG('M','W','W',' ')},	/* Hmong Daw */
+  {HB_TAG('m','w','w',' '),	HB_TAG('H','M','N',' ')},	/* Hmong Daw -> Hmong */
+  {HB_TAG('m','y','m',' '),	HB_TAG('M','E','N',' ')},	/* Me’en */
+/*{HB_TAG('m','y','n',' '),	HB_TAG('M','Y','N',' ')},*/	/* Mayan [collection] */
+  {HB_TAG('m','y','q',' '),	HB_TAG('M','N','K',' ')},	/* Forest Maninka (retired code) -> Maninka */
+  {HB_TAG('m','y','v',' '),	HB_TAG('E','R','Z',' ')},	/* Erzya */
+  {HB_TAG('m','z','b',' '),	HB_TAG('B','B','R',' ')},	/* Tumzabt -> Berber */
+/*{HB_TAG('m','z','n',' '),	HB_TAG('M','Z','N',' ')},*/	/* Mazanderani */
+  {HB_TAG('m','z','s',' '),	HB_TAG('C','P','P',' ')},	/* Macanese -> Creoles */
+  {HB_TAG('n','a','g',' '),	HB_TAG('N','A','G',' ')},	/* Naga Pidgin -> Naga-Assamese */
+  {HB_TAG('n','a','g',' '),	HB_TAG('C','P','P',' ')},	/* Naga Pidgin -> Creoles */
+/*{HB_TAG('n','a','h',' '),	HB_TAG('N','A','H',' ')},*/	/* Nahuatl [collection] */
+  {HB_TAG('n','a','n',' '),	HB_TAG('Z','H','S',' ')},	/* Min Nan Chinese -> Chinese, Simplified */
+/*{HB_TAG('n','a','p',' '),	HB_TAG('N','A','P',' ')},*/	/* Neapolitan */
+  {HB_TAG('n','a','s',' '),	HB_TAG_NONE	       },	/* Naasioi != Naskapi */
+  {HB_TAG('n','a','z',' '),	HB_TAG('N','A','H',' ')},	/* Coatepec Nahuatl -> Nahuatl */
+  {HB_TAG('n','c','h',' '),	HB_TAG('N','A','H',' ')},	/* Central Huasteca Nahuatl -> Nahuatl */
+  {HB_TAG('n','c','i',' '),	HB_TAG('N','A','H',' ')},	/* Classical Nahuatl -> Nahuatl */
+  {HB_TAG('n','c','j',' '),	HB_TAG('N','A','H',' ')},	/* Northern Puebla Nahuatl -> Nahuatl */
+  {HB_TAG('n','c','l',' '),	HB_TAG('N','A','H',' ')},	/* Michoacán Nahuatl -> Nahuatl */
+  {HB_TAG('n','c','r',' '),	HB_TAG_NONE	       },	/* Ncane != N-Cree */
+  {HB_TAG('n','c','x',' '),	HB_TAG('N','A','H',' ')},	/* Central Puebla Nahuatl -> Nahuatl */
+  {HB_TAG('n','d','b',' '),	HB_TAG_NONE	       },	/* Kenswei Nsei != Ndebele */
+/*{HB_TAG('n','d','c',' '),	HB_TAG('N','D','C',' ')},*/	/* Ndau */
+  {HB_TAG('n','d','g',' '),	HB_TAG_NONE	       },	/* Ndengereko != Ndonga */
+/*{HB_TAG('n','d','s',' '),	HB_TAG('N','D','S',' ')},*/	/* Low Saxon */
+  {HB_TAG('n','e','f',' '),	HB_TAG('C','P','P',' ')},	/* Nefamese -> Creoles */
+/*{HB_TAG('n','e','w',' '),	HB_TAG('N','E','W',' ')},*/	/* Newari */
+/*{HB_TAG('n','g','a',' '),	HB_TAG('N','G','A',' ')},*/	/* Ngbaka */
+  {HB_TAG('n','g','l',' '),	HB_TAG('L','M','W',' ')},	/* Lomwe */
+  {HB_TAG('n','g','m',' '),	HB_TAG('C','P','P',' ')},	/* Ngatik Men's Creole -> Creoles */
+  {HB_TAG('n','g','o',' '),	HB_TAG('S','X','T',' ')},	/* Ngoni (retired code) -> Sutu */
+  {HB_TAG('n','g','r',' '),	HB_TAG_NONE	       },	/* Engdewu != Nagari */
+  {HB_TAG('n','g','u',' '),	HB_TAG('N','A','H',' ')},	/* Guerrero Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','c',' '),	HB_TAG('N','A','H',' ')},	/* Tabasco Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','d',' '),	HB_TAG('G','U','A',' ')},	/* Chiripá -> Guarani */
+  {HB_TAG('n','h','e',' '),	HB_TAG('N','A','H',' ')},	/* Eastern Huasteca Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','g',' '),	HB_TAG('N','A','H',' ')},	/* Tetelcingo Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','i',' '),	HB_TAG('N','A','H',' ')},	/* Zacatlán-Ahuacatlán-Tepetzintla Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','k',' '),	HB_TAG('N','A','H',' ')},	/* Isthmus-Cosoleacaque Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','m',' '),	HB_TAG('N','A','H',' ')},	/* Morelos Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','n',' '),	HB_TAG('N','A','H',' ')},	/* Central Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','p',' '),	HB_TAG('N','A','H',' ')},	/* Isthmus-Pajapan Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','q',' '),	HB_TAG('N','A','H',' ')},	/* Huaxcaleca Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','t',' '),	HB_TAG('N','A','H',' ')},	/* Ometepec Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','v',' '),	HB_TAG('N','A','H',' ')},	/* Temascaltepec Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','w',' '),	HB_TAG('N','A','H',' ')},	/* Western Huasteca Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','x',' '),	HB_TAG('N','A','H',' ')},	/* Isthmus-Mecayapan Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','y',' '),	HB_TAG('N','A','H',' ')},	/* Northern Oaxaca Nahuatl -> Nahuatl */
+  {HB_TAG('n','h','z',' '),	HB_TAG('N','A','H',' ')},	/* Santa María La Alta Nahuatl -> Nahuatl */
+  {HB_TAG('n','i','q',' '),	HB_TAG('K','A','L',' ')},	/* Nandi -> Kalenjin */
+  {HB_TAG('n','i','s',' '),	HB_TAG_NONE	       },	/* Nimi != Nisi */
+/*{HB_TAG('n','i','u',' '),	HB_TAG('N','I','U',' ')},*/	/* Niuean */
+  {HB_TAG('n','i','v',' '),	HB_TAG('G','I','L',' ')},	/* Gilyak */
+  {HB_TAG('n','j','t',' '),	HB_TAG('C','P','P',' ')},	/* Ndyuka-Trio Pidgin -> Creoles */
+  {HB_TAG('n','j','z',' '),	HB_TAG('N','I','S',' ')},	/* Nyishi -> Nisi */
+  {HB_TAG('n','k','o',' '),	HB_TAG_NONE	       },	/* Nkonya != N’Ko */
+  {HB_TAG('n','k','x',' '),	HB_TAG('I','J','O',' ')},	/* Nkoroo -> Ijo */
+  {HB_TAG('n','l','a',' '),	HB_TAG('B','M','L',' ')},	/* Ngombale -> Bamileke */
+  {HB_TAG('n','l','e',' '),	HB_TAG('L','U','H',' ')},	/* East Nyala -> Luyia */
+  {HB_TAG('n','l','n',' '),	HB_TAG('N','A','H',' ')},	/* Durango Nahuatl (retired code) -> Nahuatl */
+  {HB_TAG('n','l','v',' '),	HB_TAG('N','A','H',' ')},	/* Orizaba Nahuatl -> Nahuatl */
+  {HB_TAG('n','n','h',' '),	HB_TAG('B','M','L',' ')},	/* Ngiemboon -> Bamileke */
+  {HB_TAG('n','n','z',' '),	HB_TAG('B','M','L',' ')},	/* Nda'nda' -> Bamileke */
+  {HB_TAG('n','o','d',' '),	HB_TAG('N','T','A',' ')},	/* Northern Thai -> Northern Tai */
+/*{HB_TAG('n','o','e',' '),	HB_TAG('N','O','E',' ')},*/	/* Nimadi */
+/*{HB_TAG('n','o','g',' '),	HB_TAG('N','O','G',' ')},*/	/* Nogai */
+/*{HB_TAG('n','o','v',' '),	HB_TAG('N','O','V',' ')},*/	/* Novial */
+  {HB_TAG('n','p','i',' '),	HB_TAG('N','E','P',' ')},	/* Nepali */
+  {HB_TAG('n','p','l',' '),	HB_TAG('N','A','H',' ')},	/* Southeastern Puebla Nahuatl -> Nahuatl */
+  {HB_TAG('n','q','o',' '),	HB_TAG('N','K','O',' ')},	/* N’Ko */
+  {HB_TAG('n','s','k',' '),	HB_TAG('N','A','S',' ')},	/* Naskapi */
+  {HB_TAG('n','s','m',' '),	HB_TAG_NONE	       },	/* Sumi Naga != Northern Sami */
+/*{HB_TAG('n','s','o',' '),	HB_TAG('N','S','O',' ')},*/	/* Northern Sotho */
+  {HB_TAG('n','s','u',' '),	HB_TAG('N','A','H',' ')},	/* Sierra Negra Nahuatl -> Nahuatl */
+  {HB_TAG('n','t','o',' '),	HB_TAG_NONE	       },	/* Ntomba != Esperanto */
+  {HB_TAG('n','u','e',' '),	HB_TAG('B','A','D','0')},	/* Ngundu -> Banda */
+  {HB_TAG('n','u','u',' '),	HB_TAG('B','A','D','0')},	/* Ngbundu -> Banda */
+  {HB_TAG('n','u','z',' '),	HB_TAG('N','A','H',' ')},	/* Tlamacazapa Nahuatl -> Nahuatl */
+  {HB_TAG('n','w','e',' '),	HB_TAG('B','M','L',' ')},	/* Ngwe -> Bamileke */
+  {HB_TAG('n','y','d',' '),	HB_TAG('L','U','H',' ')},	/* Nyore -> Luyia */
+/*{HB_TAG('n','y','m',' '),	HB_TAG('N','Y','M',' ')},*/	/* Nyamwezi */
+  {HB_TAG('n','y','n',' '),	HB_TAG('N','K','L',' ')},	/* Nyankole */
+/*{HB_TAG('n','z','a',' '),	HB_TAG('N','Z','A',' ')},*/	/* Tigon Mbembe -> Mbembe Tigon */
+/*{HB_TAG('o','j','b',' '),	HB_TAG('O','J','B',' ')},*/	/* Northwestern Ojibwa -> Ojibway */
+  {HB_TAG('o','j','c',' '),	HB_TAG('O','J','B',' ')},	/* Central Ojibwa -> Ojibway */
+  {HB_TAG('o','j','g',' '),	HB_TAG('O','J','B',' ')},	/* Eastern Ojibwa -> Ojibway */
+  {HB_TAG('o','j','s',' '),	HB_TAG('O','C','R',' ')},	/* Severn Ojibwa -> Oji-Cree */
+  {HB_TAG('o','j','s',' '),	HB_TAG('O','J','B',' ')},	/* Severn Ojibwa -> Ojibway */
+  {HB_TAG('o','j','w',' '),	HB_TAG('O','J','B',' ')},	/* Western Ojibwa -> Ojibway */
+  {HB_TAG('o','k','d',' '),	HB_TAG('I','J','O',' ')},	/* Okodia -> Ijo */
+  {HB_TAG('o','k','i',' '),	HB_TAG('K','A','L',' ')},	/* Okiek -> Kalenjin */
+  {HB_TAG('o','k','m',' '),	HB_TAG('K','O','H',' ')},	/* Middle Korean (10th-16th cent.) -> Korean Old Hangul */
+  {HB_TAG('o','k','r',' '),	HB_TAG('I','J','O',' ')},	/* Kirike -> Ijo */
+  {HB_TAG('o','n','x',' '),	HB_TAG('C','P','P',' ')},	/* Onin Based Pidgin -> Creoles */
+  {HB_TAG('o','o','r',' '),	HB_TAG('C','P','P',' ')},	/* Oorlams -> Creoles */
+  {HB_TAG('o','r','c',' '),	HB_TAG('O','R','O',' ')},	/* Orma -> Oromo */
+  {HB_TAG('o','r','n',' '),	HB_TAG('M','L','Y',' ')},	/* Orang Kanaq -> Malay */
+  {HB_TAG('o','r','o',' '),	HB_TAG_NONE	       },	/* Orokolo != Oromo */
+  {HB_TAG('o','r','r',' '),	HB_TAG('I','J','O',' ')},	/* Oruma -> Ijo */
+  {HB_TAG('o','r','s',' '),	HB_TAG('M','L','Y',' ')},	/* Orang Seletar -> Malay */
+  {HB_TAG('o','r','y',' '),	HB_TAG('O','R','I',' ')},	/* Odia (formerly Oriya) */
+  {HB_TAG('o','t','w',' '),	HB_TAG('O','J','B',' ')},	/* Ottawa -> Ojibway */
+  {HB_TAG('o','u','a',' '),	HB_TAG('B','B','R',' ')},	/* Tagargrent -> Berber */
+  {HB_TAG('p','a','a',' '),	HB_TAG_NONE	       },	/* Papuan [collection] != Palestinian Aramaic */
+/*{HB_TAG('p','a','g',' '),	HB_TAG('P','A','G',' ')},*/	/* Pangasinan */
+  {HB_TAG('p','a','l',' '),	HB_TAG_NONE	       },	/* Pahlavi != Pali */
+/*{HB_TAG('p','a','m',' '),	HB_TAG('P','A','M',' ')},*/	/* Pampanga -> Pampangan */
+  {HB_TAG('p','a','p',' '),	HB_TAG('P','A','P','0')},	/* Papiamento -> Papiamentu */
+  {HB_TAG('p','a','p',' '),	HB_TAG('C','P','P',' ')},	/* Papiamento -> Creoles */
+  {HB_TAG('p','a','s',' '),	HB_TAG_NONE	       },	/* Papasena != Pashto */
+/*{HB_TAG('p','a','u',' '),	HB_TAG('P','A','U',' ')},*/	/* Palauan */
+  {HB_TAG('p','b','t',' '),	HB_TAG('P','A','S',' ')},	/* Southern Pashto -> Pashto */
+  {HB_TAG('p','b','u',' '),	HB_TAG('P','A','S',' ')},	/* Northern Pashto -> Pashto */
+/*{HB_TAG('p','c','c',' '),	HB_TAG('P','C','C',' ')},*/	/* Bouyei */
+/*{HB_TAG('p','c','d',' '),	HB_TAG('P','C','D',' ')},*/	/* Picard */
+  {HB_TAG('p','c','e',' '),	HB_TAG('P','L','G',' ')},	/* Ruching Palaung -> Palaung */
+  {HB_TAG('p','c','k',' '),	HB_TAG('Q','I','N',' ')},	/* Paite Chin -> Chin */
+  {HB_TAG('p','c','m',' '),	HB_TAG('C','P','P',' ')},	/* Nigerian Pidgin -> Creoles */
+/*{HB_TAG('p','d','c',' '),	HB_TAG('P','D','C',' ')},*/	/* Pennsylvania German */
+  {HB_TAG('p','d','u',' '),	HB_TAG('K','R','N',' ')},	/* Kayan -> Karen */
+  {HB_TAG('p','e','a',' '),	HB_TAG('C','P','P',' ')},	/* Peranakan Indonesian -> Creoles */
+  {HB_TAG('p','e','l',' '),	HB_TAG('M','L','Y',' ')},	/* Pekal -> Malay */
+  {HB_TAG('p','e','s',' '),	HB_TAG('F','A','R',' ')},	/* Iranian Persian -> Persian */
+  {HB_TAG('p','e','y',' '),	HB_TAG('C','P','P',' ')},	/* Petjo -> Creoles */
+  {HB_TAG('p','g','a',' '),	HB_TAG('A','R','A',' ')},	/* Sudanese Creole Arabic -> Arabic */
+  {HB_TAG('p','g','a',' '),	HB_TAG('C','P','P',' ')},	/* Sudanese Creole Arabic -> Creoles */
+/*{HB_TAG('p','h','k',' '),	HB_TAG('P','H','K',' ')},*/	/* Phake */
+  {HB_TAG('p','i','h',' '),	HB_TAG('P','I','H',' ')},	/* Pitcairn-Norfolk -> Norfolk */
+  {HB_TAG('p','i','h',' '),	HB_TAG('C','P','P',' ')},	/* Pitcairn-Norfolk -> Creoles */
+  {HB_TAG('p','i','l',' '),	HB_TAG_NONE	       },	/* Yom != Filipino */
+  {HB_TAG('p','i','s',' '),	HB_TAG('C','P','P',' ')},	/* Pijin -> Creoles */
+  {HB_TAG('p','k','h',' '),	HB_TAG('Q','I','N',' ')},	/* Pankhu -> Chin */
+  {HB_TAG('p','k','o',' '),	HB_TAG('K','A','L',' ')},	/* Pökoot -> Kalenjin */
+  {HB_TAG('p','l','g',' '),	HB_TAG_NONE	       },	/* Pilagá != Palaung */
+  {HB_TAG('p','l','k',' '),	HB_TAG_NONE	       },	/* Kohistani Shina != Polish */
+  {HB_TAG('p','l','l',' '),	HB_TAG('P','L','G',' ')},	/* Shwe Palaung -> Palaung */
+  {HB_TAG('p','l','n',' '),	HB_TAG('C','P','P',' ')},	/* Palenquero -> Creoles */
+  {HB_TAG('p','l','p',' '),	HB_TAG('P','A','P',' ')},	/* Palpa (retired code) */
+  {HB_TAG('p','l','t',' '),	HB_TAG('M','L','G',' ')},	/* Plateau Malagasy -> Malagasy */
+  {HB_TAG('p','m','l',' '),	HB_TAG('C','P','P',' ')},	/* Lingua Franca -> Creoles */
+/*{HB_TAG('p','m','s',' '),	HB_TAG('P','M','S',' ')},*/	/* Piemontese */
+  {HB_TAG('p','m','y',' '),	HB_TAG('C','P','P',' ')},	/* Papuan Malay -> Creoles */
+/*{HB_TAG('p','n','b',' '),	HB_TAG('P','N','B',' ')},*/	/* Western Panjabi */
+  {HB_TAG('p','o','c',' '),	HB_TAG('M','Y','N',' ')},	/* Poqomam -> Mayan */
+  {HB_TAG('p','o','h',' '),	HB_TAG('P','O','H',' ')},	/* Poqomchi' -> Pocomchi */
+  {HB_TAG('p','o','h',' '),	HB_TAG('M','Y','N',' ')},	/* Poqomchi' -> Mayan */
+/*{HB_TAG('p','o','n',' '),	HB_TAG('P','O','N',' ')},*/	/* Pohnpeian */
+  {HB_TAG('p','o','v',' '),	HB_TAG('C','P','P',' ')},	/* Upper Guinea Crioulo -> Creoles */
+  {HB_TAG('p','p','a',' '),	HB_TAG('B','A','G',' ')},	/* Pao (retired code) -> Baghelkhandi */
+  {HB_TAG('p','r','e',' '),	HB_TAG('C','P','P',' ')},	/* Principense -> Creoles */
+/*{HB_TAG('p','r','o',' '),	HB_TAG('P','R','O',' ')},*/	/* Old Provençal (to 1500) -> Provençal / Old Provençal */
+  {HB_TAG('p','r','s',' '),	HB_TAG('D','R','I',' ')},	/* Dari */
+  {HB_TAG('p','r','s',' '),	HB_TAG('F','A','R',' ')},	/* Dari -> Persian */
+  {HB_TAG('p','s','e',' '),	HB_TAG('M','L','Y',' ')},	/* Central Malay -> Malay */
+  {HB_TAG('p','s','t',' '),	HB_TAG('P','A','S',' ')},	/* Central Pashto -> Pashto */
+  {HB_TAG('p','u','b',' '),	HB_TAG('Q','I','N',' ')},	/* Purum -> Chin */
+  {HB_TAG('p','u','z',' '),	HB_TAG('Q','I','N',' ')},	/* Purum Naga (retired code) -> Chin */
+  {HB_TAG('p','w','o',' '),	HB_TAG('P','W','O',' ')},	/* Pwo Western Karen -> Western Pwo Karen */
+  {HB_TAG('p','w','o',' '),	HB_TAG('K','R','N',' ')},	/* Pwo Western Karen -> Karen */
+  {HB_TAG('p','w','w',' '),	HB_TAG('K','R','N',' ')},	/* Pwo Northern Karen -> Karen */
+  {HB_TAG('q','u','b',' '),	HB_TAG('Q','W','H',' ')},	/* Huallaga Huánuco Quechua -> Quechua (Peru) */
+  {HB_TAG('q','u','b',' '),	HB_TAG('Q','U','Z',' ')},	/* Huallaga Huánuco Quechua -> Quechua */
+  {HB_TAG('q','u','c',' '),	HB_TAG('Q','U','C',' ')},	/* K’iche’ */
+  {HB_TAG('q','u','c',' '),	HB_TAG('M','Y','N',' ')},	/* K'iche' -> Mayan */
+  {HB_TAG('q','u','d',' '),	HB_TAG('Q','V','I',' ')},	/* Calderón Highland Quichua -> Quechua (Ecuador) */
+  {HB_TAG('q','u','d',' '),	HB_TAG('Q','U','Z',' ')},	/* Calderón Highland Quichua -> Quechua */
+  {HB_TAG('q','u','f',' '),	HB_TAG('Q','U','Z',' ')},	/* Lambayeque Quechua -> Quechua */
+  {HB_TAG('q','u','g',' '),	HB_TAG('Q','V','I',' ')},	/* Chimborazo Highland Quichua -> Quechua (Ecuador) */
+  {HB_TAG('q','u','g',' '),	HB_TAG('Q','U','Z',' ')},	/* Chimborazo Highland Quichua -> Quechua */
+  {HB_TAG('q','u','h',' '),	HB_TAG('Q','U','H',' ')},	/* South Bolivian Quechua -> Quechua (Bolivia) */
+  {HB_TAG('q','u','h',' '),	HB_TAG('Q','U','Z',' ')},	/* South Bolivian Quechua -> Quechua */
+  {HB_TAG('q','u','k',' '),	HB_TAG('Q','U','Z',' ')},	/* Chachapoyas Quechua -> Quechua */
+  {HB_TAG('q','u','l',' '),	HB_TAG('Q','U','H',' ')},	/* North Bolivian Quechua -> Quechua (Bolivia) */
+  {HB_TAG('q','u','l',' '),	HB_TAG('Q','U','Z',' ')},	/* North Bolivian Quechua -> Quechua */
+  {HB_TAG('q','u','m',' '),	HB_TAG('M','Y','N',' ')},	/* Sipacapense -> Mayan */
+  {HB_TAG('q','u','p',' '),	HB_TAG('Q','V','I',' ')},	/* Southern Pastaza Quechua -> Quechua (Ecuador) */
+  {HB_TAG('q','u','p',' '),	HB_TAG('Q','U','Z',' ')},	/* Southern Pastaza Quechua -> Quechua */
+  {HB_TAG('q','u','r',' '),	HB_TAG('Q','W','H',' ')},	/* Yanahuanca Pasco Quechua -> Quechua (Peru) */
+  {HB_TAG('q','u','r',' '),	HB_TAG('Q','U','Z',' ')},	/* Yanahuanca Pasco Quechua -> Quechua */
+  {HB_TAG('q','u','s',' '),	HB_TAG('Q','U','H',' ')},	/* Santiago del Estero Quichua -> Quechua (Bolivia) */
+  {HB_TAG('q','u','s',' '),	HB_TAG('Q','U','Z',' ')},	/* Santiago del Estero Quichua -> Quechua */
+  {HB_TAG('q','u','v',' '),	HB_TAG('M','Y','N',' ')},	/* Sacapulteco -> Mayan */
+  {HB_TAG('q','u','w',' '),	HB_TAG('Q','V','I',' ')},	/* Tena Lowland Quichua -> Quechua (Ecuador) */
+  {HB_TAG('q','u','w',' '),	HB_TAG('Q','U','Z',' ')},	/* Tena Lowland Quichua -> Quechua */
+  {HB_TAG('q','u','x',' '),	HB_TAG('Q','W','H',' ')},	/* Yauyos Quechua -> Quechua (Peru) */
+  {HB_TAG('q','u','x',' '),	HB_TAG('Q','U','Z',' ')},	/* Yauyos Quechua -> Quechua */
+  {HB_TAG('q','u','y',' '),	HB_TAG('Q','U','Z',' ')},	/* Ayacucho Quechua -> Quechua */
+/*{HB_TAG('q','u','z',' '),	HB_TAG('Q','U','Z',' ')},*/	/* Cusco Quechua -> Quechua */
+  {HB_TAG('q','v','a',' '),	HB_TAG('Q','W','H',' ')},	/* Ambo-Pasco Quechua -> Quechua (Peru) */
+  {HB_TAG('q','v','a',' '),	HB_TAG('Q','U','Z',' ')},	/* Ambo-Pasco Quechua -> Quechua */
+  {HB_TAG('q','v','c',' '),	HB_TAG('Q','U','Z',' ')},	/* Cajamarca Quechua -> Quechua */
+  {HB_TAG('q','v','e',' '),	HB_TAG('Q','U','Z',' ')},	/* Eastern Apurímac Quechua -> Quechua */
+  {HB_TAG('q','v','h',' '),	HB_TAG('Q','W','H',' ')},	/* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua (Peru) */
+  {HB_TAG('q','v','h',' '),	HB_TAG('Q','U','Z',' ')},	/* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua */
+  {HB_TAG('q','v','i',' '),	HB_TAG('Q','V','I',' ')},	/* Imbabura Highland Quichua -> Quechua (Ecuador) */
+  {HB_TAG('q','v','i',' '),	HB_TAG('Q','U','Z',' ')},	/* Imbabura Highland Quichua -> Quechua */
+  {HB_TAG('q','v','j',' '),	HB_TAG('Q','V','I',' ')},	/* Loja Highland Quichua -> Quechua (Ecuador) */
+  {HB_TAG('q','v','j',' '),	HB_TAG('Q','U','Z',' ')},	/* Loja Highland Quichua -> Quechua */
+  {HB_TAG('q','v','l',' '),	HB_TAG('Q','W','H',' ')},	/* Cajatambo North Lima Quechua -> Quechua (Peru) */
+  {HB_TAG('q','v','l',' '),	HB_TAG('Q','U','Z',' ')},	/* Cajatambo North Lima Quechua -> Quechua */
+  {HB_TAG('q','v','m',' '),	HB_TAG('Q','W','H',' ')},	/* Margos-Yarowilca-Lauricocha Quechua -> Quechua (Peru) */
+  {HB_TAG('q','v','m',' '),	HB_TAG('Q','U','Z',' ')},	/* Margos-Yarowilca-Lauricocha Quechua -> Quechua */
+  {HB_TAG('q','v','n',' '),	HB_TAG('Q','W','H',' ')},	/* North Junín Quechua -> Quechua (Peru) */
+  {HB_TAG('q','v','n',' '),	HB_TAG('Q','U','Z',' ')},	/* North Junín Quechua -> Quechua */
+  {HB_TAG('q','v','o',' '),	HB_TAG('Q','V','I',' ')},	/* Napo Lowland Quechua -> Quechua (Ecuador) */
+  {HB_TAG('q','v','o',' '),	HB_TAG('Q','U','Z',' ')},	/* Napo Lowland Quechua -> Quechua */
+  {HB_TAG('q','v','p',' '),	HB_TAG('Q','W','H',' ')},	/* Pacaraos Quechua -> Quechua (Peru) */
+  {HB_TAG('q','v','p',' '),	HB_TAG('Q','U','Z',' ')},	/* Pacaraos Quechua -> Quechua */
+  {HB_TAG('q','v','s',' '),	HB_TAG('Q','U','Z',' ')},	/* San Martín Quechua -> Quechua */
+  {HB_TAG('q','v','w',' '),	HB_TAG('Q','W','H',' ')},	/* Huaylla Wanca Quechua -> Quechua (Peru) */
+  {HB_TAG('q','v','w',' '),	HB_TAG('Q','U','Z',' ')},	/* Huaylla Wanca Quechua -> Quechua */
+  {HB_TAG('q','v','z',' '),	HB_TAG('Q','V','I',' ')},	/* Northern Pastaza Quichua -> Quechua (Ecuador) */
+  {HB_TAG('q','v','z',' '),	HB_TAG('Q','U','Z',' ')},	/* Northern Pastaza Quichua -> Quechua */
+  {HB_TAG('q','w','a',' '),	HB_TAG('Q','W','H',' ')},	/* Corongo Ancash Quechua -> Quechua (Peru) */
+  {HB_TAG('q','w','a',' '),	HB_TAG('Q','U','Z',' ')},	/* Corongo Ancash Quechua -> Quechua */
+  {HB_TAG('q','w','c',' '),	HB_TAG('Q','U','Z',' ')},	/* Classical Quechua -> Quechua */
+  {HB_TAG('q','w','h',' '),	HB_TAG('Q','W','H',' ')},	/* Huaylas Ancash Quechua -> Quechua (Peru) */
+  {HB_TAG('q','w','h',' '),	HB_TAG('Q','U','Z',' ')},	/* Huaylas Ancash Quechua -> Quechua */
+  {HB_TAG('q','w','s',' '),	HB_TAG('Q','W','H',' ')},	/* Sihuas Ancash Quechua -> Quechua (Peru) */
+  {HB_TAG('q','w','s',' '),	HB_TAG('Q','U','Z',' ')},	/* Sihuas Ancash Quechua -> Quechua */
+  {HB_TAG('q','w','t',' '),	HB_TAG('A','T','H',' ')},	/* Kwalhioqua-Tlatskanai -> Athapaskan */
+  {HB_TAG('q','x','a',' '),	HB_TAG('Q','W','H',' ')},	/* Chiquián Ancash Quechua -> Quechua (Peru) */
+  {HB_TAG('q','x','a',' '),	HB_TAG('Q','U','Z',' ')},	/* Chiquián Ancash Quechua -> Quechua */
+  {HB_TAG('q','x','c',' '),	HB_TAG('Q','W','H',' ')},	/* Chincha Quechua -> Quechua (Peru) */
+  {HB_TAG('q','x','c',' '),	HB_TAG('Q','U','Z',' ')},	/* Chincha Quechua -> Quechua */
+  {HB_TAG('q','x','h',' '),	HB_TAG('Q','W','H',' ')},	/* Panao Huánuco Quechua -> Quechua (Peru) */
+  {HB_TAG('q','x','h',' '),	HB_TAG('Q','U','Z',' ')},	/* Panao Huánuco Quechua -> Quechua */
+  {HB_TAG('q','x','l',' '),	HB_TAG('Q','V','I',' ')},	/* Salasaca Highland Quichua -> Quechua (Ecuador) */
+  {HB_TAG('q','x','l',' '),	HB_TAG('Q','U','Z',' ')},	/* Salasaca Highland Quichua -> Quechua */
+  {HB_TAG('q','x','n',' '),	HB_TAG('Q','W','H',' ')},	/* Northern Conchucos Ancash Quechua -> Quechua (Peru) */
+  {HB_TAG('q','x','n',' '),	HB_TAG('Q','U','Z',' ')},	/* Northern Conchucos Ancash Quechua -> Quechua */
+  {HB_TAG('q','x','o',' '),	HB_TAG('Q','W','H',' ')},	/* Southern Conchucos Ancash Quechua -> Quechua (Peru) */
+  {HB_TAG('q','x','o',' '),	HB_TAG('Q','U','Z',' ')},	/* Southern Conchucos Ancash Quechua -> Quechua */
+  {HB_TAG('q','x','p',' '),	HB_TAG('Q','U','Z',' ')},	/* Puno Quechua -> Quechua */
+  {HB_TAG('q','x','r',' '),	HB_TAG('Q','V','I',' ')},	/* Cañar Highland Quichua -> Quechua (Ecuador) */
+  {HB_TAG('q','x','r',' '),	HB_TAG('Q','U','Z',' ')},	/* Cañar Highland Quichua -> Quechua */
+  {HB_TAG('q','x','t',' '),	HB_TAG('Q','W','H',' ')},	/* Santa Ana de Tusi Pasco Quechua -> Quechua (Peru) */
+  {HB_TAG('q','x','t',' '),	HB_TAG('Q','U','Z',' ')},	/* Santa Ana de Tusi Pasco Quechua -> Quechua */
+  {HB_TAG('q','x','u',' '),	HB_TAG('Q','U','Z',' ')},	/* Arequipa-La Unión Quechua -> Quechua */
+  {HB_TAG('q','x','w',' '),	HB_TAG('Q','W','H',' ')},	/* Jauja Wanca Quechua -> Quechua (Peru) */
+  {HB_TAG('q','x','w',' '),	HB_TAG('Q','U','Z',' ')},	/* Jauja Wanca Quechua -> Quechua */
+  {HB_TAG('r','a','g',' '),	HB_TAG('L','U','H',' ')},	/* Logooli -> Luyia */
+/*{HB_TAG('r','a','j',' '),	HB_TAG('R','A','J',' ')},*/	/* Rajasthani [macrolanguage] */
+  {HB_TAG('r','a','l',' '),	HB_TAG('Q','I','N',' ')},	/* Ralte -> Chin */
+/*{HB_TAG('r','a','r',' '),	HB_TAG('R','A','R',' ')},*/	/* Rarotongan */
+  {HB_TAG('r','b','b',' '),	HB_TAG('P','L','G',' ')},	/* Rumai Palaung -> Palaung */
+  {HB_TAG('r','b','l',' '),	HB_TAG('B','I','K',' ')},	/* Miraya Bikol -> Bikol */
+  {HB_TAG('r','c','f',' '),	HB_TAG('C','P','P',' ')},	/* Réunion Creole French -> Creoles */
+/*{HB_TAG('r','e','j',' '),	HB_TAG('R','E','J',' ')},*/	/* Rejang */
+/*{HB_TAG('r','h','g',' '),	HB_TAG('R','H','G',' ')},*/	/* Rohingya */
+/*{HB_TAG('r','i','a',' '),	HB_TAG('R','I','A',' ')},*/	/* Riang (India) */
+  {HB_TAG('r','i','f',' '),	HB_TAG('R','I','F',' ')},	/* Tarifit */
+  {HB_TAG('r','i','f',' '),	HB_TAG('B','B','R',' ')},	/* Tarifit -> Berber */
+/*{HB_TAG('r','i','t',' '),	HB_TAG('R','I','T',' ')},*/	/* Ritharrngu -> Ritarungo */
+  {HB_TAG('r','k','i',' '),	HB_TAG('A','R','K',' ')},	/* Rakhine */
+/*{HB_TAG('r','k','w',' '),	HB_TAG('R','K','W',' ')},*/	/* Arakwal */
+  {HB_TAG('r','m','c',' '),	HB_TAG('R','O','Y',' ')},	/* Carpathian Romani -> Romany */
+  {HB_TAG('r','m','f',' '),	HB_TAG('R','O','Y',' ')},	/* Kalo Finnish Romani -> Romany */
+  {HB_TAG('r','m','l',' '),	HB_TAG('R','O','Y',' ')},	/* Baltic Romani -> Romany */
+  {HB_TAG('r','m','n',' '),	HB_TAG('R','O','Y',' ')},	/* Balkan Romani -> Romany */
+  {HB_TAG('r','m','o',' '),	HB_TAG('R','O','Y',' ')},	/* Sinte Romani -> Romany */
+  {HB_TAG('r','m','s',' '),	HB_TAG_NONE	       },	/* Romanian Sign Language != Romansh */
+  {HB_TAG('r','m','w',' '),	HB_TAG('R','O','Y',' ')},	/* Welsh Romani -> Romany */
+  {HB_TAG('r','m','y',' '),	HB_TAG('R','M','Y',' ')},	/* Vlax Romani */
+  {HB_TAG('r','m','y',' '),	HB_TAG('R','O','Y',' ')},	/* Vlax Romani -> Romany */
+  {HB_TAG('r','m','z',' '),	HB_TAG('A','R','K',' ')},	/* Marma -> Rakhine */
+  {HB_TAG('r','o','m',' '),	HB_TAG('R','O','Y',' ')},	/* Romany [macrolanguage] */
+  {HB_TAG('r','o','p',' '),	HB_TAG('C','P','P',' ')},	/* Kriol -> Creoles */
+  {HB_TAG('r','t','c',' '),	HB_TAG('Q','I','N',' ')},	/* Rungtu Chin -> Chin */
+/*{HB_TAG('r','t','m',' '),	HB_TAG('R','T','M',' ')},*/	/* Rotuman */
+  {HB_TAG('r','u','e',' '),	HB_TAG('R','S','Y',' ')},	/* Rusyn */
+/*{HB_TAG('r','u','p',' '),	HB_TAG('R','U','P',' ')},*/	/* Aromanian */
+  {HB_TAG('r','w','r',' '),	HB_TAG('M','A','W',' ')},	/* Marwari (India) */
+  {HB_TAG('s','a','d',' '),	HB_TAG_NONE	       },	/* Sandawe != Sadri */
+  {HB_TAG('s','a','h',' '),	HB_TAG('Y','A','K',' ')},	/* Yakut -> Sakha */
+  {HB_TAG('s','a','m',' '),	HB_TAG('P','A','A',' ')},	/* Samaritan Aramaic -> Palestinian Aramaic */
+/*{HB_TAG('s','a','s',' '),	HB_TAG('S','A','S',' ')},*/	/* Sasak */
+/*{HB_TAG('s','a','t',' '),	HB_TAG('S','A','T',' ')},*/	/* Santali */
+  {HB_TAG('s','a','y',' '),	HB_TAG_NONE	       },	/* Saya != Sayisi */
+  {HB_TAG('s','c','f',' '),	HB_TAG('C','P','P',' ')},	/* San Miguel Creole French -> Creoles */
+  {HB_TAG('s','c','h',' '),	HB_TAG('Q','I','N',' ')},	/* Sakachep -> Chin */
+  {HB_TAG('s','c','i',' '),	HB_TAG('C','P','P',' ')},	/* Sri Lankan Creole Malay -> Creoles */
+  {HB_TAG('s','c','k',' '),	HB_TAG('S','A','D',' ')},	/* Sadri */
+/*{HB_TAG('s','c','n',' '),	HB_TAG('S','C','N',' ')},*/	/* Sicilian */
+/*{HB_TAG('s','c','o',' '),	HB_TAG('S','C','O',' ')},*/	/* Scots */
+  {HB_TAG('s','c','s',' '),	HB_TAG('S','C','S',' ')},	/* North Slavey */
+  {HB_TAG('s','c','s',' '),	HB_TAG('S','L','A',' ')},	/* North Slavey -> Slavey */
+  {HB_TAG('s','c','s',' '),	HB_TAG('A','T','H',' ')},	/* North Slavey -> Athapaskan */
+  {HB_TAG('s','d','c',' '),	HB_TAG('S','R','D',' ')},	/* Sassarese Sardinian -> Sardinian */
+  {HB_TAG('s','d','h',' '),	HB_TAG('K','U','R',' ')},	/* Southern Kurdish -> Kurdish */
+  {HB_TAG('s','d','n',' '),	HB_TAG('S','R','D',' ')},	/* Gallurese Sardinian -> Sardinian */
+  {HB_TAG('s','d','s',' '),	HB_TAG('B','B','R',' ')},	/* Sened -> Berber */
+  {HB_TAG('s','e','h',' '),	HB_TAG('S','N','A',' ')},	/* Sena */
+  {HB_TAG('s','e','k',' '),	HB_TAG('A','T','H',' ')},	/* Sekani -> Athapaskan */
+/*{HB_TAG('s','e','l',' '),	HB_TAG('S','E','L',' ')},*/	/* Selkup */
+  {HB_TAG('s','e','z',' '),	HB_TAG('Q','I','N',' ')},	/* Senthang Chin -> Chin */
+  {HB_TAG('s','f','m',' '),	HB_TAG('S','F','M',' ')},	/* Small Flowery Miao */
+  {HB_TAG('s','f','m',' '),	HB_TAG('H','M','N',' ')},	/* Small Flowery Miao -> Hmong */
+/*{HB_TAG('s','g','a',' '),	HB_TAG('S','G','A',' ')},*/	/* Old Irish (to 900) */
+  {HB_TAG('s','g','c',' '),	HB_TAG('K','A','L',' ')},	/* Kipsigis -> Kalenjin */
+  {HB_TAG('s','g','o',' '),	HB_TAG_NONE	       },	/* Songa (retired code) != Sango */
+/*{HB_TAG('s','g','s',' '),	HB_TAG('S','G','S',' ')},*/	/* Samogitian */
+  {HB_TAG('s','g','w',' '),	HB_TAG('C','H','G',' ')},	/* Sebat Bet Gurage -> Chaha Gurage */
+  {HB_TAG('s','h','i',' '),	HB_TAG('S','H','I',' ')},	/* Tachelhit */
+  {HB_TAG('s','h','i',' '),	HB_TAG('B','B','R',' ')},	/* Tachelhit -> Berber */
+  {HB_TAG('s','h','l',' '),	HB_TAG('Q','I','N',' ')},	/* Shendu -> Chin */
+/*{HB_TAG('s','h','n',' '),	HB_TAG('S','H','N',' ')},*/	/* Shan */
+  {HB_TAG('s','h','u',' '),	HB_TAG('A','R','A',' ')},	/* Chadian Arabic -> Arabic */
+  {HB_TAG('s','h','y',' '),	HB_TAG('B','B','R',' ')},	/* Tachawit -> Berber */
+  {HB_TAG('s','i','b',' '),	HB_TAG_NONE	       },	/* Sebop != Sibe */
+/*{HB_TAG('s','i','d',' '),	HB_TAG('S','I','D',' ')},*/	/* Sidamo */
+  {HB_TAG('s','i','g',' '),	HB_TAG_NONE	       },	/* Paasaal != Silte Gurage */
+  {HB_TAG('s','i','z',' '),	HB_TAG('B','B','R',' ')},	/* Siwi -> Berber */
+  {HB_TAG('s','j','d',' '),	HB_TAG('K','S','M',' ')},	/* Kildin Sami */
+  {HB_TAG('s','j','o',' '),	HB_TAG('S','I','B',' ')},	/* Xibe -> Sibe */
+  {HB_TAG('s','j','s',' '),	HB_TAG('B','B','R',' ')},	/* Senhaja De Srair -> Berber */
+  {HB_TAG('s','k','g',' '),	HB_TAG('M','L','G',' ')},	/* Sakalava Malagasy -> Malagasy */
+  {HB_TAG('s','k','r',' '),	HB_TAG('S','R','K',' ')},	/* Saraiki */
+  {HB_TAG('s','k','s',' '),	HB_TAG_NONE	       },	/* Maia != Skolt Sami */
+  {HB_TAG('s','k','w',' '),	HB_TAG('C','P','P',' ')},	/* Skepi Creole Dutch -> Creoles */
+  {HB_TAG('s','k','y',' '),	HB_TAG_NONE	       },	/* Sikaiana != Slovak */
+  {HB_TAG('s','l','a',' '),	HB_TAG_NONE	       },	/* Slavic [collection] != Slavey */
+  {HB_TAG('s','m','a',' '),	HB_TAG('S','S','M',' ')},	/* Southern Sami */
+  {HB_TAG('s','m','d',' '),	HB_TAG('M','B','N',' ')},	/* Sama (retired code) -> Mbundu */
+  {HB_TAG('s','m','j',' '),	HB_TAG('L','S','M',' ')},	/* Lule Sami */
+  {HB_TAG('s','m','l',' '),	HB_TAG_NONE	       },	/* Central Sama != Somali */
+  {HB_TAG('s','m','n',' '),	HB_TAG('I','S','M',' ')},	/* Inari Sami */
+  {HB_TAG('s','m','s',' '),	HB_TAG('S','K','S',' ')},	/* Skolt Sami */
+  {HB_TAG('s','m','t',' '),	HB_TAG('Q','I','N',' ')},	/* Simte -> Chin */
+  {HB_TAG('s','n','b',' '),	HB_TAG('I','B','A',' ')},	/* Sebuyau (retired code) -> Iban */
+  {HB_TAG('s','n','h',' '),	HB_TAG_NONE	       },	/* Shinabo (retired code) != Sinhala (Sinhalese) */
+/*{HB_TAG('s','n','k',' '),	HB_TAG('S','N','K',' ')},*/	/* Soninke */
+  {HB_TAG('s','o','g',' '),	HB_TAG_NONE	       },	/* Sogdian != Sodo Gurage */
+/*{HB_TAG('s','o','p',' '),	HB_TAG('S','O','P',' ')},*/	/* Songe */
+  {HB_TAG('s','p','v',' '),	HB_TAG('O','R','I',' ')},	/* Sambalpuri -> Odia (formerly Oriya) */
+  {HB_TAG('s','p','y',' '),	HB_TAG('K','A','L',' ')},	/* Sabaot -> Kalenjin */
+  {HB_TAG('s','r','b',' '),	HB_TAG_NONE	       },	/* Sora != Serbian */
+  {HB_TAG('s','r','c',' '),	HB_TAG('S','R','D',' ')},	/* Logudorese Sardinian -> Sardinian */
+  {HB_TAG('s','r','k',' '),	HB_TAG_NONE	       },	/* Serudung Murut != Saraiki */
+  {HB_TAG('s','r','m',' '),	HB_TAG('C','P','P',' ')},	/* Saramaccan -> Creoles */
+  {HB_TAG('s','r','n',' '),	HB_TAG('C','P','P',' ')},	/* Sranan Tongo -> Creoles */
+  {HB_TAG('s','r','o',' '),	HB_TAG('S','R','D',' ')},	/* Campidanese Sardinian -> Sardinian */
+/*{HB_TAG('s','r','r',' '),	HB_TAG('S','R','R',' ')},*/	/* Serer */
+  {HB_TAG('s','r','s',' '),	HB_TAG('A','T','H',' ')},	/* Sarsi -> Athapaskan */
+  {HB_TAG('s','s','h',' '),	HB_TAG('A','R','A',' ')},	/* Shihhi Arabic -> Arabic */
+  {HB_TAG('s','s','l',' '),	HB_TAG_NONE	       },	/* Western Sisaala != South Slavey */
+  {HB_TAG('s','s','m',' '),	HB_TAG_NONE	       },	/* Semnam != Southern Sami */
+  {HB_TAG('s','t','a',' '),	HB_TAG('C','P','P',' ')},	/* Settla -> Creoles */
+/*{HB_TAG('s','t','q',' '),	HB_TAG('S','T','Q',' ')},*/	/* Saterfriesisch -> Saterland Frisian */
+  {HB_TAG('s','t','v',' '),	HB_TAG('S','I','G',' ')},	/* Silt'e -> Silte Gurage */
+/*{HB_TAG('s','u','k',' '),	HB_TAG('S','U','K',' ')},*/	/* Sukuma */
+  {HB_TAG('s','u','q',' '),	HB_TAG('S','U','R',' ')},	/* Suri */
+  {HB_TAG('s','u','r',' '),	HB_TAG_NONE	       },	/* Mwaghavul != Suri */
+/*{HB_TAG('s','v','a',' '),	HB_TAG('S','V','A',' ')},*/	/* Svan */
+  {HB_TAG('s','v','c',' '),	HB_TAG('C','P','P',' ')},	/* Vincentian Creole English -> Creoles */
+  {HB_TAG('s','v','e',' '),	HB_TAG_NONE	       },	/* Serili != Swedish */
+  {HB_TAG('s','w','b',' '),	HB_TAG('C','M','R',' ')},	/* Maore Comorian -> Comorian */
+  {HB_TAG('s','w','c',' '),	HB_TAG('S','W','K',' ')},	/* Congo Swahili -> Swahili */
+  {HB_TAG('s','w','h',' '),	HB_TAG('S','W','K',' ')},	/* Swahili */
+  {HB_TAG('s','w','k',' '),	HB_TAG_NONE	       },	/* Malawi Sena != Swahili */
+  {HB_TAG('s','w','n',' '),	HB_TAG('B','B','R',' ')},	/* Sawknah -> Berber */
+  {HB_TAG('s','w','v',' '),	HB_TAG('M','A','W',' ')},	/* Shekhawati -> Marwari */
+/*{HB_TAG('s','x','u',' '),	HB_TAG('S','X','U',' ')},*/	/* Upper Saxon */
+  {HB_TAG('s','y','c',' '),	HB_TAG('S','Y','R',' ')},	/* Classical Syriac -> Syriac */
+/*{HB_TAG('s','y','l',' '),	HB_TAG('S','Y','L',' ')},*/	/* Sylheti */
+/*{HB_TAG('s','y','r',' '),	HB_TAG('S','Y','R',' ')},*/	/* Syriac [macrolanguage] */
+/*{HB_TAG('s','z','l',' '),	HB_TAG('S','Z','L',' ')},*/	/* Silesian */
+  {HB_TAG('t','a','a',' '),	HB_TAG('A','T','H',' ')},	/* Lower Tanana -> Athapaskan */
+/*{HB_TAG('t','a','b',' '),	HB_TAG('T','A','B',' ')},*/	/* Tabassaran -> Tabasaran */
+  {HB_TAG('t','a','j',' '),	HB_TAG_NONE	       },	/* Eastern Tamang != Tajiki */
+  {HB_TAG('t','a','q',' '),	HB_TAG('T','M','H',' ')},	/* Tamasheq -> Tamashek */
+  {HB_TAG('t','a','q',' '),	HB_TAG('B','B','R',' ')},	/* Tamasheq -> Berber */
+  {HB_TAG('t','a','s',' '),	HB_TAG('C','P','P',' ')},	/* Tay Boi -> Creoles */
+  {HB_TAG('t','a','u',' '),	HB_TAG('A','T','H',' ')},	/* Upper Tanana -> Athapaskan */
+  {HB_TAG('t','c','b',' '),	HB_TAG('A','T','H',' ')},	/* Tanacross -> Athapaskan */
+  {HB_TAG('t','c','e',' '),	HB_TAG('A','T','H',' ')},	/* Southern Tutchone -> Athapaskan */
+  {HB_TAG('t','c','h',' '),	HB_TAG('C','P','P',' ')},	/* Turks And Caicos Creole English -> Creoles */
+  {HB_TAG('t','c','p',' '),	HB_TAG('Q','I','N',' ')},	/* Tawr Chin -> Chin */
+  {HB_TAG('t','c','s',' '),	HB_TAG('C','P','P',' ')},	/* Torres Strait Creole -> Creoles */
+  {HB_TAG('t','c','y',' '),	HB_TAG('T','U','L',' ')},	/* Tulu -> Tumbuka */
+  {HB_TAG('t','c','z',' '),	HB_TAG('Q','I','N',' ')},	/* Thado Chin -> Chin */
+/*{HB_TAG('t','d','d',' '),	HB_TAG('T','D','D',' ')},*/	/* Tai Nüa -> Dehong Dai */
+  {HB_TAG('t','d','x',' '),	HB_TAG('M','L','G',' ')},	/* Tandroy-Mahafaly Malagasy -> Malagasy */
+  {HB_TAG('t','e','c',' '),	HB_TAG('K','A','L',' ')},	/* Terik -> Kalenjin */
+  {HB_TAG('t','e','m',' '),	HB_TAG('T','M','N',' ')},	/* Timne -> Temne */
+/*{HB_TAG('t','e','t',' '),	HB_TAG('T','E','T',' ')},*/	/* Tetum */
+  {HB_TAG('t','e','z',' '),	HB_TAG('B','B','R',' ')},	/* Tetserret -> Berber */
+  {HB_TAG('t','f','n',' '),	HB_TAG('A','T','H',' ')},	/* Tanaina -> Athapaskan */
+  {HB_TAG('t','g','h',' '),	HB_TAG('C','P','P',' ')},	/* Tobagonian Creole English -> Creoles */
+  {HB_TAG('t','g','j',' '),	HB_TAG('N','I','S',' ')},	/* Tagin -> Nisi */
+  {HB_TAG('t','g','n',' '),	HB_TAG_NONE	       },	/* Tandaganon != Tongan */
+  {HB_TAG('t','g','r',' '),	HB_TAG_NONE	       },	/* Tareng != Tigre */
+  {HB_TAG('t','g','x',' '),	HB_TAG('A','T','H',' ')},	/* Tagish -> Athapaskan */
+  {HB_TAG('t','g','y',' '),	HB_TAG_NONE	       },	/* Togoyo != Tigrinya */
+  {HB_TAG('t','h','t',' '),	HB_TAG('A','T','H',' ')},	/* Tahltan -> Athapaskan */
+  {HB_TAG('t','h','v',' '),	HB_TAG('T','M','H',' ')},	/* Tahaggart Tamahaq -> Tamashek */
+  {HB_TAG('t','h','v',' '),	HB_TAG('B','B','R',' ')},	/* Tahaggart Tamahaq -> Berber */
+  {HB_TAG('t','h','z',' '),	HB_TAG('T','M','H',' ')},	/* Tayart Tamajeq -> Tamashek */
+  {HB_TAG('t','h','z',' '),	HB_TAG('B','B','R',' ')},	/* Tayart Tamajeq -> Berber */
+  {HB_TAG('t','i','a',' '),	HB_TAG('B','B','R',' ')},	/* Tidikelt Tamazight -> Berber */
+  {HB_TAG('t','i','g',' '),	HB_TAG('T','G','R',' ')},	/* Tigre */
+/*{HB_TAG('t','i','v',' '),	HB_TAG('T','I','V',' ')},*/	/* Tiv */
+/*{HB_TAG('t','j','l',' '),	HB_TAG('T','J','L',' ')},*/	/* Tai Laing */
+  {HB_TAG('t','j','o',' '),	HB_TAG('B','B','R',' ')},	/* Temacine Tamazight -> Berber */
+  {HB_TAG('t','k','g',' '),	HB_TAG('M','L','G',' ')},	/* Tesaka Malagasy -> Malagasy */
+  {HB_TAG('t','k','m',' '),	HB_TAG_NONE	       },	/* Takelma != Turkmen */
+/*{HB_TAG('t','l','i',' '),	HB_TAG('T','L','I',' ')},*/	/* Tlingit */
+  {HB_TAG('t','m','g',' '),	HB_TAG('C','P','P',' ')},	/* Ternateño -> Creoles */
+  {HB_TAG('t','m','h',' '),	HB_TAG('T','M','H',' ')},	/* Tamashek [macrolanguage] */
+  {HB_TAG('t','m','h',' '),	HB_TAG('B','B','R',' ')},	/* Tamashek [macrolanguage] -> Berber */
+  {HB_TAG('t','m','n',' '),	HB_TAG_NONE	       },	/* Taman (Indonesia) != Temne */
+  {HB_TAG('t','m','w',' '),	HB_TAG('M','L','Y',' ')},	/* Temuan -> Malay */
+  {HB_TAG('t','n','a',' '),	HB_TAG_NONE	       },	/* Tacana != Tswana */
+  {HB_TAG('t','n','e',' '),	HB_TAG_NONE	       },	/* Tinoc Kallahan (retired code) != Tundra Enets */
+  {HB_TAG('t','n','f',' '),	HB_TAG('D','R','I',' ')},	/* Tangshewi (retired code) -> Dari */
+  {HB_TAG('t','n','f',' '),	HB_TAG('F','A','R',' ')},	/* Tangshewi (retired code) -> Persian */
+  {HB_TAG('t','n','g',' '),	HB_TAG_NONE	       },	/* Tobanga != Tonga */
+  {HB_TAG('t','o','d',' '),	HB_TAG('T','O','D','0')},	/* Toma */
+  {HB_TAG('t','o','i',' '),	HB_TAG('T','N','G',' ')},	/* Tonga (Zambia) */
+  {HB_TAG('t','o','j',' '),	HB_TAG('M','Y','N',' ')},	/* Tojolabal -> Mayan */
+  {HB_TAG('t','o','l',' '),	HB_TAG('A','T','H',' ')},	/* Tolowa -> Athapaskan */
+  {HB_TAG('t','o','r',' '),	HB_TAG('B','A','D','0')},	/* Togbo-Vara Banda -> Banda */
+  {HB_TAG('t','p','i',' '),	HB_TAG('T','P','I',' ')},	/* Tok Pisin */
+  {HB_TAG('t','p','i',' '),	HB_TAG('C','P','P',' ')},	/* Tok Pisin -> Creoles */
+  {HB_TAG('t','r','f',' '),	HB_TAG('C','P','P',' ')},	/* Trinidadian Creole English -> Creoles */
+  {HB_TAG('t','r','k',' '),	HB_TAG_NONE	       },	/* Turkic [collection] != Turkish */
+  {HB_TAG('t','r','u',' '),	HB_TAG('T','U','A',' ')},	/* Turoyo -> Turoyo Aramaic */
+  {HB_TAG('t','r','u',' '),	HB_TAG('S','Y','R',' ')},	/* Turoyo -> Syriac */
+  {HB_TAG('t','s','g',' '),	HB_TAG_NONE	       },	/* Tausug != Tsonga */
+/*{HB_TAG('t','s','j',' '),	HB_TAG('T','S','J',' ')},*/	/* Tshangla */
+  {HB_TAG('t','t','c',' '),	HB_TAG('M','Y','N',' ')},	/* Tektiteko -> Mayan */
+  {HB_TAG('t','t','m',' '),	HB_TAG('A','T','H',' ')},	/* Northern Tutchone -> Athapaskan */
+  {HB_TAG('t','t','q',' '),	HB_TAG('T','M','H',' ')},	/* Tawallammat Tamajaq -> Tamashek */
+  {HB_TAG('t','t','q',' '),	HB_TAG('B','B','R',' ')},	/* Tawallammat Tamajaq -> Berber */
+  {HB_TAG('t','u','a',' '),	HB_TAG_NONE	       },	/* Wiarumus != Turoyo Aramaic */
+  {HB_TAG('t','u','l',' '),	HB_TAG_NONE	       },	/* Tula != Tumbuka */
+/*{HB_TAG('t','u','m',' '),	HB_TAG('T','U','M',' ')},*/	/* Tumbuka -> Tulu */
+  {HB_TAG('t','u','u',' '),	HB_TAG('A','T','H',' ')},	/* Tututni -> Athapaskan */
+  {HB_TAG('t','u','v',' '),	HB_TAG_NONE	       },	/* Turkana != Tuvin */
+  {HB_TAG('t','u','y',' '),	HB_TAG('K','A','L',' ')},	/* Tugen -> Kalenjin */
+/*{HB_TAG('t','v','l',' '),	HB_TAG('T','V','L',' ')},*/	/* Tuvalu */
+  {HB_TAG('t','v','y',' '),	HB_TAG('C','P','P',' ')},	/* Timor Pidgin -> Creoles */
+  {HB_TAG('t','x','c',' '),	HB_TAG('A','T','H',' ')},	/* Tsetsaut -> Athapaskan */
+  {HB_TAG('t','x','y',' '),	HB_TAG('M','L','G',' ')},	/* Tanosy Malagasy -> Malagasy */
+  {HB_TAG('t','y','v',' '),	HB_TAG('T','U','V',' ')},	/* Tuvinian -> Tuvin */
+/*{HB_TAG('t','y','z',' '),	HB_TAG('T','Y','Z',' ')},*/	/* Tày */
+  {HB_TAG('t','z','h',' '),	HB_TAG('M','Y','N',' ')},	/* Tzeltal -> Mayan */
+  {HB_TAG('t','z','j',' '),	HB_TAG('M','Y','N',' ')},	/* Tz'utujil -> Mayan */
+  {HB_TAG('t','z','m',' '),	HB_TAG('T','Z','M',' ')},	/* Central Atlas Tamazight -> Tamazight */
+  {HB_TAG('t','z','m',' '),	HB_TAG('B','B','R',' ')},	/* Central Atlas Tamazight -> Berber */
+  {HB_TAG('t','z','o',' '),	HB_TAG('T','Z','O',' ')},	/* Tzotzil */
+  {HB_TAG('t','z','o',' '),	HB_TAG('M','Y','N',' ')},	/* Tzotzil -> Mayan */
+  {HB_TAG('u','b','l',' '),	HB_TAG('B','I','K',' ')},	/* Buhi'non Bikol -> Bikol */
+/*{HB_TAG('u','d','m',' '),	HB_TAG('U','D','M',' ')},*/	/* Udmurt */
+  {HB_TAG('u','k','i',' '),	HB_TAG('K','U','I',' ')},	/* Kui (India) */
+  {HB_TAG('u','l','n',' '),	HB_TAG('C','P','P',' ')},	/* Unserdeutsch -> Creoles */
+/*{HB_TAG('u','m','b',' '),	HB_TAG('U','M','B',' ')},*/	/* Umbundu */
+  {HB_TAG('u','n','r',' '),	HB_TAG('M','U','N',' ')},	/* Mundari */
+  {HB_TAG('u','r','k',' '),	HB_TAG('M','L','Y',' ')},	/* Urak Lawoi' -> Malay */
+  {HB_TAG('u','s','p',' '),	HB_TAG('M','Y','N',' ')},	/* Uspanteco -> Mayan */
+  {HB_TAG('u','z','n',' '),	HB_TAG('U','Z','B',' ')},	/* Northern Uzbek -> Uzbek */
+  {HB_TAG('u','z','s',' '),	HB_TAG('U','Z','B',' ')},	/* Southern Uzbek -> Uzbek */
+  {HB_TAG('v','a','p',' '),	HB_TAG('Q','I','N',' ')},	/* Vaiphei -> Chin */
+/*{HB_TAG('v','e','c',' '),	HB_TAG('V','E','C',' ')},*/	/* Venetian */
+  {HB_TAG('v','i','c',' '),	HB_TAG('C','P','P',' ')},	/* Virgin Islands Creole English -> Creoles */
+  {HB_TAG('v','i','t',' '),	HB_TAG_NONE	       },	/* Viti != Vietnamese */
+  {HB_TAG('v','k','k',' '),	HB_TAG('M','L','Y',' ')},	/* Kaur -> Malay */
+  {HB_TAG('v','k','p',' '),	HB_TAG('C','P','P',' ')},	/* Korlai Creole Portuguese -> Creoles */
+  {HB_TAG('v','k','t',' '),	HB_TAG('M','L','Y',' ')},	/* Tenggarong Kutai Malay -> Malay */
+  {HB_TAG('v','l','s',' '),	HB_TAG('F','L','E',' ')},	/* Vlaams -> Dutch (Flemish) */
+  {HB_TAG('v','m','w',' '),	HB_TAG('M','A','K',' ')},	/* Makhuwa */
+/*{HB_TAG('v','r','o',' '),	HB_TAG('V','R','O',' ')},*/	/* Võro */
+  {HB_TAG('w','a','g',' '),	HB_TAG_NONE	       },	/* Wa'ema != Wagdi */
+/*{HB_TAG('w','a','r',' '),	HB_TAG('W','A','R',' ')},*/	/* Waray (Philippines) -> Waray-Waray */
+  {HB_TAG('w','b','m',' '),	HB_TAG('W','A',' ',' ')},	/* Wa */
+  {HB_TAG('w','b','r',' '),	HB_TAG('W','A','G',' ')},	/* Wagdi */
+  {HB_TAG('w','b','r',' '),	HB_TAG('R','A','J',' ')},	/* Wagdi -> Rajasthani */
+/*{HB_TAG('w','c','i',' '),	HB_TAG('W','C','I',' ')},*/	/* Waci Gbe */
+  {HB_TAG('w','e','a',' '),	HB_TAG('K','R','N',' ')},	/* Wewaw -> Karen */
+  {HB_TAG('w','e','s',' '),	HB_TAG('C','P','P',' ')},	/* Cameroon Pidgin -> Creoles */
+  {HB_TAG('w','e','u',' '),	HB_TAG('Q','I','N',' ')},	/* Rawngtu Chin -> Chin */
+  {HB_TAG('w','l','c',' '),	HB_TAG('C','M','R',' ')},	/* Mwali Comorian -> Comorian */
+  {HB_TAG('w','l','e',' '),	HB_TAG('S','I','G',' ')},	/* Wolane -> Silte Gurage */
+  {HB_TAG('w','l','k',' '),	HB_TAG('A','T','H',' ')},	/* Wailaki -> Athapaskan */
+  {HB_TAG('w','n','i',' '),	HB_TAG('C','M','R',' ')},	/* Ndzwani Comorian -> Comorian */
+  {HB_TAG('w','r','y',' '),	HB_TAG('M','A','W',' ')},	/* Merwari -> Marwari */
+  {HB_TAG('w','s','g',' '),	HB_TAG('G','O','N',' ')},	/* Adilabad Gondi -> Gondi */
+/*{HB_TAG('w','t','m',' '),	HB_TAG('W','T','M',' ')},*/	/* Mewati */
+  {HB_TAG('w','u','u',' '),	HB_TAG('Z','H','S',' ')},	/* Wu Chinese -> Chinese, Simplified */
+  {HB_TAG('x','a','l',' '),	HB_TAG('K','L','M',' ')},	/* Kalmyk */
+  {HB_TAG('x','a','l',' '),	HB_TAG('T','O','D',' ')},	/* Kalmyk -> Todo */
+  {HB_TAG('x','a','n',' '),	HB_TAG('S','E','K',' ')},	/* Xamtanga -> Sekota */
+  {HB_TAG('x','b','d',' '),	HB_TAG_NONE	       },	/* Bindal != Lü */
+/*{HB_TAG('x','j','b',' '),	HB_TAG('X','J','B',' ')},*/	/* Minjungbal -> Minjangbal */
+/*{HB_TAG('x','k','f',' '),	HB_TAG('X','K','F',' ')},*/	/* Khengkha */
+  {HB_TAG('x','m','g',' '),	HB_TAG('B','M','L',' ')},	/* Mengaka -> Bamileke */
+  {HB_TAG('x','m','m',' '),	HB_TAG('M','L','Y',' ')},	/* Manado Malay -> Malay */
+  {HB_TAG('x','m','m',' '),	HB_TAG('C','P','P',' ')},	/* Manado Malay -> Creoles */
+  {HB_TAG('x','m','v',' '),	HB_TAG('M','L','G',' ')},	/* Antankarana Malagasy -> Malagasy */
+  {HB_TAG('x','m','w',' '),	HB_TAG('M','L','G',' ')},	/* Tsimihety Malagasy -> Malagasy */
+  {HB_TAG('x','n','j',' '),	HB_TAG('S','X','T',' ')},	/* Ngoni (Tanzania) -> Sutu */
+  {HB_TAG('x','n','q',' '),	HB_TAG('S','X','T',' ')},	/* Ngoni (Mozambique) -> Sutu */
+  {HB_TAG('x','n','r',' '),	HB_TAG('D','G','R',' ')},	/* Kangri -> Dogri (macrolanguage) */
+/*{HB_TAG('x','o','g',' '),	HB_TAG('X','O','G',' ')},*/	/* Soga */
+  {HB_TAG('x','p','e',' '),	HB_TAG('X','P','E',' ')},	/* Liberia Kpelle -> Kpelle (Liberia) */
+  {HB_TAG('x','p','e',' '),	HB_TAG('K','P','L',' ')},	/* Liberia Kpelle -> Kpelle */
+  {HB_TAG('x','s','l',' '),	HB_TAG('S','S','L',' ')},	/* South Slavey */
+  {HB_TAG('x','s','l',' '),	HB_TAG('S','L','A',' ')},	/* South Slavey -> Slavey */
+  {HB_TAG('x','s','l',' '),	HB_TAG('A','T','H',' ')},	/* South Slavey -> Athapaskan */
+  {HB_TAG('x','s','t',' '),	HB_TAG('S','I','G',' ')},	/* Silt'e (retired code) -> Silte Gurage */
+/*{HB_TAG('x','u','b',' '),	HB_TAG('X','U','B',' ')},*/	/* Betta Kurumba -> Bette Kuruma */
+/*{HB_TAG('x','u','j',' '),	HB_TAG('X','U','J',' ')},*/	/* Jennu Kurumba -> Jennu Kuruma */
+  {HB_TAG('x','u','p',' '),	HB_TAG('A','T','H',' ')},	/* Upper Umpqua -> Athapaskan */
+  {HB_TAG('x','w','o',' '),	HB_TAG('T','O','D',' ')},	/* Written Oirat -> Todo */
+  {HB_TAG('y','a','j',' '),	HB_TAG('B','A','D','0')},	/* Banda-Yangere -> Banda */
+  {HB_TAG('y','a','k',' '),	HB_TAG_NONE	       },	/* Yakama != Sakha */
+/*{HB_TAG('y','a','o',' '),	HB_TAG('Y','A','O',' ')},*/	/* Yao */
+/*{HB_TAG('y','a','p',' '),	HB_TAG('Y','A','P',' ')},*/	/* Yapese */
+  {HB_TAG('y','b','a',' '),	HB_TAG_NONE	       },	/* Yala != Yoruba */
+  {HB_TAG('y','b','b',' '),	HB_TAG('B','M','L',' ')},	/* Yemba -> Bamileke */
+  {HB_TAG('y','b','d',' '),	HB_TAG('A','R','K',' ')},	/* Yangbye (retired code) -> Rakhine */
+  {HB_TAG('y','d','d',' '),	HB_TAG('J','I','I',' ')},	/* Eastern Yiddish -> Yiddish */
+/*{HB_TAG('y','g','p',' '),	HB_TAG('Y','G','P',' ')},*/	/* Gepo */
+  {HB_TAG('y','i','h',' '),	HB_TAG('J','I','I',' ')},	/* Western Yiddish -> Yiddish */
+  {HB_TAG('y','i','m',' '),	HB_TAG_NONE	       },	/* Yimchungru Naga != Yi Modern */
+/*{HB_TAG('y','n','a',' '),	HB_TAG('Y','N','A',' ')},*/	/* Aluo */
+  {HB_TAG('y','o','s',' '),	HB_TAG('Q','I','N',' ')},	/* Yos (retired code) -> Chin */
+  {HB_TAG('y','u','a',' '),	HB_TAG('M','Y','N',' ')},	/* Yucateco -> Mayan */
+  {HB_TAG('y','u','e',' '),	HB_TAG('Z','H','H',' ')},	/* Yue Chinese -> Chinese, Traditional, Hong Kong SAR */
+/*{HB_TAG('y','w','q',' '),	HB_TAG('Y','W','Q',' ')},*/	/* Wuding-Luquan Yi */
+  {HB_TAG('z','c','h',' '),	HB_TAG('Z','H','A',' ')},	/* Central Hongshuihe Zhuang -> Zhuang */
+  {HB_TAG('z','d','j',' '),	HB_TAG('C','M','R',' ')},	/* Ngazidja Comorian -> Comorian */
+/*{HB_TAG('z','e','a',' '),	HB_TAG('Z','E','A',' ')},*/	/* Zeeuws -> Zealandic */
+  {HB_TAG('z','e','h',' '),	HB_TAG('Z','H','A',' ')},	/* Eastern Hongshuihe Zhuang -> Zhuang */
+  {HB_TAG('z','e','n',' '),	HB_TAG('B','B','R',' ')},	/* Zenaga -> Berber */
+  {HB_TAG('z','g','b',' '),	HB_TAG('Z','H','A',' ')},	/* Guibei Zhuang -> Zhuang */
+  {HB_TAG('z','g','h',' '),	HB_TAG('Z','G','H',' ')},	/* Standard Moroccan Tamazight */
+  {HB_TAG('z','g','h',' '),	HB_TAG('B','B','R',' ')},	/* Standard Moroccan Tamazight -> Berber */
+  {HB_TAG('z','g','m',' '),	HB_TAG('Z','H','A',' ')},	/* Minz Zhuang -> Zhuang */
+  {HB_TAG('z','g','n',' '),	HB_TAG('Z','H','A',' ')},	/* Guibian Zhuang -> Zhuang */
+  {HB_TAG('z','h','d',' '),	HB_TAG('Z','H','A',' ')},	/* Dai Zhuang -> Zhuang */
+  {HB_TAG('z','h','n',' '),	HB_TAG('Z','H','A',' ')},	/* Nong Zhuang -> Zhuang */
+  {HB_TAG('z','l','j',' '),	HB_TAG('Z','H','A',' ')},	/* Liujiang Zhuang -> Zhuang */
+  {HB_TAG('z','l','m',' '),	HB_TAG('M','L','Y',' ')},	/* Malay */
+  {HB_TAG('z','l','n',' '),	HB_TAG('Z','H','A',' ')},	/* Lianshan Zhuang -> Zhuang */
+  {HB_TAG('z','l','q',' '),	HB_TAG('Z','H','A',' ')},	/* Liuqian Zhuang -> Zhuang */
+  {HB_TAG('z','m','i',' '),	HB_TAG('M','L','Y',' ')},	/* Negeri Sembilan Malay -> Malay */
+  {HB_TAG('z','m','z',' '),	HB_TAG('B','A','D','0')},	/* Mbandja -> Banda */
+  {HB_TAG('z','n','d',' '),	HB_TAG_NONE	       },	/* Zande [collection] != Zande */
+  {HB_TAG('z','n','e',' '),	HB_TAG('Z','N','D',' ')},	/* Zande */
+  {HB_TAG('z','o','m',' '),	HB_TAG('Q','I','N',' ')},	/* Zou -> Chin */
+  {HB_TAG('z','q','e',' '),	HB_TAG('Z','H','A',' ')},	/* Qiubei Zhuang -> Zhuang */
+  {HB_TAG('z','s','m',' '),	HB_TAG('M','L','Y',' ')},	/* Standard Malay -> Malay */
+  {HB_TAG('z','u','m',' '),	HB_TAG('L','R','C',' ')},	/* Kumzari -> Luri */
+  {HB_TAG('z','y','b',' '),	HB_TAG('Z','H','A',' ')},	/* Yongbei Zhuang -> Zhuang */
+  {HB_TAG('z','y','g',' '),	HB_TAG('Z','H','A',' ')},	/* Yang Zhuang -> Zhuang */
+  {HB_TAG('z','y','j',' '),	HB_TAG('Z','H','A',' ')},	/* Youjiang Zhuang -> Zhuang */
+  {HB_TAG('z','y','n',' '),	HB_TAG('Z','H','A',' ')},	/* Yongnan Zhuang -> Zhuang */
+  {HB_TAG('z','y','p',' '),	HB_TAG('Q','I','N',' ')},	/* Zyphe Chin -> Chin */
+/*{HB_TAG('z','z','a',' '),	HB_TAG('Z','Z','A',' ')},*/	/* Zazaki [macrolanguage] */
+  {HB_TAG('z','z','j',' '),	HB_TAG('Z','H','A',' ')},	/* Zuojiang Zhuang -> Zhuang */
+};
+
 /**
  * hb_ot_tags_from_complex_language:
  * @lang_str: a BCP 47 language tag to convert.
@@ -1639,69 +1642,75 @@
 				  unsigned int *count /* IN/OUT */,
 				  hb_tag_t     *tags /* OUT */)
 {
-  if (subtag_matches (lang_str, limit, "-fonnapa"))
+  if (limit - lang_str >= 7)
   {
-    /* Undetermined; North American Phonetic Alphabet */
-    tags[0] = HB_TAG('A','P','P','H');  /* Phonetic transcription—Americanist conventions */
-    *count = 1;
-    return true;
+    const char *p = strchr (lang_str, '-');
+    if (!p || p >= limit || limit - p < 5) goto out;
+    if (subtag_matches (p, limit, "-fonnapa", 8))
+    {
+      /* Undetermined; North American Phonetic Alphabet */
+      tags[0] = HB_TAG('A','P','P','H');  /* Phonetic transcription—Americanist conventions */
+      *count = 1;
+      return true;
+    }
+    if (subtag_matches (p, limit, "-polyton", 8))
+    {
+      /* Modern Greek (1453-); Polytonic Greek */
+      tags[0] = HB_TAG('P','G','R',' ');  /* Polytonic Greek */
+      *count = 1;
+      return true;
+    }
+    if (subtag_matches (p, limit, "-arevmda", 8))
+    {
+      /* Armenian; Western Armenian (retired code) */
+      tags[0] = HB_TAG('H','Y','E',' ');  /* Armenian */
+      *count = 1;
+      return true;
+    }
+    if (subtag_matches (p, limit, "-provenc", 8))
+    {
+      /* Occitan (post 1500); Provençal */
+      tags[0] = HB_TAG('P','R','O',' ');  /* Provençal / Old Provençal */
+      *count = 1;
+      return true;
+    }
+    if (subtag_matches (p, limit, "-fonipa", 7))
+    {
+      /* Undetermined; International Phonetic Alphabet */
+      tags[0] = HB_TAG('I','P','P','H');  /* Phonetic transcription—IPA conventions */
+      *count = 1;
+      return true;
+    }
+    if (subtag_matches (p, limit, "-geok", 5))
+    {
+      /* Undetermined; Khutsuri (Asomtavruli and Nuskhuri) */
+      tags[0] = HB_TAG('K','G','E',' ');  /* Khutsuri Georgian */
+      *count = 1;
+      return true;
+    }
+    if (subtag_matches (p, limit, "-syre", 5))
+    {
+      /* Undetermined; Syriac (Estrangelo variant) */
+      tags[0] = HB_TAG('S','Y','R','E');  /* Syriac, Estrangela script-variant (equivalent to ISO 15924 'Syre') */
+      *count = 1;
+      return true;
+    }
+    if (subtag_matches (p, limit, "-syrj", 5))
+    {
+      /* Undetermined; Syriac (Western variant) */
+      tags[0] = HB_TAG('S','Y','R','J');  /* Syriac, Western script-variant (equivalent to ISO 15924 'Syrj') */
+      *count = 1;
+      return true;
+    }
+    if (subtag_matches (p, limit, "-syrn", 5))
+    {
+      /* Undetermined; Syriac (Eastern variant) */
+      tags[0] = HB_TAG('S','Y','R','N');  /* Syriac, Eastern script-variant (equivalent to ISO 15924 'Syrn') */
+      *count = 1;
+      return true;
+    }
   }
-  if (subtag_matches (lang_str, limit, "-polyton"))
-  {
-    /* Modern Greek (1453-); Polytonic Greek */
-    tags[0] = HB_TAG('P','G','R',' ');  /* Polytonic Greek */
-    *count = 1;
-    return true;
-  }
-  if (subtag_matches (lang_str, limit, "-arevmda"))
-  {
-    /* Armenian; Western Armenian (retired code) */
-    tags[0] = HB_TAG('H','Y','E',' ');  /* Armenian */
-    *count = 1;
-    return true;
-  }
-  if (subtag_matches (lang_str, limit, "-provenc"))
-  {
-    /* Occitan (post 1500); Provençal */
-    tags[0] = HB_TAG('P','R','O',' ');  /* Provençal / Old Provençal */
-    *count = 1;
-    return true;
-  }
-  if (subtag_matches (lang_str, limit, "-fonipa"))
-  {
-    /* Undetermined; International Phonetic Alphabet */
-    tags[0] = HB_TAG('I','P','P','H');  /* Phonetic transcription—IPA conventions */
-    *count = 1;
-    return true;
-  }
-  if (subtag_matches (lang_str, limit, "-geok"))
-  {
-    /* Undetermined; Khutsuri (Asomtavruli and Nuskhuri) */
-    tags[0] = HB_TAG('K','G','E',' ');  /* Khutsuri Georgian */
-    *count = 1;
-    return true;
-  }
-  if (subtag_matches (lang_str, limit, "-syre"))
-  {
-    /* Undetermined; Syriac (Estrangelo variant) */
-    tags[0] = HB_TAG('S','Y','R','E');  /* Syriac, Estrangela script-variant (equivalent to ISO 15924 'Syre') */
-    *count = 1;
-    return true;
-  }
-  if (subtag_matches (lang_str, limit, "-syrj"))
-  {
-    /* Undetermined; Syriac (Western variant) */
-    tags[0] = HB_TAG('S','Y','R','J');  /* Syriac, Western script-variant (equivalent to ISO 15924 'Syrj') */
-    *count = 1;
-    return true;
-  }
-  if (subtag_matches (lang_str, limit, "-syrn"))
-  {
-    /* Undetermined; Syriac (Eastern variant) */
-    tags[0] = HB_TAG('S','Y','R','N');  /* Syriac, Eastern script-variant (equivalent to ISO 15924 'Syrn') */
-    *count = 1;
-    return true;
-  }
+out:
   switch (lang_str[0])
   {
   case 'a':
@@ -1714,7 +1723,7 @@
     }
     break;
   case 'c':
-    if (lang_matches (&lang_str[1], "do-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "do-hant-hk", 10))
     {
       /* Min Dong Chinese; Han (Traditional variant); Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -1721,7 +1730,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "do-hant-mo"))
+    if (lang_matches (&lang_str[1], limit, "do-hant-mo", 10))
     {
       /* Min Dong Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -1734,7 +1743,7 @@
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "jy-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "jy-hant-hk", 10))
     {
       /* Jinyu Chinese; Han (Traditional variant); Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -1741,7 +1750,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "jy-hant-mo"))
+    if (lang_matches (&lang_str[1], limit, "jy-hant-mo", 10))
     {
       /* Jinyu Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -1754,7 +1763,7 @@
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "mn-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "mn-hant-hk", 10))
     {
       /* Mandarin Chinese; Han (Traditional variant); Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -1761,7 +1770,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "mn-hant-mo"))
+    if (lang_matches (&lang_str[1], limit, "mn-hant-mo", 10))
     {
       /* Mandarin Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -1774,7 +1783,7 @@
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "np-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "np-hant-hk", 10))
     {
       /* Northern Ping Chinese; Han (Traditional variant); Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -1781,7 +1790,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "np-hant-mo"))
+    if (lang_matches (&lang_str[1], limit, "np-hant-mo", 10))
     {
       /* Northern Ping Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -1794,7 +1803,7 @@
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "px-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "px-hant-hk", 10))
     {
       /* Pu-Xian Chinese; Han (Traditional variant); Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -1801,7 +1810,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "px-hant-mo"))
+    if (lang_matches (&lang_str[1], limit, "px-hant-mo", 10))
     {
       /* Pu-Xian Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -1814,7 +1823,7 @@
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "sp-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "sp-hant-hk", 10))
     {
       /* Southern Ping Chinese; Han (Traditional variant); Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -1821,7 +1830,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "sp-hant-mo"))
+    if (lang_matches (&lang_str[1], limit, "sp-hant-mo", 10))
     {
       /* Southern Ping Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -1834,7 +1843,7 @@
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "zh-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "zh-hant-hk", 10))
     {
       /* Huizhou Chinese; Han (Traditional variant); Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -1841,7 +1850,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "zh-hant-mo"))
+    if (lang_matches (&lang_str[1], limit, "zh-hant-mo", 10))
     {
       /* Huizhou Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -1854,7 +1863,7 @@
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "zo-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "zo-hant-hk", 10))
     {
       /* Min Zhong Chinese; Han (Traditional variant); Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -1861,7 +1870,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "zo-hant-mo"))
+    if (lang_matches (&lang_str[1], limit, "zo-hant-mo", 10))
     {
       /* Min Zhong Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -1874,7 +1883,7 @@
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "do-hans"))
+    if (lang_matches (&lang_str[1], limit, "do-hans", 7))
     {
       /* Min Dong Chinese; Han (Simplified variant) */
       tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
@@ -1881,7 +1890,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "do-hant"))
+    if (lang_matches (&lang_str[1], limit, "do-hant", 7))
     {
       /* Min Dong Chinese; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -1888,7 +1897,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "jy-hans"))
+    if (lang_matches (&lang_str[1], limit, "jy-hans", 7))
     {
       /* Jinyu Chinese; Han (Simplified variant) */
       tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
@@ -1895,7 +1904,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "jy-hant"))
+    if (lang_matches (&lang_str[1], limit, "jy-hant", 7))
     {
       /* Jinyu Chinese; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -1902,7 +1911,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "mn-hans"))
+    if (lang_matches (&lang_str[1], limit, "mn-hans", 7))
     {
       /* Mandarin Chinese; Han (Simplified variant) */
       tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
@@ -1909,7 +1918,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "mn-hant"))
+    if (lang_matches (&lang_str[1], limit, "mn-hant", 7))
     {
       /* Mandarin Chinese; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -1916,7 +1925,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "np-hans"))
+    if (lang_matches (&lang_str[1], limit, "np-hans", 7))
     {
       /* Northern Ping Chinese; Han (Simplified variant) */
       tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
@@ -1923,7 +1932,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "np-hant"))
+    if (lang_matches (&lang_str[1], limit, "np-hant", 7))
     {
       /* Northern Ping Chinese; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -1930,7 +1939,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "px-hans"))
+    if (lang_matches (&lang_str[1], limit, "px-hans", 7))
     {
       /* Pu-Xian Chinese; Han (Simplified variant) */
       tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
@@ -1937,7 +1946,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "px-hant"))
+    if (lang_matches (&lang_str[1], limit, "px-hant", 7))
     {
       /* Pu-Xian Chinese; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -1944,7 +1953,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "sp-hans"))
+    if (lang_matches (&lang_str[1], limit, "sp-hans", 7))
     {
       /* Southern Ping Chinese; Han (Simplified variant) */
       tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
@@ -1951,7 +1960,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "sp-hant"))
+    if (lang_matches (&lang_str[1], limit, "sp-hant", 7))
     {
       /* Southern Ping Chinese; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -1958,7 +1967,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "zh-hans"))
+    if (lang_matches (&lang_str[1], limit, "zh-hans", 7))
     {
       /* Huizhou Chinese; Han (Simplified variant) */
       tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
@@ -1965,7 +1974,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "zh-hant"))
+    if (lang_matches (&lang_str[1], limit, "zh-hant", 7))
     {
       /* Huizhou Chinese; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -1972,7 +1981,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "zo-hans"))
+    if (lang_matches (&lang_str[1], limit, "zo-hans", 7))
     {
       /* Min Zhong Chinese; Han (Simplified variant) */
       tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
@@ -1979,7 +1988,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "zo-hant"))
+    if (lang_matches (&lang_str[1], limit, "zo-hant", 7))
     {
       /* Min Zhong Chinese; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -1987,7 +1996,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "do-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Min Dong Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -1995,7 +2004,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "do-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Min Dong Chinese; Macao */
       unsigned int i;
@@ -2009,7 +2018,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "do-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Min Dong Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2017,7 +2026,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "jy-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Jinyu Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2025,7 +2034,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "jy-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Jinyu Chinese; Macao */
       unsigned int i;
@@ -2039,7 +2048,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "jy-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Jinyu Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2047,7 +2056,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "mn-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Mandarin Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2055,7 +2064,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "mn-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Mandarin Chinese; Macao */
       unsigned int i;
@@ -2069,7 +2078,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "mn-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Mandarin Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2077,7 +2086,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "np-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Northern Ping Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2085,7 +2094,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "np-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Northern Ping Chinese; Macao */
       unsigned int i;
@@ -2099,7 +2108,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "np-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Northern Ping Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2107,7 +2116,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "px-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Pu-Xian Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2115,7 +2124,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "px-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Pu-Xian Chinese; Macao */
       unsigned int i;
@@ -2129,7 +2138,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "px-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Pu-Xian Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2137,7 +2146,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "sp-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Southern Ping Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2145,7 +2154,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "sp-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Southern Ping Chinese; Macao */
       unsigned int i;
@@ -2159,7 +2168,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "sp-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Southern Ping Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2167,7 +2176,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "zh-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Huizhou Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2175,7 +2184,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "zh-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Huizhou Chinese; Macao */
       unsigned int i;
@@ -2189,7 +2198,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "zh-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Huizhou Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2197,7 +2206,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "zo-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Min Zhong Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2205,7 +2214,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "zo-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Min Zhong Chinese; Macao */
       unsigned int i;
@@ -2219,7 +2228,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "zo-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Min Zhong Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2228,7 +2237,7 @@
     }
     break;
   case 'g':
-    if (lang_matches (&lang_str[1], "an-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "an-hant-hk", 10))
     {
       /* Gan Chinese; Han (Traditional variant); Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2235,7 +2244,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "an-hant-mo"))
+    if (lang_matches (&lang_str[1], limit, "an-hant-mo", 10))
     {
       /* Gan Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -2248,7 +2257,7 @@
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "an-hans"))
+    if (lang_matches (&lang_str[1], limit, "an-hans", 7))
     {
       /* Gan Chinese; Han (Simplified variant) */
       tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
@@ -2255,7 +2264,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "an-hant"))
+    if (lang_matches (&lang_str[1], limit, "an-hant", 7))
     {
       /* Gan Chinese; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2262,7 +2271,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "a-latg"))
+    if (lang_matches (&lang_str[1], limit, "a-latg", 6))
     {
       /* Irish; Latin (Gaelic variant) */
       tags[0] = HB_TAG('I','R','T',' ');  /* Irish Traditional */
@@ -2270,7 +2279,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "an-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Gan Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2278,7 +2287,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "an-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Gan Chinese; Macao */
       unsigned int i;
@@ -2292,7 +2301,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "an-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Gan Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2301,7 +2310,7 @@
     }
     break;
   case 'h':
-    if (lang_matches (&lang_str[1], "ak-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "ak-hant-hk", 10))
     {
       /* Hakka Chinese; Han (Traditional variant); Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2308,7 +2317,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "ak-hant-mo"))
+    if (lang_matches (&lang_str[1], limit, "ak-hant-mo", 10))
     {
       /* Hakka Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -2321,7 +2330,7 @@
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "sn-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "sn-hant-hk", 10))
     {
       /* Xiang Chinese; Han (Traditional variant); Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2328,7 +2337,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "sn-hant-mo"))
+    if (lang_matches (&lang_str[1], limit, "sn-hant-mo", 10))
     {
       /* Xiang Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -2341,7 +2350,7 @@
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "ak-hans"))
+    if (lang_matches (&lang_str[1], limit, "ak-hans", 7))
     {
       /* Hakka Chinese; Han (Simplified variant) */
       tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
@@ -2348,7 +2357,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "ak-hant"))
+    if (lang_matches (&lang_str[1], limit, "ak-hant", 7))
     {
       /* Hakka Chinese; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2355,7 +2364,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "sn-hans"))
+    if (lang_matches (&lang_str[1], limit, "sn-hans", 7))
     {
       /* Xiang Chinese; Han (Simplified variant) */
       tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
@@ -2362,7 +2371,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "sn-hant"))
+    if (lang_matches (&lang_str[1], limit, "sn-hant", 7))
     {
       /* Xiang Chinese; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2370,7 +2379,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "ak-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Hakka Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2378,7 +2387,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "ak-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Hakka Chinese; Macao */
       unsigned int i;
@@ -2392,7 +2401,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "ak-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Hakka Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2400,7 +2409,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "sn-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Xiang Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2408,7 +2417,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "sn-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Xiang Chinese; Macao */
       unsigned int i;
@@ -2422,7 +2431,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "sn-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Xiang Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2460,7 +2469,7 @@
     }
     break;
   case 'l':
-    if (lang_matches (&lang_str[1], "zh-hans"))
+    if (lang_matches (&lang_str[1], limit, "zh-hans", 7))
     {
       /* Literary Chinese; Han (Simplified variant) */
       tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
@@ -2469,7 +2478,7 @@
     }
     break;
   case 'm':
-    if (lang_matches (&lang_str[1], "np-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "np-hant-hk", 10))
     {
       /* Min Bei Chinese; Han (Traditional variant); Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2476,7 +2485,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "np-hant-mo"))
+    if (lang_matches (&lang_str[1], limit, "np-hant-mo", 10))
     {
       /* Min Bei Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -2489,7 +2498,7 @@
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "np-hans"))
+    if (lang_matches (&lang_str[1], limit, "np-hans", 7))
     {
       /* Min Bei Chinese; Han (Simplified variant) */
       tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
@@ -2496,7 +2505,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "np-hant"))
+    if (lang_matches (&lang_str[1], limit, "np-hant", 7))
     {
       /* Min Bei Chinese; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2504,7 +2513,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "np-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Min Bei Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2512,7 +2521,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "np-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Min Bei Chinese; Macao */
       unsigned int i;
@@ -2526,7 +2535,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "np-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Min Bei Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2534,7 +2543,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "nw-", 3)
-	&& subtag_matches (lang_str, limit, "-th"))
+	&& subtag_matches (lang_str, limit, "-th", 3))
     {
       /* Mon; Thailand */
       tags[0] = HB_TAG('M','O','N','T');  /* Thailand Mon */
@@ -2543,7 +2552,7 @@
     }
     break;
   case 'n':
-    if (lang_matches (&lang_str[1], "an-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "an-hant-hk", 10))
     {
       /* Min Nan Chinese; Han (Traditional variant); Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2550,7 +2559,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "an-hant-mo"))
+    if (lang_matches (&lang_str[1], limit, "an-hant-mo", 10))
     {
       /* Min Nan Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -2563,7 +2572,7 @@
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "an-hans"))
+    if (lang_matches (&lang_str[1], limit, "an-hans", 7))
     {
       /* Min Nan Chinese; Han (Simplified variant) */
       tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
@@ -2570,7 +2579,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "an-hant"))
+    if (lang_matches (&lang_str[1], limit, "an-hant", 7))
     {
       /* Min Nan Chinese; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2578,7 +2587,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "an-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Min Nan Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2586,7 +2595,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "an-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Min Nan Chinese; Macao */
       unsigned int i;
@@ -2600,7 +2609,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "an-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Min Nan Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2624,7 +2633,7 @@
     break;
   case 'r':
     if (0 == strncmp (&lang_str[1], "o-", 2)
-	&& subtag_matches (lang_str, limit, "-md"))
+	&& subtag_matches (lang_str, limit, "-md", 3))
     {
       /* Romanian; Moldova */
       unsigned int i;
@@ -2639,7 +2648,7 @@
     }
     break;
   case 'w':
-    if (lang_matches (&lang_str[1], "uu-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "uu-hant-hk", 10))
     {
       /* Wu Chinese; Han (Traditional variant); Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2646,7 +2655,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "uu-hant-mo"))
+    if (lang_matches (&lang_str[1], limit, "uu-hant-mo", 10))
     {
       /* Wu Chinese; Han (Traditional variant); Macao */
       unsigned int i;
@@ -2659,7 +2668,7 @@
       *count = i;
       return true;
     }
-    if (lang_matches (&lang_str[1], "uu-hans"))
+    if (lang_matches (&lang_str[1], limit, "uu-hans", 7))
     {
       /* Wu Chinese; Han (Simplified variant) */
       tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
@@ -2666,7 +2675,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "uu-hant"))
+    if (lang_matches (&lang_str[1], limit, "uu-hant", 7))
     {
       /* Wu Chinese; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2674,7 +2683,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "uu-", 3)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Wu Chinese; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2682,7 +2691,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "uu-", 3)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Wu Chinese; Macao */
       unsigned int i;
@@ -2696,7 +2705,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "uu-", 3)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Wu Chinese; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2705,7 +2714,7 @@
     }
     break;
   case 'y':
-    if (lang_matches (&lang_str[1], "ue-hans"))
+    if (lang_matches (&lang_str[1], limit, "ue-hans", 7))
     {
       /* Yue Chinese; Han (Simplified variant) */
       tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
@@ -2714,7 +2723,7 @@
     }
     break;
   case 'z':
-    if (lang_matches (&lang_str[1], "h-hant-hk"))
+    if (lang_matches (&lang_str[1], limit, "h-hant-hk", 9))
     {
       /* Chinese [macrolanguage]; Han (Traditional variant); Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2721,7 +2730,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "h-hant-mo"))
+    if (lang_matches (&lang_str[1], limit, "h-hant-mo", 9))
     {
       /* Chinese [macrolanguage]; Han (Traditional variant); Macao */
       unsigned int i;
@@ -2741,7 +2750,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "h-hans"))
+    if (lang_matches (&lang_str[1], limit, "h-hans", 6))
     {
       /* Chinese [macrolanguage]; Han (Simplified variant) */
       tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
@@ -2748,7 +2757,7 @@
       *count = 1;
       return true;
     }
-    if (lang_matches (&lang_str[1], "h-hant"))
+    if (lang_matches (&lang_str[1], limit, "h-hant", 6))
     {
       /* Chinese [macrolanguage]; Han (Traditional variant) */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
@@ -2763,7 +2772,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "h-", 2)
-	&& subtag_matches (lang_str, limit, "-hk"))
+	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
       /* Chinese [macrolanguage]; Hong Kong */
       tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
@@ -2771,7 +2780,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "h-", 2)
-	&& subtag_matches (lang_str, limit, "-mo"))
+	&& subtag_matches (lang_str, limit, "-mo", 3))
     {
       /* Chinese [macrolanguage]; Macao */
       unsigned int i;
@@ -2785,7 +2794,7 @@
       return true;
     }
     if (0 == strncmp (&lang_str[1], "h-", 2)
-	&& subtag_matches (lang_str, limit, "-tw"))
+	&& subtag_matches (lang_str, limit, "-tw", 3))
     {
       /* Chinese [macrolanguage]; Taiwan, Province of China */
       tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-tag.cc
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-tag.cc	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-tag.cc	2022-05-21 02:44:01 UTC (rev 63353)
@@ -189,48 +189,46 @@
 
 /* hb_language_t */
 
-static bool
+static inline bool
 subtag_matches (const char *lang_str,
 		const char *limit,
-		const char *subtag)
+		const char *subtag,
+		unsigned    subtag_len)
 {
+  if (likely ((unsigned) (limit - lang_str) < subtag_len))
+    return false;
+
   do {
     const char *s = strstr (lang_str, subtag);
     if (!s || s >= limit)
       return false;
-    if (!ISALNUM (s[strlen (subtag)]))
+    if (!ISALNUM (s[subtag_len]))
       return true;
-    lang_str = s + strlen (subtag);
+    lang_str = s + subtag_len;
   } while (true);
 }
 
-static hb_bool_t
-lang_matches (const char *lang_str, const char *spec)
+static bool
+lang_matches (const char *lang_str,
+	      const char *limit,
+	      const char *spec,
+	      unsigned    spec_len)
 {
-  unsigned int len = strlen (spec);
+  if (likely ((unsigned) (limit - lang_str) < spec_len))
+    return false;
 
-  return strncmp (lang_str, spec, len) == 0 &&
-	 (lang_str[len] == '\0' || lang_str[len] == '-');
+  return strncmp (lang_str, spec, spec_len) == 0 &&
+	 (lang_str[spec_len] == '\0' || lang_str[spec_len] == '-');
 }
 
 struct LangTag
 {
-  char language[4];
+  hb_tag_t language;
   hb_tag_t tag;
 
-  int cmp (const char *a) const
+  int cmp (hb_tag_t a) const
   {
-    const char *b = this->language;
-    unsigned int da, db;
-    const char *p;
-
-    p = strchr (a, '-');
-    da = p ? (unsigned int) (p - a) : strlen (a);
-
-    p = strchr (b, '-');
-    db = p ? (unsigned int) (p - b) : strlen (b);
-
-    return strncmp (a, b, hb_max (da, db));
+    return a < this->language ? -1 : a > this->language ? +1 : 0;
   }
   int cmp (const LangTag *that) const
   { return cmp (that->language); }
@@ -266,7 +264,6 @@
 			  hb_tag_t     *tags)
 {
   const char *s;
-  unsigned int tag_idx;
 
   /* Check for matches of multiple subtags. */
   if (hb_ot_tags_from_complex_language (lang_str, limit, count, tags))
@@ -283,17 +280,39 @@
 	  ISALPHA (s[1]))
 	lang_str = s + 1;
     }
-    if (hb_sorted_array (ot_languages).bfind (lang_str, &tag_idx))
+    const LangTag *ot_languages = nullptr;
+    unsigned ot_languages_len = 0;
+    const char *dash = strchr (lang_str, '-');
+    unsigned first_len = dash ? dash - lang_str : limit - lang_str;
+    if (first_len == 2)
     {
+      ot_languages = ot_languages2;
+      ot_languages_len = ARRAY_LENGTH (ot_languages2);
+    }
+    else if (first_len == 3)
+    {
+      ot_languages = ot_languages3;
+      ot_languages_len = ARRAY_LENGTH (ot_languages3);
+    }
+
+    hb_tag_t lang_tag = hb_tag_from_string (lang_str, first_len);
+
+    static unsigned last_tag_idx; /* Poor man's cache. */
+    unsigned tag_idx = last_tag_idx;
+
+    if (likely (tag_idx < ot_languages_len && ot_languages[tag_idx].language == lang_tag) ||
+	hb_sorted_array (ot_languages, ot_languages_len).bfind (lang_tag, &tag_idx))
+    {
+      last_tag_idx = tag_idx;
       unsigned int i;
       while (tag_idx != 0 &&
-	     0 == strcmp (ot_languages[tag_idx].language, ot_languages[tag_idx - 1].language))
+	     ot_languages[tag_idx].language == ot_languages[tag_idx - 1].language)
 	tag_idx--;
       for (i = 0;
 	   i < *count &&
-	   tag_idx + i < ARRAY_LENGTH (ot_languages) &&
+	   tag_idx + i < ot_languages_len &&
 	   ot_languages[tag_idx + i].tag != HB_TAG_NONE &&
-	   0 == strcmp (ot_languages[tag_idx + i].language, ot_languages[tag_idx].language);
+	   ot_languages[tag_idx + i].language == ot_languages[tag_idx].language;
 	   i++)
 	tags[i] = ot_languages[tag_idx + i].tag;
       *count = i;
@@ -459,9 +478,19 @@
       return disambiguated_tag;
   }
 
-  for (i = 0; i < ARRAY_LENGTH (ot_languages); i++)
-    if (ot_languages[i].tag == tag)
-      return hb_language_from_string (ot_languages[i].language, -1);
+  char buf[4];
+  for (i = 0; i < ARRAY_LENGTH (ot_languages2); i++)
+    if (ot_languages2[i].tag == tag)
+    {
+      hb_tag_to_string (ot_languages2[i].language, buf);
+      return hb_language_from_string (buf, 2);
+    }
+  for (i = 0; i < ARRAY_LENGTH (ot_languages3); i++)
+    if (ot_languages3[i].tag == tag)
+    {
+      hb_tag_to_string (ot_languages3[i].language, buf);
+      return hb_language_from_string (buf, 3);
+    }
 
   /* Return a custom language in the form of "x-hbot-AABBCCDD".
    * If it's three letters long, also guess it's ISO 639-3 and lower-case and
@@ -557,16 +586,26 @@
 static inline void
 test_langs_sorted ()
 {
-  for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages); i++)
+  for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages2); i++)
   {
-    int c = ot_languages[i].cmp (&ot_languages[i - 1]);
+    int c = ot_languages2[i].cmp (&ot_languages2[i - 1]);
     if (c > 0)
     {
-      fprintf (stderr, "ot_languages not sorted at index %d: %s %d %s\n",
-	       i, ot_languages[i-1].language, c, ot_languages[i].language);
+      fprintf (stderr, "ot_languages2 not sorted at index %d: %08x %d %08x\n",
+	       i, ot_languages2[i-1].language, c, ot_languages2[i].language);
       abort();
     }
   }
+  for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages3); i++)
+  {
+    int c = ot_languages3[i].cmp (&ot_languages3[i - 1]);
+    if (c > 0)
+    {
+      fprintf (stderr, "ot_languages3 not sorted at index %d: %08x %d %08x\n",
+	       i, ot_languages3[i-1].language, c, ot_languages3[i].language);
+      abort();
+    }
+  }
 }
 
 int

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-gvar-table.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-gvar-table.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-var-gvar-table.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -390,13 +390,10 @@
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && (version.major == 1) &&
-		  (glyphCount == c->get_num_glyphs ()) &&
 		  sharedTuples.sanitize (c, this, axisCount * sharedTupleCount) &&
 		  (is_long_offset () ?
 		     c->check_array (get_long_offset_array (), glyphCount+1) :
-		     c->check_array (get_short_offset_array (), glyphCount+1)) &&
-		  c->check_array (((const HBUINT8*)&(this+dataZ)) + get_offset (0),
-				  get_offset (glyphCount) - get_offset (0)));
+		     c->check_array (get_short_offset_array (), glyphCount+1)));
   }
 
   /* GlyphVariationData not sanitized here; must be checked while accessing each glyph variation data */
@@ -482,7 +479,9 @@
   const hb_bytes_t get_glyph_var_data_bytes (hb_blob_t *blob, hb_codepoint_t glyph) const
   {
     unsigned start_offset = get_offset (glyph);
-    unsigned length = get_offset (glyph+1) - start_offset;
+    unsigned end_offset = get_offset (glyph+1);
+    if (unlikely (end_offset < start_offset)) return hb_bytes_t ();
+    unsigned length = end_offset - start_offset;
     hb_bytes_t var_data = blob->as_bytes ().sub_array (((unsigned) dataZ) + start_offset, length);
     return likely (var_data.length >= GlyphVariationData::min_size) ? var_data : hb_bytes_t ();
   }
@@ -490,7 +489,10 @@
   bool is_long_offset () const { return flags & 1; }
 
   unsigned get_offset (unsigned i) const
-  { return is_long_offset () ? get_long_offset_array ()[i] : get_short_offset_array ()[i] * 2; }
+  {
+    if (unlikely (i > glyphCount)) return 0;
+    return is_long_offset () ? get_long_offset_array ()[i] : get_short_offset_array ()[i] * 2;
+  }
 
   const HBUINT32 * get_long_offset_array () const { return (const HBUINT32 *) &offsetZ; }
   const HBUINT16 *get_short_offset_array () const { return (const HBUINT16 *) &offsetZ; }
@@ -696,7 +698,7 @@
 		offsetZ;	/* Offsets from the start of the GlyphVariationData array
 				 * to each GlyphVariationData table. */
   public:
-  DEFINE_SIZE_MIN (20);
+  DEFINE_SIZE_ARRAY (20, offsetZ);
 };
 
 struct gvar_accelerator_t : gvar::accelerator_t {

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-priority-queue.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-priority-queue.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-priority-queue.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -38,19 +38,12 @@
  */
 struct hb_priority_queue_t
 {
-  HB_DELETE_COPY_ASSIGN (hb_priority_queue_t);
-  hb_priority_queue_t ()  { init (); }
-  ~hb_priority_queue_t () { fini (); }
-
  private:
   typedef hb_pair_t<int64_t, unsigned> item_t;
   hb_vector_t<item_t> heap;
 
  public:
-  void init () { heap.init (); }
 
-  void fini () { heap.fini (); }
-
   void reset () { heap.resize (0); }
 
   bool in_error () const { return heap.in_error (); }
@@ -58,14 +51,17 @@
   void insert (int64_t priority, unsigned value)
   {
     heap.push (item_t (priority, value));
+    if (unlikely (heap.in_error ())) return;
     bubble_up (heap.length - 1);
   }
 
   item_t pop_minimum ()
   {
-    item_t result = heap[0];
+    assert (!is_empty ());
 
-    heap[0] = heap[heap.length - 1];
+    item_t result = heap.arrayZ[0];
+
+    heap.arrayZ[0] = heap.arrayZ[heap.length - 1];
     heap.shrink (heap.length - 1);
     bubble_down (0);
 
@@ -104,6 +100,8 @@
 
   void bubble_down (unsigned index)
   {
+    assert (index <= heap.length);
+
     unsigned left = left_child (index);
     unsigned right = right_child (index);
 
@@ -113,11 +111,11 @@
       return;
 
     bool has_right = right < heap.length;
-    if (heap[index].first <= heap[left].first
-        && (!has_right || heap[index].first <= heap[right].first))
+    if (heap.arrayZ[index].first <= heap.arrayZ[left].first
+        && (!has_right || heap[index].first <= heap.arrayZ[right].first))
       return;
 
-    if (!has_right || heap[left].first < heap[right].first)
+    if (!has_right || heap.arrayZ[left].first < heap.arrayZ[right].first)
     {
       swap (index, left);
       bubble_down (left);
@@ -130,10 +128,12 @@
 
   void bubble_up (unsigned index)
   {
+    assert (index <= heap.length);
+
     if (index == 0) return;
 
     unsigned parent_index = parent (index);
-    if (heap[parent_index].first <= heap[index].first)
+    if (heap.arrayZ[parent_index].first <= heap.arrayZ[index].first)
       return;
 
     swap (index, parent_index);
@@ -142,9 +142,9 @@
 
   void swap (unsigned a, unsigned b)
   {
-    item_t temp = heap[a];
-    heap[a] = heap[b];
-    heap[b] = temp;
+    assert (a <= heap.length);
+    assert (b <= heap.length);
+    hb_swap (heap.arrayZ[a], heap.arrayZ[b]);
   }
 };
 

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-repacker.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-repacker.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-repacker.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -49,6 +49,17 @@
     unsigned end = 0;
     unsigned priority = 0;
 
+    friend void swap (vertex_t& a, vertex_t& b)
+    {
+      hb_swap (a.obj, b.obj);
+      hb_swap (a.distance, b.distance);
+      hb_swap (a.space, b.space);
+      hb_swap (a.parents, b.parents);
+      hb_swap (a.start, b.start);
+      hb_swap (a.end, b.end);
+      hb_swap (a.priority, b.priority);
+    }
+
     bool is_shared () const
     {
       return parents.length > 1;
@@ -148,6 +159,8 @@
   {
     num_roots_for_space_.push (1);
     bool removed_nil = false;
+    vertices_.alloc (objects.length);
+    vertices_scratch_.alloc (objects.length);
     for (unsigned i = 0; i < objects.length; i++)
     {
       // TODO(grieger): check all links point to valid objects.
@@ -247,59 +260,6 @@
   }
 
   /*
-   * Generates a new topological sorting of graph using Kahn's
-   * algorithm: https://en.wikipedia.org/wiki/Topological_sorting#Algorithms
-   */
-  void sort_kahn ()
-  {
-    positions_invalid = true;
-
-    if (vertices_.length <= 1) {
-      // Graph of 1 or less doesn't need sorting.
-      return;
-    }
-
-    hb_vector_t<unsigned> queue;
-    hb_vector_t<vertex_t> sorted_graph;
-    if (unlikely (!check_success (sorted_graph.resize (vertices_.length)))) return;
-    hb_vector_t<unsigned> id_map;
-    if (unlikely (!check_success (id_map.resize (vertices_.length)))) return;
-
-    hb_vector_t<unsigned> removed_edges;
-    if (unlikely (!check_success (removed_edges.resize (vertices_.length)))) return;
-    update_parents ();
-
-    queue.push (root_idx ());
-    int new_id = vertices_.length - 1;
-
-    while (!queue.in_error () && queue.length)
-    {
-      unsigned next_id = queue[0];
-      queue.remove (0);
-
-      vertex_t& next = vertices_[next_id];
-      sorted_graph[new_id] = next;
-      id_map[next_id] = new_id--;
-
-      for (const auto& link : next.obj.all_links ()) {
-        removed_edges[link.objidx]++;
-        if (!(vertices_[link.objidx].incoming_edges () - removed_edges[link.objidx]))
-          queue.push (link.objidx);
-      }
-    }
-
-    check_success (!queue.in_error ());
-    check_success (!sorted_graph.in_error ());
-    if (!check_success (new_id == -1))
-      print_orphaned_nodes ();
-
-    remap_all_obj_indices (id_map, &sorted_graph);
-
-    hb_swap (vertices_, sorted_graph);
-    sorted_graph.fini ();
-  }
-
-  /*
    * Generates a new topological sorting of graph ordered by the shortest
    * distance to each node.
    */
@@ -315,7 +275,7 @@
     update_distances ();
 
     hb_priority_queue_t queue;
-    hb_vector_t<vertex_t> sorted_graph;
+    hb_vector_t<vertex_t> &sorted_graph = vertices_scratch_;
     if (unlikely (!check_success (sorted_graph.resize (vertices_.length)))) return;
     hb_vector_t<unsigned> id_map;
     if (unlikely (!check_success (id_map.resize (vertices_.length)))) return;
@@ -331,8 +291,9 @@
     {
       unsigned next_id = queue.pop_minimum().second;
 
-      vertex_t& next = vertices_[next_id];
-      sorted_graph[new_id] = next;
+      hb_swap (sorted_graph[new_id], vertices_[next_id]);
+      const vertex_t& next = sorted_graph[new_id];
+
       id_map[next_id] = new_id--;
 
       for (const auto& link : next.obj.all_links ()) {
@@ -356,7 +317,6 @@
     remap_all_obj_indices (id_map, &sorted_graph);
 
     hb_swap (vertices_, sorted_graph);
-    sorted_graph.fini ();
   }
 
   /*
@@ -568,12 +528,10 @@
     // The last object is the root of the graph, so swap back the root to the end.
     // The root's obj idx does change, however since it's root nothing else refers to it.
     // all other obj idx's will be unaffected.
-    vertex_t root = vertices_[vertices_.length - 2];
-    vertices_[clone_idx] = *clone;
-    vertices_[vertices_.length - 1] = root;
+    hb_swap (vertices_[vertices_.length - 2], *clone);
 
     // Since the root moved, update the parents arrays of all children on the root.
-    for (const auto& l : root.obj.all_links ())
+    for (const auto& l : root ().obj.all_links ())
       vertices_[l.objidx].remap_parent (root_idx () - 1, root_idx ());
 
     return clone_idx;
@@ -1090,6 +1048,7 @@
  public:
   // TODO(garretrieger): make private, will need to move most of offset overflow code into graph.
   hb_vector_t<vertex_t> vertices_;
+  hb_vector_t<vertex_t> vertices_scratch_;
  private:
   bool parents_invalid;
   bool distance_invalid;
@@ -1217,7 +1176,6 @@
   // Kahn sort is ~twice as fast as shortest distance sort and works for many fonts
   // so try it first to save time.
   graph_t sorted_graph (packed);
-  sorted_graph.sort_kahn ();
   if (!sorted_graph.will_overflow ())
   {
     return sorted_graph.serialize ();

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-serialize.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-serialize.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-serialize.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -74,7 +74,7 @@
     }
 
     object_t () = default;
-    
+
 #ifdef HB_EXPERIMENTAL_API
     object_t (const hb_object_t &o)
     {
@@ -91,6 +91,15 @@
     }
 #endif
 
+    friend void swap (object_t& a, object_t& b)
+    {
+      hb_swap (a.head, b.head);
+      hb_swap (a.tail, b.tail);
+      hb_swap (a.next, b.next);
+      hb_swap (a.real_links, b.real_links);
+      hb_swap (a.virtual_links, b.virtual_links);
+    }
+
     bool operator == (const object_t &o) const
     {
       // Virtual links aren't considered for equality since they don't affect the functionality
@@ -111,10 +120,10 @@
     struct link_t
     {
       unsigned width: 3;
-      bool is_signed: 1;
+      unsigned is_signed: 1;
       unsigned whence: 2;
-      unsigned position: 28;
-      unsigned bias;
+      unsigned bias : 26;
+      unsigned position;
       objidx_t objidx;
 
       link_t () = default;

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-set.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-set.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-set.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -43,8 +43,8 @@
 
   hb_sparseset_t (const hb_sparseset_t& other) : hb_sparseset_t () { set (other); }
   hb_sparseset_t (hb_sparseset_t&& other) : hb_sparseset_t () { s = std::move (other.s); }
-  hb_sparseset_t& operator= (const hb_sparseset_t& other) { set (other); return *this; }
-  hb_sparseset_t& operator= (hb_sparseset_t&& other) { hb_swap (*this, other); return *this; }
+  hb_sparseset_t& operator = (const hb_sparseset_t& other) { set (other); return *this; }
+  hb_sparseset_t& operator = (hb_sparseset_t&& other) { s = std::move (other.s); return *this; }
   friend void swap (hb_sparseset_t& a, hb_sparseset_t& b) { hb_swap (a.s, b.s); }
 
   hb_sparseset_t (std::initializer_list<hb_codepoint_t> lst) : hb_sparseset_t ()
@@ -53,7 +53,7 @@
       add (item);
   }
   template <typename Iterable,
-	    hb_requires (hb_is_iterable (Iterable))>
+           hb_requires (hb_is_iterable (Iterable))>
   hb_sparseset_t (const Iterable &o) : hb_sparseset_t ()
   {
     hb_copy (o, *this);
@@ -77,10 +77,12 @@
   void err () { s.err (); }
   bool in_error () const { return s.in_error (); }
 
+  void alloc (unsigned sz) { s.alloc (sz); }
   void reset () { s.reset (); }
   void clear () { s.clear (); }
   void invert () { s.invert (); }
   bool is_empty () const { return s.is_empty (); }
+  uint32_t hash () const { return s.hash (); }
 
   void add (hb_codepoint_t g) { s.add (g); }
   bool add_range (hb_codepoint_t a, hb_codepoint_t b) { return s.add_range (a, b); }
@@ -125,6 +127,8 @@
   void set (const hb_sparseset_t &other) { s.set (other.s); }
 
   bool is_equal (const hb_sparseset_t &other) const { return s.is_equal (other.s); }
+  bool operator == (const hb_set_t &other) const { return is_equal (other); }
+  bool operator != (const hb_set_t &other) const { return !is_equal (other); }
 
   bool is_subset (const hb_sparseset_t &larger_set) const { return s.is_subset (larger_set.s); }
 
@@ -158,15 +162,19 @@
 
 struct hb_set_t : hb_sparseset_t<hb_bit_set_invertible_t>
 {
-  hb_set_t () = default;
+  using sparseset = hb_sparseset_t<hb_bit_set_invertible_t>;
+
   ~hb_set_t () = default;
-  hb_set_t (hb_set_t&) = default;
-  hb_set_t& operator= (const hb_set_t&) = default;
-  hb_set_t& operator= (hb_set_t&&) = default;
-  hb_set_t (std::initializer_list<hb_codepoint_t> lst) : hb_sparseset_t<hb_bit_set_invertible_t> (lst) {}
+  hb_set_t () : sparseset () {};
+  hb_set_t (std::nullptr_t) : hb_set_t () {};
+  hb_set_t (const hb_set_t &o) : sparseset ((sparseset &) o) {};
+  hb_set_t (hb_set_t&& o) : sparseset (std::move ((sparseset &) o)) {}
+  hb_set_t& operator = (const hb_set_t&) = default;
+  hb_set_t& operator = (hb_set_t&&) = default;
+  hb_set_t (std::initializer_list<hb_codepoint_t> lst) : sparseset (lst) {}
   template <typename Iterable,
 	    hb_requires (hb_is_iterable (Iterable))>
-  hb_set_t (const Iterable &o) : hb_sparseset_t<hb_bit_set_invertible_t> (o) {}
+  hb_set_t (const Iterable &o) : sparseset (o) {}
 };
 
 static_assert (hb_set_t::INVALID == HB_SET_VALUE_INVALID, "");

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff-common.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff-common.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff-common.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -40,7 +40,7 @@
   str_encoder_t (str_buff_t &buff_)
     : buff (buff_), error (false) {}
 
-  void reset () { buff.resize (0); }
+  void reset () { buff.reset (); }
 
   void encode_byte (unsigned char b)
   {
@@ -107,20 +107,18 @@
       encode_byte (op);
   }
 
-  void copy_str (const byte_str_t &str)
+  void copy_str (const hb_ubytes_t &str)
   {
     unsigned int  offset = buff.length;
-    if (unlikely (!buff.resize (offset + str.length)))
+    /* Manually resize buffer since faster. */
+    if ((signed) (buff.length + str.length) <= buff.allocated)
+      buff.length += str.length;
+    else if (unlikely (!buff.resize (offset + str.length)))
     {
       set_error ();
       return;
     }
-    if (unlikely (buff.length < offset + str.length))
-    {
-      set_error ();
-      return;
-    }
-    memcpy (&buff[offset], &str[0], str.length);
+    memcpy (buff.arrayZ + offset, &str[0], str.length);
   }
 
   bool is_error () const { return error; }
@@ -253,12 +251,12 @@
 	if (endchar_op != OpCode_Invalid) flat_charstrings[i].push (endchar_op);
 	continue;
       }
-      const byte_str_t str = (*acc.charStrings)[glyph];
+      const hb_ubytes_t str = (*acc.charStrings)[glyph];
       unsigned int fd = acc.fdSelect->get_fd (glyph);
       if (unlikely (fd >= acc.fdCount))
 	return false;
-      cs_interpreter_t<ENV, OPSET, flatten_param_t> interp;
-      interp.env.init (str, acc, fd);
+      ENV env (str, acc, fd);
+      cs_interpreter_t<ENV, OPSET, flatten_param_t> interp (env);
       flatten_param_t  param = {
         flat_charstrings[i],
         (bool) (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
@@ -317,9 +315,9 @@
   unsigned int  subr_num;
 
   protected:
-  bool	  drop_flag : 1;
-  bool	  keep_flag : 1;
-  bool	  skip_flag : 1;
+  bool	  drop_flag;
+  bool	  keep_flag;
+  bool	  skip_flag;
 };
 
 struct parsed_cs_str_t : parsed_values_t<parsed_cs_op_t>
@@ -398,19 +396,19 @@
 
 struct subr_subset_param_t
 {
-  void init (parsed_cs_str_t *parsed_charstring_,
-	     parsed_cs_str_vec_t *parsed_global_subrs_, parsed_cs_str_vec_t *parsed_local_subrs_,
-	     hb_set_t *global_closure_, hb_set_t *local_closure_,
-	     bool drop_hints_)
-  {
-    parsed_charstring = parsed_charstring_;
-    current_parsed_str = parsed_charstring;
-    parsed_global_subrs = parsed_global_subrs_;
-    parsed_local_subrs = parsed_local_subrs_;
-    global_closure = global_closure_;
-    local_closure = local_closure_;
-    drop_hints = drop_hints_;
-  }
+  subr_subset_param_t (parsed_cs_str_t *parsed_charstring_,
+		       parsed_cs_str_vec_t *parsed_global_subrs_,
+		       parsed_cs_str_vec_t *parsed_local_subrs_,
+		       hb_set_t *global_closure_,
+		       hb_set_t *local_closure_,
+		       bool drop_hints_) :
+      current_parsed_str (parsed_charstring_),
+      parsed_charstring (parsed_charstring_),
+      parsed_global_subrs (parsed_global_subrs_),
+      parsed_local_subrs (parsed_local_subrs_),
+      global_closure (global_closure_),
+      local_closure (local_closure_),
+      drop_hints (drop_hints_) {}
 
   parsed_cs_str_t *get_parsed_str_for_context (call_context_t &context)
   {
@@ -468,6 +466,7 @@
      * no optimization based on usage counts. fonttools doesn't appear doing that either.
      */
 
+    resize (closure->get_population ());
     hb_codepoint_t old_num = HB_SET_VALUE_INVALID;
     while (hb_set_next (closure, &old_num))
       add (old_num);
@@ -561,19 +560,21 @@
       hb_codepoint_t  glyph;
       if (!plan->old_gid_for_new_gid (i, &glyph))
 	continue;
-      const byte_str_t str = (*acc.charStrings)[glyph];
+      const hb_ubytes_t str = (*acc.charStrings)[glyph];
       unsigned int fd = acc.fdSelect->get_fd (glyph);
       if (unlikely (fd >= acc.fdCount))
 	return false;
 
-      cs_interpreter_t<ENV, OPSET, subr_subset_param_t> interp;
-      interp.env.init (str, acc, fd);
+      ENV env (str, acc, fd);
+      cs_interpreter_t<ENV, OPSET, subr_subset_param_t> interp (env);
 
-      subr_subset_param_t  param;
-      param.init (&parsed_charstrings[i],
-		  &parsed_global_subrs,  &parsed_local_subrs[fd],
-		  &closures.global_closure, &closures.local_closures[fd],
-		  plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
+      parsed_charstrings[i].alloc (str.length);
+      subr_subset_param_t  param (&parsed_charstrings[i],
+				  &parsed_global_subrs,
+				  &parsed_local_subrs[fd],
+				  &closures.global_closure,
+				  &closures.local_closures[fd],
+				  plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
 
       if (unlikely (!interp.interpret (param)))
 	return false;
@@ -593,11 +594,12 @@
 	unsigned int fd = acc.fdSelect->get_fd (glyph);
 	if (unlikely (fd >= acc.fdCount))
 	  return false;
-	subr_subset_param_t  param;
-	param.init (&parsed_charstrings[i],
-		    &parsed_global_subrs,  &parsed_local_subrs[fd],
-		    &closures.global_closure, &closures.local_closures[fd],
-                    plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
+	subr_subset_param_t  param (&parsed_charstrings[i],
+				    &parsed_global_subrs,
+				    &parsed_local_subrs[fd],
+				    &closures.global_closure,
+				    &closures.local_closures[fd],
+				    plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
 
 	drop_hints_param_t  drop;
 	if (drop_hints_in_str (parsed_charstrings[i], param, drop))
@@ -618,11 +620,12 @@
 	unsigned int fd = acc.fdSelect->get_fd (glyph);
 	if (unlikely (fd >= acc.fdCount))
 	  return false;
-	subr_subset_param_t  param;
-	param.init (&parsed_charstrings[i],
-		    &parsed_global_subrs,  &parsed_local_subrs[fd],
-		    &closures.global_closure, &closures.local_closures[fd],
-                    plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
+	subr_subset_param_t  param (&parsed_charstrings[i],
+				    &parsed_global_subrs,
+				    &parsed_local_subrs[fd],
+				    &closures.global_closure,
+				    &closures.local_closures[fd],
+				    plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
 	collect_subr_refs_in_str (parsed_charstrings[i], param);
       }
     }
@@ -849,9 +852,10 @@
 
   bool encode_str (const parsed_cs_str_t &str, const unsigned int fd, str_buff_t &buff) const
   {
-    buff.init ();
+    unsigned count = str.get_count ();
     str_encoder_t  encoder (buff);
     encoder.reset ();
+    buff.alloc (count * 3);
     /* if a prefix (CFF1 width or CFF2 vsindex) has been removed along with hints,
      * re-insert it at the beginning of charstreing */
     if (str.has_prefix () && str.is_hint_dropped ())
@@ -860,7 +864,7 @@
       if (str.prefix_op () != OpCode_Invalid)
 	encoder.encode_op (str.prefix_op ());
     }
-    for (unsigned int i = 0; i < str.get_count(); i++)
+    for (unsigned int i = 0; i < count; i++)
     {
       const parsed_cs_op_t  &opstr = str.values[i];
       if (!opstr.for_drop () && !opstr.for_skip ())

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff1.cc
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff1.cc	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff1.cc	2022-05-21 02:44:01 UTC (rev 63353)
@@ -169,7 +169,7 @@
 	  supp_op.op = op;
 	  if ( unlikely (!(opstr.str.length >= opstr.last_arg_offset + 3)))
 	    return_trace (false);
-	  supp_op.str = byte_str_t (&opstr.str + opstr.last_arg_offset, opstr.str.length - opstr.last_arg_offset);
+	  supp_op.str = hb_ubytes_t (&opstr.str + opstr.last_arg_offset, opstr.str.length - opstr.last_arg_offset);
 	  return_trace (UnsizedByteStr::serialize_int2 (c, mod.nameSIDs[name_dict_values_t::registry]) &&
 			UnsizedByteStr::serialize_int2 (c, mod.nameSIDs[name_dict_values_t::ordering]) &&
 			copy_opstr (c, supp_op));
@@ -270,13 +270,13 @@
   /* replace the first glyph ID in the "glyph" field each range with a nLeft value */
   bool complete (unsigned int last_glyph)
   {
-    bool  two_byte = false;
-    for (unsigned int i = (*this).length; i > 0; i--)
+    bool two_byte = false;
+    unsigned count = this->length;
+    for (unsigned int i = count; i; i--)
     {
-      code_pair_t &pair = (*this)[i - 1];
-      unsigned int  nLeft = last_glyph - pair.glyph - 1;
-      if (nLeft >= 0x100)
-	two_byte = true;
+      code_pair_t &pair = arrayZ[i - 1];
+      unsigned int nLeft = last_glyph - pair.glyph - 1;
+      two_byte |= nLeft >= 0x100;
       last_glyph = pair.glyph;
       pair.glyph = nLeft;
     }
@@ -442,6 +442,9 @@
       return;
     }
 
+    bool use_glyph_to_sid_map = plan->num_output_glyphs () > plan->source->get_num_glyphs () / 8.;
+    hb_map_t *glyph_to_sid_map = use_glyph_to_sid_map ? acc.create_glyph_to_sid_map () : nullptr;
+
     unsigned int glyph;
     for (glyph = 1; glyph < plan->num_output_glyphs (); glyph++)
     {
@@ -451,7 +454,7 @@
 	/* Retain the SID for the old missing glyph ID */
 	old_glyph = glyph;
       }
-      sid = acc.glyph_to_sid (old_glyph);
+      sid = glyph_to_sid_map ? glyph_to_sid_map->get (old_glyph) : acc.glyph_to_sid (old_glyph);
 
       if (!acc.is_CID ())
 	sid = sidmap.add (sid);
@@ -464,6 +467,9 @@
       last_sid = sid;
     }
 
+    if (glyph_to_sid_map)
+      hb_map_destroy (glyph_to_sid_map);
+
     bool two_byte = subset_charset_ranges.complete (glyph);
 
     size0 = Charset0::min_size + HBUINT16::static_size * (plan->num_output_glyphs () - 1);

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff2.cc
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff2.cc	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-cff2.cc	2022-05-21 02:44:01 UTC (rev 63353)
@@ -67,9 +67,9 @@
   }
 };
 
-struct cff2_cs_opset_flatten_t : cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t>
+struct cff2_cs_opset_flatten_t : cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t, blend_arg_t>
 {
-  static void flush_args_and_op (op_code_t op, cff2_cs_interp_env_t &env, flatten_param_t& param)
+  static void flush_args_and_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
   {
     switch (op)
     {
@@ -97,7 +97,7 @@
     }
   }
 
-  static void flush_args (cff2_cs_interp_env_t &env, flatten_param_t& param)
+  static void flush_args (cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
   {
     for (unsigned int i = 0; i < env.argStack.get_count ();)
     {
@@ -122,7 +122,7 @@
     SUPER::flush_args (env, param);
   }
 
-  static void flatten_blends (const blend_arg_t &arg, unsigned int i, cff2_cs_interp_env_t &env, flatten_param_t& param)
+  static void flatten_blends (const blend_arg_t &arg, unsigned int i, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
   {
     /* flatten the default values */
     str_encoder_t  encoder (param.flatStr);
@@ -149,7 +149,7 @@
     encoder.encode_op (OpCode_blendcs);
   }
 
-  static void flush_op (op_code_t op, cff2_cs_interp_env_t &env, flatten_param_t& param)
+  static void flush_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
   {
     switch (op)
     {
@@ -163,13 +163,13 @@
   }
 
   private:
-  typedef cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t> SUPER;
-  typedef cs_opset_t<blend_arg_t, cff2_cs_opset_flatten_t, cff2_cs_opset_flatten_t, cff2_cs_interp_env_t, flatten_param_t> CSOPSET;
+  typedef cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t, blend_arg_t> SUPER;
+  typedef cs_opset_t<blend_arg_t, cff2_cs_opset_flatten_t, cff2_cs_opset_flatten_t, cff2_cs_interp_env_t<blend_arg_t>, flatten_param_t> CSOPSET;
 };
 
-struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t>
+struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t, blend_arg_t>
 {
-  static void process_op (op_code_t op, cff2_cs_interp_env_t &env, subr_subset_param_t& param)
+  static void process_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param)
   {
     switch (op) {
 
@@ -201,7 +201,7 @@
 
   protected:
   static void process_call_subr (op_code_t op, cs_type_t type,
-				 cff2_cs_interp_env_t &env, subr_subset_param_t& param,
+				 cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param,
 				 cff2_biased_subrs_t& subrs, hb_set_t *closure)
   {
     byte_str_ref_t    str_ref = env.str_ref;
@@ -212,15 +212,15 @@
   }
 
   private:
-  typedef cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t> SUPER;
+  typedef cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t, blend_arg_t> SUPER;
 };
 
-struct cff2_subr_subsetter_t : subr_subsetter_t<cff2_subr_subsetter_t, CFF2Subrs, const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t, cff2_cs_opset_subr_subset_t>
+struct cff2_subr_subsetter_t : subr_subsetter_t<cff2_subr_subsetter_t, CFF2Subrs, const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t<blend_arg_t>, cff2_cs_opset_subr_subset_t>
 {
   cff2_subr_subsetter_t (const OT::cff2::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_)
     : subr_subsetter_t (acc_, plan_) {}
 
-  static void complete_parsed_str (cff2_cs_interp_env_t &env, subr_subset_param_t& param, parsed_cs_str_t &charstring)
+  static void complete_parsed_str (cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param, parsed_cs_str_t &charstring)
   {
     /* vsindex is inserted at the beginning of the charstring as necessary */
     if (env.seen_vsindex ())
@@ -245,7 +245,7 @@
     if (desubroutinize)
     {
       /* Flatten global & local subrs */
-      subr_flattener_t<const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t, cff2_cs_opset_flatten_t>
+      subr_flattener_t<const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t<blend_arg_t>, cff2_cs_opset_flatten_t>
 		    flattener(acc, plan);
       if (!flattener.flatten (subset_charstrings))
 	return false;

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-plan.cc
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-plan.cc	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-plan.cc	2022-05-21 02:44:01 UTC (rev 63353)
@@ -279,12 +279,7 @@
 _remove_invalid_gids (hb_set_t *glyphs,
 		      unsigned int num_glyphs)
 {
-  hb_codepoint_t gid = HB_SET_VALUE_INVALID;
-  while (glyphs->next (&gid))
-  {
-    if (gid >= num_glyphs)
-      glyphs->del (gid);
-  }
+  glyphs->del_range (num_glyphs, HB_SET_VALUE_INVALID);
 }
 
 static void
@@ -294,12 +289,13 @@
 {
   OT::cmap::accelerator_t cmap (plan->source);
 
-  constexpr static const int size_threshold = 4096;
-
+  unsigned size_threshold = plan->source->get_num_glyphs ();
   if (glyphs->is_empty () && unicodes->get_population () < size_threshold)
   {
-    /* This is the fast path if it's anticipated that size of unicodes
-     * is << than the number of codepoints in the font. */
+    // This is approach to collection is faster, but can only be used  if glyphs
+    // are not being explicitly added to the subset and the input unicodes set is
+    // not excessively large (eg. an inverted set).
+    plan->unicode_to_new_gid_list.alloc (unicodes->get_population ());
     for (hb_codepoint_t cp : *unicodes)
     {
       hb_codepoint_t gid;
@@ -310,27 +306,32 @@
       }
 
       plan->codepoint_to_glyph->set (cp, gid);
+      plan->unicode_to_new_gid_list.push (hb_pair (cp, gid));
     }
   }
   else
   {
+    // This approach is slower, but can handle adding in glyphs to the subset and will match
+    // them with cmap entries.
     hb_map_t unicode_glyphid_map;
-    cmap.collect_mapping (hb_set_get_empty (), &unicode_glyphid_map);
+    hb_set_t cmap_unicodes;
+    cmap.collect_mapping (&cmap_unicodes, &unicode_glyphid_map);
+    plan->unicode_to_new_gid_list.alloc (hb_min(unicodes->get_population ()
+                                                + glyphs->get_population (),
+                                                cmap_unicodes.get_population ()));
 
-    for (hb_pair_t<hb_codepoint_t, hb_codepoint_t> cp_gid :
-	 + unicode_glyphid_map.iter ())
+    for (hb_codepoint_t cp : cmap_unicodes)
     {
-      if (!unicodes->has (cp_gid.first) && !glyphs->has (cp_gid.second))
-	continue;
+      hb_codepoint_t gid = unicode_glyphid_map[cp];
+      if (!unicodes->has (cp) && !glyphs->has (gid))
+        continue;
 
-      plan->codepoint_to_glyph->set (cp_gid.first, cp_gid.second);
+      plan->codepoint_to_glyph->set (cp, gid);
+      plan->unicode_to_new_gid_list.push (hb_pair (cp, gid));
     }
 
     /* Add gids which where requested, but not mapped in cmap */
-    // TODO(garretrieger):
-    // Once https://github.com/harfbuzz/harfbuzz/issues/3169
-    // is implemented, this can be done with union and del_range
-    for (hb_codepoint_t gid : glyphs->iter ())
+    for (hb_codepoint_t gid : *glyphs)
     {
       if (gid >= plan->source->get_num_glyphs ())
 	break;
@@ -338,8 +339,12 @@
     }
   }
 
-  + plan->codepoint_to_glyph->keys ()   | hb_sink (plan->unicodes);
-  + plan->codepoint_to_glyph->values () | hb_sink (plan->_glyphset_gsub);
+  auto &arr = plan->unicode_to_new_gid_list;
+  if (arr.length)
+  {
+    plan->unicodes->add_sorted_array (&arr.arrayZ->first, arr.length, sizeof (*arr.arrayZ));
+    plan->_glyphset_gsub->add_array (&arr.arrayZ->second, arr.length, sizeof (*arr.arrayZ));
+  }
 }
 
 static void
@@ -388,16 +393,19 @@
   _remove_invalid_gids (&cur_glyphset, plan->source->get_num_glyphs ());
 
   hb_set_set (plan->_glyphset_colred, &cur_glyphset);
-  // Populate a full set of glyphs to retain by adding all referenced
-  // composite glyphs.
-  for (hb_codepoint_t gid : cur_glyphset.iter ())
-  {
-    glyf.add_gid_and_children (gid, plan->_glyphset);
+
+  /* Populate a full set of glyphs to retain by adding all referenced
+   * composite glyphs. */
+  if (glyf.has_data ())
+    for (hb_codepoint_t gid : cur_glyphset)
+      glyf.add_gid_and_children (gid, plan->_glyphset);
+  else
+    plan->_glyphset->union_ (cur_glyphset);
 #ifndef HB_NO_SUBSET_CFF
-    if (cff.is_valid ())
+  if (cff.is_valid ())
+    for (hb_codepoint_t gid : cur_glyphset)
       _add_cff_seac_components (cff, gid, plan->_glyphset);
 #endif
-  }
 
   _remove_invalid_gids (plan->_glyphset, plan->source->get_num_glyphs ());
 
@@ -413,6 +421,20 @@
 }
 
 static void
+_create_glyph_map_gsub (const hb_set_t* glyph_set_gsub,
+                        const hb_map_t* glyph_map,
+                        hb_map_t* out)
+{
+  + hb_iter (glyph_set_gsub)
+  | hb_map ([&] (hb_codepoint_t gid) {
+    return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid,
+                                                      glyph_map->get (gid));
+  })
+  | hb_sink (out)
+  ;
+}
+
+static void
 _create_old_gid_to_new_gid_map (const hb_face_t *face,
 				bool		 retain_gids,
 				const hb_set_t	*all_gids_to_retain,
@@ -420,6 +442,10 @@
 				hb_map_t	*reverse_glyph_map, /* OUT */
 				unsigned int	*num_glyphs /* OUT */)
 {
+  unsigned pop = all_gids_to_retain->get_population ();
+  reverse_glyph_map->resize (pop);
+  glyph_map->resize (pop);
+
   if (!retain_gids)
   {
     + hb_enumerate (hb_iter (all_gids_to_retain), (hb_codepoint_t) 0)
@@ -426,7 +452,9 @@
     | hb_sink (reverse_glyph_map)
     ;
     *num_glyphs = reverse_glyph_map->get_population ();
-  } else {
+  }
+  else
+  {
     + hb_iter (all_gids_to_retain)
     | hb_map ([] (hb_codepoint_t _) {
 		return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (_, _);
@@ -434,10 +462,9 @@
     | hb_sink (reverse_glyph_map)
     ;
 
-    unsigned max_glyph =
-    + hb_iter (all_gids_to_retain)
-    | hb_reduce (hb_max, 0u)
-    ;
+    hb_codepoint_t max_glyph = HB_SET_VALUE_INVALID;
+    hb_set_previous (all_gids_to_retain, &max_glyph);
+
     *num_glyphs = max_glyph + 1;
   }
 
@@ -485,6 +512,9 @@
   plan->successful = true;
   plan->flags = input->flags;
   plan->unicodes = hb_set_create ();
+
+  plan->unicode_to_new_gid_list.init ();
+
   plan->name_ids = hb_set_copy (input->sets.name_ids);
   _nameid_closure (face, plan->name_ids);
   plan->name_languages = hb_set_copy (input->sets.name_languages);
@@ -502,6 +532,7 @@
   plan->codepoint_to_glyph = hb_map_create ();
   plan->glyph_map = hb_map_create ();
   plan->reverse_glyph_map = hb_map_create ();
+  plan->glyph_map_gsub = hb_map_create ();
   plan->gsub_lookups = hb_map_create ();
   plan->gpos_lookups = hb_map_create ();
 
@@ -536,6 +567,19 @@
 				  plan->reverse_glyph_map,
 				  &plan->_num_output_glyphs);
 
+  _create_glyph_map_gsub (
+      plan->_glyphset_gsub,
+      plan->glyph_map,
+      plan->glyph_map_gsub);
+
+  // Now that we have old to new gid map update the unicode to new gid list.
+  for (unsigned i = 0; i < plan->unicode_to_new_gid_list.length; i++)
+  {
+    // Use raw array access for performance.
+    plan->unicode_to_new_gid_list.arrayZ[i].second =
+        plan->glyph_map->get(plan->unicode_to_new_gid_list.arrayZ[i].second);
+  }
+
   if (unlikely (plan->in_error ())) {
     hb_subset_plan_destroy (plan);
     return nullptr;
@@ -558,6 +602,7 @@
   if (!hb_object_destroy (plan)) return;
 
   hb_set_destroy (plan->unicodes);
+  plan->unicode_to_new_gid_list.fini ();
   hb_set_destroy (plan->name_ids);
   hb_set_destroy (plan->name_languages);
   hb_set_destroy (plan->layout_features);
@@ -569,6 +614,7 @@
   hb_map_destroy (plan->codepoint_to_glyph);
   hb_map_destroy (plan->glyph_map);
   hb_map_destroy (plan->reverse_glyph_map);
+  hb_map_destroy (plan->glyph_map_gsub);
   hb_set_destroy (plan->_glyphset);
   hb_set_destroy (plan->_glyphset_gsub);
   hb_set_destroy (plan->_glyphset_mathed);

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-plan.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-plan.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset-plan.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -44,6 +44,7 @@
 
   // For each cp that we'd like to retain maps to the corresponding gid.
   hb_set_t *unicodes;
+  hb_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> unicode_to_new_gid_list;
 
   // name_ids we would like to retain
   hb_set_t *name_ids;
@@ -69,6 +70,7 @@
   // Old -> New glyph id mapping
   hb_map_t *glyph_map;
   hb_map_t *reverse_glyph_map;
+  hb_map_t *glyph_map_gsub;
 
   // Plan is only good for a specific source/dest so keep them with it
   hb_face_t *source;

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset.cc
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset.cc	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-subset.cc	2022-05-21 02:44:01 UTC (rev 63353)
@@ -79,12 +79,14 @@
  */
 
 static unsigned
-_plan_estimate_subset_table_size (hb_subset_plan_t *plan, unsigned table_len)
+_plan_estimate_subset_table_size (hb_subset_plan_t *plan,
+				  unsigned table_len,
+				  bool same_size)
 {
   unsigned src_glyphs = plan->source->get_num_glyphs ();
   unsigned dst_glyphs = plan->glyphset ()->get_population ();
 
-  if (unlikely (!src_glyphs))
+  if (unlikely (!src_glyphs) || same_size)
     return 512 + table_len;
 
   return 512 + (unsigned) (table_len * sqrt ((double) dst_glyphs / src_glyphs));
@@ -123,7 +125,6 @@
 bool
 _try_subset (const TableType *table,
              hb_vector_t<char>* buf,
-             unsigned buf_size,
              hb_subset_context_t* c /* OUT */)
 {
   c->serializer->start_serialize<TableType> ();
@@ -136,7 +137,8 @@
     return needed;
   }
 
-  buf_size += (buf_size >> 1) + 32;
+  unsigned buf_size = buf->allocated;
+  buf_size = buf_size * 2 + 16;
   DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c ran out of room; reallocating to %u bytes.",
              HB_UNTAG (c->table_tag), buf_size);
 
@@ -147,13 +149,13 @@
     return needed;
   }
 
-  c->serializer->reset (buf->arrayZ, buf_size);
-  return _try_subset (table, buf, buf_size, c);
+  c->serializer->reset (buf->arrayZ, buf->allocated);
+  return _try_subset (table, buf, c);
 }
 
 template<typename TableType>
 static bool
-_subset (hb_subset_plan_t *plan)
+_subset (hb_subset_plan_t *plan, hb_vector_t<char> &buf)
 {
   hb_blob_t *source_blob = hb_sanitize_context_t ().reference_table<TableType> (plan->source);
   const TableType *table = source_blob->as<TableType> ();
@@ -167,10 +169,13 @@
     return false;
   }
 
-  hb_vector_t<char> buf;
-  /* TODO Not all tables are glyph-related.  'name' table size for example should not be
-   * affected by number of glyphs.  Accommodate that. */
-  unsigned buf_size = _plan_estimate_subset_table_size (plan, source_blob->length);
+  /* Tables that we want to allocate same space as the source table. For GSUB/GPOS it's
+   * because those are expensive to subset, so giving them more room is fine. */
+  bool same_size_table = TableType::tableTag == HB_OT_TAG_GSUB ||
+			 TableType::tableTag == HB_OT_TAG_GPOS ||
+			 TableType::tableTag == HB_OT_TAG_name;
+
+  unsigned buf_size = _plan_estimate_subset_table_size (plan, source_blob->length, same_size_table);
   DEBUG_MSG (SUBSET, nullptr,
              "OT::%c%c%c%c initial estimated table size: %u bytes.", HB_UNTAG (tag), buf_size);
   if (unlikely (!buf.alloc (buf_size)))
@@ -181,10 +186,10 @@
   }
 
   bool needed = false;
-  hb_serialize_context_t serializer (buf.arrayZ, buf_size);
+  hb_serialize_context_t serializer (buf.arrayZ, buf.allocated);
   {
     hb_subset_context_t c (source_blob, plan, &serializer, tag);
-    needed = _try_subset (table, &buf, buf_size, &c);
+    needed = _try_subset (table, &buf, &c);
   }
   hb_blob_destroy (source_blob);
 
@@ -274,7 +279,9 @@
 }
 
 static bool
-_subset_table (hb_subset_plan_t *plan, hb_tag_t tag)
+_subset_table (hb_subset_plan_t *plan,
+	       hb_vector_t<char> &buf,
+	       hb_tag_t tag)
 {
   if (plan->no_subset_tables->has (tag)) {
     return _passthrough (plan, tag);
@@ -283,42 +290,42 @@
   DEBUG_MSG (SUBSET, nullptr, "subset %c%c%c%c", HB_UNTAG (tag));
   switch (tag)
   {
-  case HB_OT_TAG_glyf: return _subset<const OT::glyf> (plan);
-  case HB_OT_TAG_hdmx: return _subset<const OT::hdmx> (plan);
-  case HB_OT_TAG_name: return _subset<const OT::name> (plan);
+  case HB_OT_TAG_glyf: return _subset<const OT::glyf> (plan, buf);
+  case HB_OT_TAG_hdmx: return _subset<const OT::hdmx> (plan, buf);
+  case HB_OT_TAG_name: return _subset<const OT::name> (plan, buf);
   case HB_OT_TAG_head:
     if (_is_table_present (plan->source, HB_OT_TAG_glyf) && !_should_drop_table (plan, HB_OT_TAG_glyf))
       return true; /* skip head, handled by glyf */
-    return _subset<const OT::head> (plan);
+    return _subset<const OT::head> (plan, buf);
   case HB_OT_TAG_hhea: return true; /* skip hhea, handled by hmtx */
-  case HB_OT_TAG_hmtx: return _subset<const OT::hmtx> (plan);
+  case HB_OT_TAG_hmtx: return _subset<const OT::hmtx> (plan, buf);
   case HB_OT_TAG_vhea: return true; /* skip vhea, handled by vmtx */
-  case HB_OT_TAG_vmtx: return _subset<const OT::vmtx> (plan);
-  case HB_OT_TAG_maxp: return _subset<const OT::maxp> (plan);
-  case HB_OT_TAG_sbix: return _subset<const OT::sbix> (plan);
+  case HB_OT_TAG_vmtx: return _subset<const OT::vmtx> (plan, buf);
+  case HB_OT_TAG_maxp: return _subset<const OT::maxp> (plan, buf);
+  case HB_OT_TAG_sbix: return _subset<const OT::sbix> (plan, buf);
   case HB_OT_TAG_loca: return true; /* skip loca, handled by glyf */
-  case HB_OT_TAG_cmap: return _subset<const OT::cmap> (plan);
-  case HB_OT_TAG_OS2 : return _subset<const OT::OS2 > (plan);
-  case HB_OT_TAG_post: return _subset<const OT::post> (plan);
-  case HB_OT_TAG_COLR: return _subset<const OT::COLR> (plan);
-  case HB_OT_TAG_CPAL: return _subset<const OT::CPAL> (plan);
-  case HB_OT_TAG_CBLC: return _subset<const OT::CBLC> (plan);
+  case HB_OT_TAG_cmap: return _subset<const OT::cmap> (plan, buf);
+  case HB_OT_TAG_OS2 : return _subset<const OT::OS2 > (plan, buf);
+  case HB_OT_TAG_post: return _subset<const OT::post> (plan, buf);
+  case HB_OT_TAG_COLR: return _subset<const OT::COLR> (plan, buf);
+  case HB_OT_TAG_CPAL: return _subset<const OT::CPAL> (plan, buf);
+  case HB_OT_TAG_CBLC: return _subset<const OT::CBLC> (plan, buf);
   case HB_OT_TAG_CBDT: return true; /* skip CBDT, handled by CBLC */
-  case HB_OT_TAG_MATH: return _subset<const OT::MATH> (plan);
+  case HB_OT_TAG_MATH: return _subset<const OT::MATH> (plan, buf);
 
 #ifndef HB_NO_SUBSET_CFF
-  case HB_OT_TAG_cff1: return _subset<const OT::cff1> (plan);
-  case HB_OT_TAG_cff2: return _subset<const OT::cff2> (plan);
-  case HB_OT_TAG_VORG: return _subset<const OT::VORG> (plan);
+  case HB_OT_TAG_cff1: return _subset<const OT::cff1> (plan, buf);
+  case HB_OT_TAG_cff2: return _subset<const OT::cff2> (plan, buf);
+  case HB_OT_TAG_VORG: return _subset<const OT::VORG> (plan, buf);
 #endif
 
 #ifndef HB_NO_SUBSET_LAYOUT
-  case HB_OT_TAG_GDEF: return _subset<const OT::GDEF> (plan);
-  case HB_OT_TAG_GSUB: return _subset<const GSUB> (plan);
-  case HB_OT_TAG_GPOS: return _subset<const OT::GPOS> (plan);
-  case HB_OT_TAG_gvar: return _subset<const OT::gvar> (plan);
-  case HB_OT_TAG_HVAR: return _subset<const OT::HVAR> (plan);
-  case HB_OT_TAG_VVAR: return _subset<const OT::VVAR> (plan);
+  case HB_OT_TAG_GDEF: return _subset<const OT::GDEF> (plan, buf);
+  case HB_OT_TAG_GSUB: return _subset<const GSUB> (plan, buf);
+  case HB_OT_TAG_GPOS: return _subset<const OT::GPOS> (plan, buf);
+  case HB_OT_TAG_gvar: return _subset<const OT::gvar> (plan, buf);
+  case HB_OT_TAG_HVAR: return _subset<const OT::HVAR> (plan, buf);
+  case HB_OT_TAG_VVAR: return _subset<const OT::VVAR> (plan, buf);
 #endif
 
   default:
@@ -379,6 +386,8 @@
   bool success = true;
   hb_tag_t table_tags[32];
   unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags);
+  hb_vector_t<char> buf;
+  buf.alloc (4096 - 16);
   while ((hb_face_get_table_tags (plan->source, offset, &num_tables, table_tags), num_tables))
   {
     for (unsigned i = 0; i < num_tables; ++i)
@@ -386,7 +395,7 @@
       hb_tag_t tag = table_tags[i];
       if (_should_drop_table (plan, tag) && !tags_set.has (tag)) continue;
       tags_set.add (tag);
-      success = _subset_table (plan, tag);
+      success = _subset_table (plan, buf, tag);
       if (unlikely (!success)) goto end;
     }
     offset += num_tables;

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-vector.hh
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-vector.hh	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-vector.hh	2022-05-21 02:44:01 UTC (rev 63353)
@@ -29,6 +29,7 @@
 
 #include "hb.hh"
 #include "hb-array.hh"
+#include "hb-meta.hh"
 #include "hb-null.hh"
 
 
@@ -42,6 +43,7 @@
   using c_array_t = typename std::conditional<sorted, hb_sorted_array_t<const Type>, hb_array_t<const Type>>::type;
 
   hb_vector_t () = default;
+  hb_vector_t (std::nullptr_t) : hb_vector_t () {}
   hb_vector_t (std::initializer_list<Type> lst) : hb_vector_t ()
   {
     alloc (lst.size ());
@@ -59,7 +61,8 @@
   hb_vector_t (const hb_vector_t &o) : hb_vector_t ()
   {
     alloc (o.length);
-    hb_copy (o, *this);
+    if (unlikely (in_error ())) return;
+    copy_vector (o);
   }
   hb_vector_t (hb_vector_t &&o)
   {
@@ -70,9 +73,8 @@
   }
   ~hb_vector_t () { fini (); }
 
-  private:
+  public:
   int allocated = 0; /* == -1 means allocation failed. */
-  public:
   unsigned int length = 0;
   public:
   Type *arrayZ = nullptr;
@@ -108,7 +110,10 @@
   {
     reset ();
     alloc (o.length);
-    hb_copy (o, *this);
+    if (unlikely (in_error ())) return *this;
+
+    copy_vector (o);
+
     return *this;
   }
   hb_vector_t& operator = (hb_vector_t &&o)
@@ -184,12 +189,14 @@
   {
     if (unlikely (!resize (length + 1)))
       return &Crap (Type);
-    return &arrayZ[length - 1];
+    return std::addressof (arrayZ[length - 1]);
   }
-  template <typename T>
+  template <typename T,
+	    typename T2 = Type,
+	    hb_enable_if (!std::is_copy_constructible<T2>::value &&
+			  std::is_copy_assignable<T>::value)>
   Type *push (T&& v)
   {
-    /* TODO Emplace? */
     Type *p = push ();
     if (p == &Crap (Type))
       // If push failed to allocate then don't copy v, since this may cause
@@ -199,11 +206,27 @@
     *p = std::forward<T> (v);
     return p;
   }
+  template <typename T,
+	    typename T2 = Type,
+	    hb_enable_if (std::is_copy_constructible<T2>::value)>
+  Type *push (T&& v)
+  {
+    if (unlikely (!alloc (length + 1)))
+      // If push failed to allocate then don't copy v, since this may cause
+      // the created copy to leak memory since we won't have stored a
+      // reference to it.
+      return &Crap (Type);
 
+    /* Emplace. */
+    length++;
+    Type *p = std::addressof (arrayZ[length - 1]);
+    return new (p) Type (std::forward<T> (v));
+  }
+
   bool in_error () const { return allocated < 0; }
 
   template <typename T = Type,
-	    hb_enable_if (std::is_trivially_copy_assignable<T>::value)>
+	    hb_enable_if (hb_is_trivially_copy_assignable(T))>
   Type *
   realloc_vector (unsigned new_allocated)
   {
@@ -210,7 +233,7 @@
     return (Type *) hb_realloc (arrayZ, new_allocated * sizeof (Type));
   }
   template <typename T = Type,
-	    hb_enable_if (!std::is_trivially_copy_assignable<T>::value)>
+	    hb_enable_if (!hb_is_trivially_copy_assignable(T))>
   Type *
   realloc_vector (unsigned new_allocated)
   {
@@ -230,8 +253,7 @@
   }
 
   template <typename T = Type,
-	    hb_enable_if (std::is_trivially_constructible<T>::value ||
-			  !std::is_default_constructible<T>::value)>
+	    hb_enable_if (hb_is_trivially_constructible(T))>
   void
   grow_vector (unsigned size)
   {
@@ -239,8 +261,7 @@
     length = size;
   }
   template <typename T = Type,
-	    hb_enable_if (!std::is_trivially_constructible<T>::value &&
-			   std::is_default_constructible<T>::value)>
+	    hb_enable_if (!hb_is_trivially_constructible(T))>
   void
   grow_vector (unsigned size)
   {
@@ -252,14 +273,52 @@
   }
 
   template <typename T = Type,
-	    hb_enable_if (std::is_trivially_destructible<T>::value)>
+	    hb_enable_if (hb_is_trivially_copyable (T))>
   void
+  copy_vector (const hb_vector_t &other)
+  {
+    length = other.length;
+    hb_memcpy ((void *) arrayZ, (const void *) other.arrayZ, length * item_size);
+  }
+  template <typename T = Type,
+	    hb_enable_if (!hb_is_trivially_copyable (T) &&
+			   std::is_copy_constructible<T>::value)>
+  void
+  copy_vector (const hb_vector_t &other)
+  {
+    length = 0;
+    while (length < other.length)
+    {
+      length++;
+      new (std::addressof (arrayZ[length - 1])) Type (other.arrayZ[length - 1]);
+    }
+  }
+  template <typename T = Type,
+	    hb_enable_if (!hb_is_trivially_copyable (T) &&
+			  !std::is_copy_constructible<T>::value &&
+			  std::is_default_constructible<T>::value &&
+			  std::is_copy_assignable<T>::value)>
+  void
+  copy_vector (const hb_vector_t &other)
+  {
+    length = 0;
+    while (length < other.length)
+    {
+      length++;
+      new (std::addressof (arrayZ[length - 1])) Type ();
+      arrayZ[length - 1] = other.arrayZ[length - 1];
+    }
+  }
+
+  template <typename T = Type,
+	    hb_enable_if (hb_is_trivially_destructible(T))>
+  void
   shrink_vector (unsigned size)
   {
     length = size;
   }
   template <typename T = Type,
-	    hb_enable_if (!std::is_trivially_destructible<T>::value)>
+	    hb_enable_if (!hb_is_trivially_destructible(T))>
   void
   shrink_vector (unsigned size)
   {
@@ -271,7 +330,7 @@
   }
 
   template <typename T = Type,
-	    hb_enable_if (std::is_trivially_copy_assignable<T>::value)>
+	    hb_enable_if (hb_is_trivially_copy_assignable(T))>
   void
   shift_down_vector (unsigned i)
   {
@@ -280,7 +339,7 @@
 	     (length - i) * sizeof (Type));
   }
   template <typename T = Type,
-	    hb_enable_if (!std::is_trivially_copy_assignable<T>::value)>
+	    hb_enable_if (!hb_is_trivially_copy_assignable(T))>
   void
   shift_down_vector (unsigned i)
   {
@@ -341,7 +400,7 @@
   Type pop ()
   {
     if (!length) return Null (Type);
-    Type v = std::move (arrayZ[length - 1]);
+    Type v = arrayZ[length - 1];
     arrayZ[length - 1].~Type ();
     length--;
     return v;
@@ -351,8 +410,8 @@
   {
     if (unlikely (i >= length))
       return;
-    arrayZ[i].~Type ();
     shift_down_vector (i + 1);
+    arrayZ[length - 1].~Type ();
     length--;
   }
 

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/test-map.cc
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/test-map.cc	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/test-map.cc	2022-05-21 02:44:01 UTC (rev 63353)
@@ -25,9 +25,13 @@
 
 #include "hb.hh"
 #include "hb-map.hh"
+#include "hb-set.hh"
 #include <string>
 
 static const std::string invalid{"invalid"};
+static const hb_map_t invalid_map{};
+static const hb_set_t invalid_set{};
+static const hb_vector_t<unsigned> invalid_vector{};
 
 int
 main (int argc, char **argv)
@@ -57,13 +61,21 @@
 
   /* Test move constructor. */
   {
-    hb_map_t v {hb_map_t {}};
+    hb_map_t s {};
+    s.set (1, 2);
+    hb_map_t v (std::move (s));
+    assert (s.get_population () == 0);
+    assert (v.get_population () == 1);
   }
 
   /* Test move assignment. */
   {
+    hb_map_t s {};
+    s.set (1, 2);
     hb_map_t v;
-    v = hb_map_t {};
+    v = std::move (s);
+    assert (s.get_population () == 0);
+    assert (v.get_population () == 1);
   }
 
   /* Test initializing from iterable. */
@@ -73,9 +85,15 @@
     s.set (1, 2);
     s.set (3, 4);
 
-    hb_map_t v (s);
+    hb_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> v (s);
+    hb_map_t v0 (v);
+    hb_map_t v1 (s);
+    hb_map_t v2 (std::move (s));
 
-    assert (v.get_population () == 2);
+    assert (s.get_population () == 0);
+    assert (v0.get_population () == 2);
+    assert (v1.get_population () == 2);
+    assert (v2.get_population () == 2);
   }
 
   /* Test call fini() twice. */
@@ -135,5 +153,75 @@
     }
   }
 
+  /* Test hashing maps. */
+  {
+    using pair = hb_pair_t<hb_codepoint_t, hb_codepoint_t>;
+
+    hb_hashmap_t<hb_map_t, hb_map_t, const hb_map_t *, const hb_map_t *, &invalid_map, &invalid_map> m1;
+    hb_hashmap_t<hb_map_t, hb_map_t, std::nullptr_t, std::nullptr_t, nullptr, nullptr> m2;
+
+    m1.set (hb_map_t (), hb_map_t {});
+    m2.set (hb_map_t (), hb_map_t {});
+
+    m1.set (hb_map_t (), hb_map_t {pair (1u, 2u)});
+    m2.set (hb_map_t (), hb_map_t {pair (1u, 2u)});
+
+    m1.set (hb_map_t {pair (1u, 2u)}, hb_map_t {pair (2u, 3u)});
+    m2.set (hb_map_t {pair (1u, 2u)}, hb_map_t {pair (2u, 3u)});
+
+    /* Cannot override empty map. */
+    assert (m1.get (hb_map_t ()) == hb_map_t ());
+    assert (m2.get (hb_map_t ()) == hb_map_t ());
+
+   assert (m1.get (hb_map_t {pair (1u, 2u)}) == hb_map_t {pair (2u, 3u)});
+   assert (m2.get (hb_map_t {pair (1u, 2u)}) == hb_map_t {pair (2u, 3u)});
+  }
+
+  /* Test hashing sets. */
+  {
+    hb_hashmap_t<hb_set_t, hb_set_t, const hb_set_t *, const hb_set_t *, &invalid_set, &invalid_set> m1;
+    hb_hashmap_t<hb_set_t, hb_set_t, std::nullptr_t, std::nullptr_t, nullptr, nullptr> m2;
+
+    m1.set (hb_set_t (), hb_set_t ());
+    m2.set (hb_set_t (), hb_set_t ());
+
+    m1.set (hb_set_t (), hb_set_t {1});
+    m2.set (hb_set_t (), hb_set_t {1});
+
+    m1.set (hb_set_t {1, 1000}, hb_set_t {2});
+    m2.set (hb_set_t {1, 1000}, hb_set_t {2});
+
+    /* Cannot override empty set. */
+    assert (m1.get (hb_set_t ()) == hb_set_t ());
+    assert (m2.get (hb_set_t ()) == hb_set_t ());
+
+    assert (m1.get (hb_set_t {1000, 1}) == hb_set_t {2});
+    assert (m2.get (hb_set_t {1000, 1}) == hb_set_t {2});
+  }
+
+  /* Test hashing vectors. */
+  {
+    using vector_t = hb_vector_t<unsigned>;
+
+    hb_hashmap_t<vector_t, vector_t, const vector_t *, const vector_t *, &invalid_vector, &invalid_vector> m1;
+    hb_hashmap_t<vector_t, vector_t, std::nullptr_t, std::nullptr_t, nullptr, nullptr> m2;
+
+    m1.set (vector_t (), vector_t ());
+    m2.set (vector_t (), vector_t ());
+
+    m1.set (vector_t (), vector_t {1});
+    m2.set (vector_t (), vector_t {1});
+
+    m1.set (vector_t {1}, vector_t {2});
+    m2.set (vector_t {1}, vector_t {2});
+
+    /* Cannot override empty vector. */
+    assert (m1.get (vector_t ()) == vector_t ());
+    assert (m2.get (vector_t ()) == vector_t ());
+
+    assert (m1.get (vector_t {1}) == vector_t {2});
+    assert (m2.get (vector_t {1}) == vector_t {2});
+  }
+
   return 0;
 }

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/test-priority-queue.cc
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/test-priority-queue.cc	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/test-priority-queue.cc	2022-05-21 02:44:01 UTC (rev 63353)
@@ -73,17 +73,9 @@
   assert (queue.is_empty ());
 }
 
-static void
-test_extract_empty ()
-{
-  hb_priority_queue_t queue;
-  assert (queue.pop_minimum () == hb_pair (0, 0));
-}
-
 int
 main (int argc, char **argv)
 {
   test_insert ();
   test_extract ();
-  test_extract_empty ();
 }

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/test-repacker.cc
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/test-repacker.cc	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/test-repacker.cc	2022-05-21 02:44:01 UTC (rev 63353)
@@ -729,26 +729,6 @@
 }
 
 static void
-populate_serializer_complex_1 (hb_serialize_context_t* c)
-{
-  c->start_serialize<char> ();
-
-  unsigned obj_4 = add_object ("jkl", 3, c);
-  unsigned obj_3 = add_object ("ghi", 3, c);
-
-  start_object ("def", 3, c);
-  add_offset (obj_3, c);
-  unsigned obj_2 = c->pop_pack (false);
-
-  start_object ("abc", 3, c);
-  add_offset (obj_2, c);
-  add_offset (obj_4, c);
-  c->pop_pack (false);
-
-  c->end_serialize();
-}
-
-static void
 populate_serializer_complex_2 (hb_serialize_context_t* c)
 {
   c->start_serialize<char> ();
@@ -831,68 +811,6 @@
   c->end_serialize();
 }
 
-static void test_sort_kahn_1 ()
-{
-  size_t buffer_size = 100;
-  void* buffer = malloc (buffer_size);
-  hb_serialize_context_t c (buffer, buffer_size);
-  populate_serializer_complex_1 (&c);
-
-  graph_t graph (c.object_graph ());
-  graph.sort_kahn ();
-
-  assert(strncmp (graph.object (3).head, "abc", 3) == 0);
-  assert(graph.object (3).real_links.length == 2);
-  assert(graph.object (3).real_links[0].objidx == 2);
-  assert(graph.object (3).real_links[1].objidx == 1);
-
-  assert(strncmp (graph.object (2).head, "def", 3) == 0);
-  assert(graph.object (2).real_links.length == 1);
-  assert(graph.object (2).real_links[0].objidx == 0);
-
-  assert(strncmp (graph.object (1).head, "jkl", 3) == 0);
-  assert(graph.object (1).real_links.length == 0);
-
-  assert(strncmp (graph.object (0).head, "ghi", 3) == 0);
-  assert(graph.object (0).real_links.length == 0);
-
-  free (buffer);
-}
-
-static void test_sort_kahn_2 ()
-{
-  size_t buffer_size = 100;
-  void* buffer = malloc (buffer_size);
-  hb_serialize_context_t c (buffer, buffer_size);
-  populate_serializer_complex_2 (&c);
-
-  graph_t graph (c.object_graph ());
-  graph.sort_kahn ();
-
-
-  assert(strncmp (graph.object (4).head, "abc", 3) == 0);
-  assert(graph.object (4).real_links.length == 3);
-  assert(graph.object (4).real_links[0].objidx == 3);
-    assert(graph.object (4).real_links[1].objidx == 0);
-  assert(graph.object (4).real_links[2].objidx == 2);
-
-  assert(strncmp (graph.object (3).head, "def", 3) == 0);
-  assert(graph.object (3).real_links.length == 1);
-  assert(graph.object (3).real_links[0].objidx == 1);
-
-  assert(strncmp (graph.object (2).head, "mn", 2) == 0);
-  assert(graph.object (2).real_links.length == 0);
-
-  assert(strncmp (graph.object (1).head, "ghi", 3) == 0);
-  assert(graph.object (1).real_links.length == 1);
-  assert(graph.object (1).real_links[0].objidx == 0);
-
-  assert(strncmp (graph.object (0).head, "jkl", 3) == 0);
-  assert(graph.object (0).real_links.length == 0);
-
-  free (buffer);
-}
-
 static void test_sort_shortest ()
 {
   size_t buffer_size = 100;
@@ -1336,8 +1254,6 @@
 main (int argc, char **argv)
 {
   test_serialize ();
-  test_sort_kahn_1 ();
-  test_sort_kahn_2 ();
   test_sort_shortest ();
   test_will_overflow_1 ();
   test_will_overflow_2 ();

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/test-set.cc
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/test-set.cc	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/test-set.cc	2022-05-21 02:44:01 UTC (rev 63353)
@@ -42,7 +42,8 @@
   /* Test copy assignment. */
   {
     hb_set_t v1 {1, 2};
-    hb_set_t v2 = v1;
+    hb_set_t v2;
+    v2 = v1;
     assert (v1.get_population () == 2);
     assert (v2.get_population () == 2);
   }
@@ -49,14 +50,18 @@
 
   /* Test move constructor. */
   {
-    hb_set_t v {hb_set_t {1, 2}};
+    hb_set_t s {1, 2};
+    hb_set_t v (std::move (s));
+    assert (s.get_population () == 0);
     assert (v.get_population () == 2);
   }
 
   /* Test move assignment. */
   {
+    hb_set_t s = hb_set_t {1, 2};
     hb_set_t v;
-    v = hb_set_t {1, 2};
+    v = std::move (s);
+    assert (s.get_population () == 0);
     assert (v.get_population () == 2);
   }
 
@@ -67,9 +72,15 @@
     s.add (18);
     s.add (12);
 
-    hb_set_t v (s);
+    hb_vector_t<hb_codepoint_t> v (s);
+    hb_set_t v0 (v);
+    hb_set_t v1 (s);
+    hb_set_t v2 (std::move (s));
 
-    assert (v.get_population () == 2);
+    assert (s.get_population () == 0);
+    assert (v0.get_population () == 2);
+    assert (v1.get_population () == 2);
+    assert (v2.get_population () == 2);
   }
 
   /* Test initializing from iterator. */

Modified: trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/test-vector.cc
===================================================================
--- trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/test-vector.cc	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/harfbuzz-src/src/test-vector.cc	2022-05-21 02:44:01 UTC (rev 63353)
@@ -61,8 +61,12 @@
 
   /* Test move constructor. */
   {
-    hb_vector_t<int> v {hb_vector_t<int> {1, 2}};
-    hb_vector_t<int> V {hb_vector_t<int> {1, 2}};
+    hb_vector_t<int> s {1, 2};
+    hb_sorted_vector_t<int> S {1, 2};
+    hb_vector_t<int> v (std::move (s));
+    hb_sorted_vector_t<int> V (std::move (S));
+    assert (s.length == 0);
+    assert (S.length == 0);
     assert (v.length == 2);
     assert (v[0] == 1);
     assert (v[1] == 2);
@@ -70,11 +74,16 @@
 
   /* Test move assignment. */
   {
+    hb_vector_t<int> s {1, 2};
+    hb_sorted_vector_t<int> S {1, 2};
     hb_vector_t<int> v;
     hb_sorted_vector_t<int> V;
-    v = hb_vector_t<int> {1, 2};
-    V = hb_sorted_vector_t<int> {1, 2};
+    v = std::move (s);
+    V = std::move (S);
+    assert (s.length == 0);
+    assert (S.length == 0);
     assert (v.length == 2);
+    assert (V.length == 2);
     assert (v[0] == 1);
     assert (v[1] == 2);
   }
@@ -137,7 +146,6 @@
     assert (v2[2] == 3);
   }
 
-#if 0
   {
     hb_vector_t<std::string> v;
 
@@ -147,8 +155,13 @@
       s += "x";
       v.push (s);
     }
+
+    hb_vector_t<std::string> v2;
+
+    v2 = v;
+
+    v2.remove (50);
   }
-#endif
 
   return 0;
 }

Modified: trunk/Build/source/libs/harfbuzz/version.ac
===================================================================
--- trunk/Build/source/libs/harfbuzz/version.ac	2022-05-20 23:48:17 UTC (rev 63352)
+++ trunk/Build/source/libs/harfbuzz/version.ac	2022-05-21 02:44:01 UTC (rev 63353)
@@ -8,4 +8,4 @@
 dnl --------------------------------------------------------
 dnl
 dnl  m4-include this file to define the current harfbuzz version
-m4_define([harfbuzz_version], [4.2.1])
+m4_define([harfbuzz_version], [4.3.0])



More information about the tex-live-commits mailing list.