Merge branch 'master' into var-subset
diff --git a/.circleci/config.yml b/.circleci/config.yml
index e221943..81959b5 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -4,7 +4,7 @@
 
   macos-10.12.6-aat-fonts:
     macos:
-      xcode: "9.2.0"
+      xcode: "9.0.1"
     steps:
       - checkout
       - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget autoconf automake libtool pkg-config ragel freetype glib cairo
@@ -98,7 +98,9 @@
       - run: CFLAGS="-O0" CXXFLAGS="-O0" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-fontconfig --with-glib --with-cairo --with-icu --with-graphite2
       - run: make -j32
       - run: LD_LIBRARY_PATH="$PWD/freetype-2.9/objs/.libs" make check || .ci/fail.sh
-      - run: make clean && cd src && clang++ -c hb-*.cc
+      - run: make clean
+      - run: make -Csrc CPPFLAGS="-DHB_TINY -DHB_NO_OT_FONT" libharfbuzz-subset.la && make clean
+      - run: clang -c src/hb-*.cc -DHB_NO_MT
 
   gcc-valgrind:
     docker:
@@ -112,7 +114,7 @@
       - run: make -j32
       # run-shape-fuzzer-tests.py automatically runs valgrind if see available
       # but test/api runs it by request, we probably should normalize the approaches
-      - run: RUN_VALGRIND=1 make check && make -Ctest/api check-valgrind || .ci/fail.sh
+      - run: HB_TEST_SHAPE_FUZZER_TIMEOUT=3 HB_TEST_SUBSET_FUZZER_TIMEOUT=30 RUN_VALGRIND=1 make check && make -Ctest/api check-valgrind || .ci/fail.sh
       # informational for now
       - run: make -Ctest/api check-symbols || true
 
@@ -164,7 +166,7 @@
       - run: wget https://ftp.gnome.org/pub/gnome/sources/glib/2.58/glib-2.58.1.tar.xz && tar xf glib-2.58.1.tar.xz && cd glib-2.58.1 && ./autogen.sh --with-pcre CPPFLAGS="-fsanitize=memory" LDFLAGS="-fsanitize=memory" CFLAGS="-fsanitize=memory" CXXFLAGS="-fsanitize=memory" LD=ld.lld CC=clang CXX=clang++ && make -j32 && make install && cd ..
       - run: wget http://download.savannah.gnu.org/releases/freetype/freetype-2.9.tar.bz2 && tar xf freetype-2.9.tar.bz2 && cd freetype-2.9 && ./autogen.sh && ./configure CPPFLAGS="-fsanitize=memory" LDFLAGS="-fsanitize=memory -O1 -g -fno-omit-frame-pointer" CFLAGS="-fsanitize=memory -O1 -g -fno-omit-frame-pointer" CXXFLAGS="-fsanitize=memory -O1 -g -fno-omit-frame-pointer" LD=ld.lld CC=clang CXX=clang++ && make -j32 && make install && cd ..
       - run: CPPFLAGS="-fsanitize=memory -fsanitize-memory-track-origins" LDFLAGS="-fsanitize=memory -fsanitize-memory-track-origins -O1 -g -fno-omit-frame-pointer" CFLAGS="-fsanitize=memory -fsanitize-memory-track-origins -O1 -g -fno-omit-frame-pointer" CXXFLAGS="-fsanitize=memory -fsanitize-memory-track-origins -O1 -g -fno-omit-frame-pointer" LD=ld.lld CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --without-icu
-      - run: make -j32 && MSAN_OPTIONS=exitcode=42 make check || .ci/fail.sh | asan_symbolize | c++filt
+      - run: make -j32 && MSAN_OPTIONS=exitcode=42 HB_TEST_SUBSET_FUZZER_TIMEOUT=12 make check || .ci/fail.sh | asan_symbolize | c++filt
 
   clang-tsan:
     docker:
@@ -180,7 +182,7 @@
       - run: pip install fonttools
       - run: CPPFLAGS="-fsanitize=thread" LDFLAGS="-fsanitize=thread -O1 -g -fno-omit-frame-pointer" CFLAGS="-fsanitize=thread -O1 -g -fno-omit-frame-pointer" CXXFLAGS="-fsanitize=thread -O1 -g -fno-omit-frame-pointer" LD=ld.lld CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2
       - run: make -j32
-      - run: make check || .ci/fail.sh | asan_symbolize | c++filt
+      - run: HB_TEST_SUBSET_FUZZER_TIMEOUT=40 make check || .ci/fail.sh | asan_symbolize | c++filt
 
   clang-ubsan:
     docker:
@@ -203,7 +205,7 @@
       - image: fedora
     steps:
       - checkout
-      - run: dnf install -y pkg-config ragel gcc gcc-c++ automake autoconf libtool make which glib2-devel freetype-devel cairo-devel libicu-devel gobject-introspection-devel graphite2-devel redhat-rpm-config python mingw32-gcc-c++ mingw64-gcc-c++ mingw32-glib2 mingw32-cairo mingw32-freetype mingw64-glib2 mingw64-cairo mingw64-freetype glibc-devel.i686 || true
+      - run: dnf install -y pkg-config ragel gcc gcc-c++ automake autoconf libtool make which diffutils glib2-devel freetype-devel cairo-devel libicu-devel gobject-introspection-devel graphite2-devel redhat-rpm-config python python-pip mingw32-gcc-c++ mingw64-gcc-c++ mingw32-glib2 mingw32-cairo mingw32-freetype mingw64-glib2 mingw64-cairo mingw64-freetype glibc-devel.i686 || true
       - run: NOCONFIGURE=1 ./autogen.sh
       - run: mkdir build && cd build && CFLAGS="-O0" CXXFLAGS="-O0" CPPFLAGS="-DHB_DEBUG" ../configure --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2 && make -j32 && (make check || ../.ci/fail.sh)
       - run: pip install pefile
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2a8fd8b..aa00a88 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -465,6 +465,19 @@
 add_library(harfbuzz ${project_sources} ${project_extra_sources} ${project_headers})
 target_link_libraries(harfbuzz ${THIRD_PARTY_LIBS})
 
+
+## Define harfbuzz-icu library
+if (HB_HAVE_ICU)
+  add_library(harfbuzz-icu ${PROJECT_SOURCE_DIR}/src/hb-icu.cc ${PROJECT_SOURCE_DIR}/src/hb-icu.h)
+  add_dependencies(harfbuzz-icu harfbuzz)
+  target_link_libraries(harfbuzz-icu harfbuzz ${THIRD_PARTY_LIBS})
+
+  if (BUILD_SHARED_LIBS)
+    set_target_properties(harfbuzz harfbuzz-icu PROPERTIES VISIBILITY_INLINES_HIDDEN TRUE)
+  endif ()
+endif ()
+
+
 ## Define harfbuzz-subset library
 if (HB_BUILD_SUBSET)
   add_library(harfbuzz-subset ${subset_project_sources} ${subset_project_headers})
@@ -723,6 +736,14 @@
       NAMESPACE harfbuzz::
       DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/harfbuzz
   )
+  if (HB_HAVE_ICU)
+    install(TARGETS harfbuzz-icu
+      ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+      LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+      RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+      FRAMEWORK DESTINATION Library/Frameworks
+    )
+  endif ()
   if (HB_BUILD_UTILS)
     if (WIN32 AND BUILD_SHARED_LIBS)
       install(TARGETS harfbuzz-subset
diff --git a/NEWS b/NEWS
index 30fa8c3..7dde119 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,18 @@
+Overview of changes leading to 2.6.4
+Monday, October 29, 2019
+====================================
+- Small bug fix.
+- Build fixes.
+
+
+Overview of changes leading to 2.6.3
+Monday, October 28, 2019
+====================================
+- Misc small fixes, mostly to build-related issues.
+- New API:
++hb_font_get_nominal_glyphs()
+
+
 Overview of changes leading to 2.6.2
 Monday, September 30, 2019
 ====================================
diff --git a/TESTING.md b/TESTING.md
index 4efc64c..94be3a0 100644
--- a/TESTING.md
+++ b/TESTING.md
@@ -73,3 +73,14 @@
 sudo python infra/helper.py build_fuzzers --sanitizer address harfbuzz
 sudo python infra/helper.py run_fuzzer harfbuzz hb-subset-fuzzer
 ```
+
+## Profiling
+
+```
+make clean
+./configure CXXFLAGS="-fno-omit-frame-pointer -g"
+make
+perf record -o <perf output file> -g <command to run>
+perf report -i<perf output file>
+```
+
diff --git a/configure.ac b/configure.ac
index c88cffe..4125756 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [2.6.2],
+        [2.6.4],
         [https://github.com/harfbuzz/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt
index c625b92..93d6de6 100644
--- a/docs/harfbuzz-sections.txt
+++ b/docs/harfbuzz-sections.txt
@@ -365,6 +365,8 @@
 hb_ft_font_create_referenced
 hb_ft_font_changed
 hb_ft_font_get_face
+hb_ft_font_lock_face
+hb_ft_font_unlock_face
 hb_ft_font_set_load_flags
 hb_ft_font_get_load_flags
 hb_ft_font_set_funcs
diff --git a/src/Makefile.am b/src/Makefile.am
index a76d968..32b9f71 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -290,7 +290,7 @@
 use-table: gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt
 	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-use-table.cc \
 	|| ($(RM) $(srcdir)/hb-ot-shape-complex-use-table.cc; false)
-vowel-constraints: gen-vowel-constraints.py HBIndicVowelConstraints.txt Scripts.txt
+vowel-constraints: gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt
 	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-vowel-constraints.cc \
 	|| ($(RM) $(srcdir)/hb-ot-shape-complex-vowel-constraints.cc; false)
 
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 911ee2f..cbbad90 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -163,7 +163,6 @@
 	hb-unicode.hh \
 	hb-utf.hh \
 	hb-vector.hh \
-	hb-warning.cc \
 	hb.hh \
 	$(NULL)
 
diff --git a/src/gen-vowel-constraints.py b/src/gen-vowel-constraints.py
index 8ca90c8..e0ae2a6 100755
--- a/src/gen-vowel-constraints.py
+++ b/src/gen-vowel-constraints.py
@@ -25,7 +25,7 @@
 import sys
 
 if len (sys.argv) != 3:
-	print ('usage: ./gen-vowel-constraints.py HBIndicVowelConstraints.txt Scripts.txt', file=sys.stderr)
+	print ('usage: ./gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt', file=sys.stderr)
 	sys.exit (1)
 
 with io.open (sys.argv[2], encoding='utf-8') as f:
@@ -84,7 +84,8 @@
 			else:
 				self._c[first] = ConstraintSet (rest)
 
-	def _indent (self, depth):
+	@staticmethod
+	def _indent (depth):
 		return ('  ' * depth).replace ('        ', '\t')
 
 	def __str__ (self, index=0, depth=4):
@@ -92,17 +93,20 @@
 		indent = self._indent (depth)
 		if isinstance (self._c, list):
 			if len (self._c) == 0:
+				assert index == 2, 'Cannot use `matched` for this constraint; the general case has not been implemented'
 				s.append ('{}matched = true;\n'.format (indent))
 			elif len (self._c) == 1:
+				assert index == 1, 'Cannot use `matched` for this constraint; the general case has not been implemented'
 				s.append ('{}matched = 0x{:04X}u == buffer->cur ({}).codepoint;\n'.format (indent, next (iter (self._c)), index or ''))
 			else:
-				s.append ('{}if (0x{:04X}u == buffer->cur ({}).codepoint &&\n'.format (indent, self._c[0], index))
-				s.append ('{}buffer->idx + {} < count &&\n'.format (self._indent (depth + 2), len (self._c)))
+				s.append ('{}if (0x{:04X}u == buffer->cur ({}).codepoint &&\n'.format (indent, self._c[0], index or ''))
+				if index:
+					s.append ('{}buffer->idx + {} < count &&\n'.format (self._indent (depth + 2), index + 1))
 				for i, cp in enumerate (self._c[1:], start=1):
 					s.append ('{}0x{:04X}u == buffer->cur ({}).codepoint{}\n'.format (
 						self._indent (depth + 2), cp, index + i, ')' if i == len (self._c) - 1 else ' &&'))
 				s.append ('{}{{\n'.format (indent))
-				for i in range (len (self._c)):
+				for i in range (index + 1):
 					s.append ('{}buffer->next_glyph ();\n'.format (self._indent (depth + 1)))
 				s.append ('{}_output_dotted_circle (buffer);\n'.format (self._indent (depth + 1)))
 				s.append ('{}}}\n'.format (indent))
@@ -128,7 +132,12 @@
 
 constraints = {}
 with io.open (sys.argv[1], encoding='utf-8') as f:
-	constraints_header = [f.readline ().strip () for i in range (2)]
+	constraints_header = []
+	while True:
+		line = f.readline ().strip ()
+		if line == '#':
+			break
+		constraints_header.append(line)
 	for line in f:
 		j = line.find ('#')
 		if j >= 0:
@@ -147,7 +156,7 @@
 print ('/*')
 print (' * The following functions are generated by running:')
 print (' *')
-print (' *   %s use Scripts.txt' % sys.argv[0])
+print (' *   %s ms-use/IndicShapingInvalidCluster.txt Scripts.txt' % sys.argv[0])
 print (' *')
 print (' * on files with these headers:')
 print (' *')
@@ -185,7 +194,7 @@
 print ('\t\t\t\t       hb_buffer_t              *buffer,')
 print ('\t\t\t\t       hb_font_t                *font HB_UNUSED)')
 print ('{')
-print ('#if defined(HB_NO_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS)')
+print ('#ifdef HB_NO_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS')
 print ('  return;')
 print ('#endif')
 print ('  if (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)')
diff --git a/src/harfbuzz.cc b/src/harfbuzz.cc
index fe1c360..251a065 100644
--- a/src/harfbuzz.cc
+++ b/src/harfbuzz.cc
@@ -44,7 +44,6 @@
 #include "hb-static.cc"
 #include "hb-ucd.cc"
 #include "hb-unicode.cc"
-#include "hb-warning.cc"
 #include "hb-glib.cc"
 #include "hb-ft.cc"
 #include "hb-graphite2.cc"
diff --git a/src/hb-array.hh b/src/hb-array.hh
index 20d3eb7..d9adf2c 100644
--- a/src/hb-array.hh
+++ b/src/hb-array.hh
@@ -99,7 +99,14 @@
   template <typename T> operator T * () const { return arrayZ; }
 
   HB_INTERNAL bool operator == (const hb_array_t &o) const;
-  HB_INTERNAL uint32_t hash () const;
+
+  uint32_t hash () const {
+    uint32_t current = 0;
+    for (unsigned int i = 0; i < this->length; i++) {
+      current = current * 31 + hb_hash (this->arrayZ[i]);
+    }
+    return current;
+  }
 
   /*
    * Compare, Sort, and Search.
@@ -189,6 +196,15 @@
   const T *as () const
   { return length < hb_null_size (T) ? &Null (T) : reinterpret_cast<const T *> (arrayZ); }
 
+  template <typename T,
+	    unsigned P = sizeof (Type),
+	    hb_enable_if (P == 1)>
+  bool in_range (const T *p, unsigned int size = T::static_size) const
+  {
+    return ((const char *) p) >= arrayZ
+	&& ((const char *) p + size) <= arrayZ + length;
+  }
+
   /* Only call if you allocated the underlying array using malloc() or similar. */
   void free ()
   { ::free ((void *) arrayZ); arrayZ = nullptr; length = 0; }
@@ -332,27 +348,35 @@
 template <typename T>
 bool hb_array_t<T>::operator == (const hb_array_t<T> &o) const
 {
-  return length == o.length &&
-  + hb_zip (*this, o)
-  | hb_map ([] (hb_pair_t<T&, T&> &&_) { return _.first == _.second; })
-  | hb_all
-  ;
+  if (o.length != this->length) return false;
+  for (unsigned int i = 0; i < this->length; i++) {
+    if (this->arrayZ[i] != o.arrayZ[i]) return false;
+  }
+  return true;
 }
-template <typename T>
-uint32_t hb_array_t<T>::hash () const
-{
-  return
-  + hb_iter (*this)
-  | hb_map (hb_hash)
-  | hb_reduce ([] (uint32_t a, uint32_t b) { return a * 31 + b; }, 0)
-  ;
+
+/* TODO Specialize opeator== for hb_bytes_t and hb_ubytes_t. */
+
+template <>
+inline uint32_t hb_array_t<const char>::hash () const {
+  uint32_t current = 0;
+  for (unsigned int i = 0; i < this->length; i++)
+    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;
+  for (unsigned int i = 0; i < this->length; i++)
+    current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u);
+  return current;
+}
+
+
 typedef hb_array_t<const char> hb_bytes_t;
 typedef hb_array_t<const unsigned char> hb_ubytes_t;
 
-/* TODO Specialize opeator==/hash() for hb_bytes_t and hb_ubytes_t. */
-//template <>
-//uint32_t hb_array_t<const char>::hash () const { return 0; }
+
 
 #endif /* HB_ARRAY_HH */
diff --git a/src/hb-atomic.hh b/src/hb-atomic.hh
index 09d8893..b3fb296 100644
--- a/src/hb-atomic.hh
+++ b/src/hb-atomic.hh
@@ -212,18 +212,7 @@
 static_assert ((sizeof (long) == sizeof (void *)), "");
 
 
-#elif !defined(HB_NO_MT)
-
-#define HB_ATOMIC_INT_NIL 1 /* Warn that fallback implementation is in use. */
-
-#define _hb_memory_barrier()			do {} while (0)
-
-#define hb_atomic_int_impl_add(AI, V)		((*(AI) += (V)) - (V))
-
-#define hb_atomic_ptr_impl_cmpexch(P,O,N)	(* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
-
-
-#else /* HB_NO_MT */
+#elif defined(HB_NO_MT)
 
 #define hb_atomic_int_impl_add(AI, V)		((*(AI) += (V)) - (V))
 
@@ -232,6 +221,11 @@
 #define hb_atomic_ptr_impl_cmpexch(P,O,N)	(* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
 
 
+#else
+
+#error "Could not find any system to define atomic_int macros."
+#error "Check hb-atomic.hh for possible resolutions."
+
 #endif
 
 
diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
index 40ac55c..6f62e34 100644
--- a/src/hb-buffer.cc
+++ b/src/hb-buffer.cc
@@ -776,8 +776,10 @@
 
   free (buffer->info);
   free (buffer->pos);
+#ifndef HB_NO_BUFFER_MESSAGE
   if (buffer->message_destroy)
     buffer->message_destroy (buffer->message_data);
+#endif
 
   free (buffer);
 }
@@ -1858,18 +1860,8 @@
 
   bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
 
-  unsigned int count = buffer->len;
-  if (unlikely (!count)) return;
-  hb_glyph_info_t *info = buffer->info;
-
-  unsigned int start = 0;
-  unsigned int end;
-  for (end = start + 1; end < count; end++)
-    if (info[start].cluster != info[end].cluster) {
-      normalize_glyphs_cluster (buffer, start, end, backward);
-      start = end;
-    }
-  normalize_glyphs_cluster (buffer, start, end, backward);
+  foreach_cluster (buffer, start, end)
+    normalize_glyphs_cluster (buffer, start, end, backward);
 }
 
 void
diff --git a/src/hb-buffer.hh b/src/hb-buffer.hh
index b8ea5ad..b5596d9 100644
--- a/src/hb-buffer.hh
+++ b/src/hb-buffer.hh
@@ -126,9 +126,9 @@
   /* Debugging API */
 #ifndef HB_NO_BUFFER_MESSAGE
   hb_buffer_message_func_t message_func;
-#endif
   void *message_data;
   hb_destroy_func_t message_destroy;
+#endif
 
   /* Internal debugging. */
   /* The bits here reflect current allocations of the bytes in glyph_info_t's var1 and var2. */
diff --git a/src/hb-cff-interp-cs-common.hh b/src/hb-cff-interp-cs-common.hh
index d9ad4d0..eb7df08 100644
--- a/src/hb-cff-interp-cs-common.hh
+++ b/src/hb-cff-interp-cs-common.hh
@@ -551,8 +551,13 @@
 
   static void rcurveline (ENV &env, PARAM& param)
   {
+    unsigned int arg_count = env.argStack.get_count ();
+    if (unlikely (arg_count < 8))
+      return;
+
     unsigned int i = 0;
-    for (; i + 6 <= env.argStack.get_count (); i += 6)
+    unsigned int curve_limit = arg_count - 2;
+    for (; i + 6 <= curve_limit; i += 6)
     {
       point_t pt1 = env.get_pt ();
       pt1.move (env.eval_arg (i), env.eval_arg (i+1));
@@ -562,34 +567,34 @@
       pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
       PATH::curve (env, param, pt1, pt2, pt3);
     }
-    for (; i + 2 <= env.argStack.get_count (); i += 2)
-    {
-      point_t pt1 = env.get_pt ();
-      pt1.move (env.eval_arg (i), env.eval_arg (i+1));
-      PATH::line (env, param, pt1);
-    }
+
+    point_t pt1 = env.get_pt ();
+    pt1.move (env.eval_arg (i), env.eval_arg (i+1));
+    PATH::line (env, param, pt1);
   }
 
   static void rlinecurve (ENV &env, PARAM& param)
   {
+    unsigned int arg_count = env.argStack.get_count ();
+    if (unlikely (arg_count < 8))
+      return;
+
     unsigned int i = 0;
-    unsigned int line_limit = (env.argStack.get_count () % 6);
+    unsigned int line_limit = arg_count - 6;
     for (; i + 2 <= line_limit; i += 2)
     {
       point_t pt1 = env.get_pt ();
       pt1.move (env.eval_arg (i), env.eval_arg (i+1));
       PATH::line (env, param, pt1);
     }
-    for (; i + 6 <= env.argStack.get_count (); i += 6)
-    {
-      point_t pt1 = env.get_pt ();
-      pt1.move (env.eval_arg (i), env.eval_arg (i+1));
-      point_t pt2 = pt1;
-      pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
-      point_t pt3 = pt2;
-      pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
-      PATH::curve (env, param, pt1, pt2, pt3);
-    }
+
+    point_t pt1 = env.get_pt ();
+    pt1.move (env.eval_arg (i), env.eval_arg (i+1));
+    point_t pt2 = pt1;
+    pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
+    point_t pt3 = pt2;
+    pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
+    PATH::curve (env, param, pt1, pt2, pt3);
   }
 
   static void vvcurveto (ENV &env, PARAM& param)
diff --git a/src/hb-common.h b/src/hb-common.h
index 9cfaab1..037e508 100644
--- a/src/hb-common.h
+++ b/src/hb-common.h
@@ -434,7 +434,7 @@
  * @start: the cluster to start applying this feature setting (inclusive).
  * @end: the cluster to end applying this feature setting (exclusive).
  *
- * The hb_feature_t is the structure that holds information about requested
+ * The #hb_feature_t is the structure that holds information about requested
  * feature application. The feature will be applied with the given value to all
  * glyphs which are in clusters between @start (inclusive) and @end (exclusive).
  * Setting start to @HB_FEATURE_GLOBAL_START and end to @HB_FEATURE_GLOBAL_END
diff --git a/src/hb-font.cc b/src/hb-font.cc
index ee35966..e89ad69 100644
--- a/src/hb-font.cc
+++ b/src/hb-font.cc
@@ -792,6 +792,29 @@
 }
 
 /**
+ * hb_font_get_nominal_glyphs:
+ * @font: a font.
+ *
+ *
+ *
+ * Return value:
+ *
+ * Since: 2.6.3
+ **/
+unsigned int
+hb_font_get_nominal_glyphs (hb_font_t *font,
+			    unsigned int count,
+			    const hb_codepoint_t *first_unicode,
+			    unsigned int unicode_stride,
+			    hb_codepoint_t *first_glyph,
+			    unsigned int glyph_stride)
+{
+  return font->get_nominal_glyphs (count,
+				   first_unicode, unicode_stride,
+				   first_glyph, glyph_stride);
+}
+
+/**
  * hb_font_get_variation_glyph:
  * @font: a font.
  * @unicode:
diff --git a/src/hb-font.h b/src/hb-font.h
index 677be2f..01ff201 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -459,6 +459,14 @@
 			     hb_codepoint_t unicode, hb_codepoint_t variation_selector,
 			     hb_codepoint_t *glyph);
 
+HB_EXTERN unsigned int
+hb_font_get_nominal_glyphs (hb_font_t *font,
+			    unsigned int count,
+			    const hb_codepoint_t *first_unicode,
+			    unsigned int unicode_stride,
+			    hb_codepoint_t *first_glyph,
+			    unsigned int glyph_stride);
+
 HB_EXTERN hb_position_t
 hb_font_get_glyph_h_advance (hb_font_t *font,
 			     hb_codepoint_t glyph);
diff --git a/src/hb-ft.cc b/src/hb-ft.cc
index e526bf4..2a7b0de 100644
--- a/src/hb-ft.cc
+++ b/src/hb-ft.cc
@@ -140,7 +140,7 @@
   if (hb_object_is_immutable (font))
     return;
 
-  if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)
+  if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy))
     return;
 
   hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data;
@@ -160,7 +160,7 @@
 int
 hb_ft_font_get_load_flags (hb_font_t *font)
 {
-  if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)
+  if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy))
     return 0;
 
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data;
@@ -168,10 +168,19 @@
   return ft_font->load_flags;
 }
 
+/**
+ * hb_ft_font_get_face:
+ * @font:
+ *
+ *
+ *
+ * Return value:
+ * Since: 0.9.2
+ **/
 FT_Face
 hb_ft_font_get_face (hb_font_t *font)
 {
-  if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)
+  if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy))
     return nullptr;
 
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data;
@@ -179,6 +188,47 @@
   return ft_font->ft_face;
 }
 
+/**
+ * hb_ft_font_lock_face:
+ * @font:
+ *
+ *
+ *
+ * Return value:
+ * Since: REPLACEME
+ **/
+FT_Face
+hb_ft_font_lock_face (hb_font_t *font)
+{
+  if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy))
+    return nullptr;
+
+  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data;
+
+  ft_font->lock.lock ();
+
+  return ft_font->ft_face;
+}
+
+/**
+ * hb_ft_font_unlock_face:
+ * @font:
+ *
+ *
+ *
+ * Return value:
+ * Since: REPLACEME
+ **/
+void
+hb_ft_font_unlock_face (hb_font_t *font)
+{
+  if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy))
+    return;
+
+  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data;
+
+  ft_font->lock.unlock ();
+}
 
 
 static hb_bool_t
@@ -718,7 +768,7 @@
 		    ft_face->size->metrics.y_ppem);
 #endif
 
-#ifdef HAVE_FT_GET_VAR_BLEND_COORDINATES
+#if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR)
   FT_MM_Var *mm_var = nullptr;
   if (!FT_Get_MM_Var (ft_face, &mm_var))
   {
@@ -857,7 +907,7 @@
     FT_Set_Transform (ft_face, &matrix, nullptr);
   }
 
-#ifdef HAVE_FT_SET_VAR_BLEND_COORDINATES
+#if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR)
   unsigned int num_coords;
   const int *coords = hb_font_get_var_coords_normalized (font, &num_coords);
   if (num_coords)
diff --git a/src/hb-ft.h b/src/hb-ft.h
index 94013ee..bf07115 100644
--- a/src/hb-ft.h
+++ b/src/hb-ft.h
@@ -110,6 +110,12 @@
 HB_EXTERN FT_Face
 hb_ft_font_get_face (hb_font_t *font);
 
+HB_EXTERN FT_Face
+hb_ft_font_lock_face (hb_font_t *font);
+
+HB_EXTERN void
+hb_ft_font_unlock_face (hb_font_t *font);
+
 HB_EXTERN void
 hb_ft_font_set_load_flags (hb_font_t *font, int load_flags);
 
diff --git a/src/hb-machinery.hh b/src/hb-machinery.hh
index 15535d7..e33cd64 100644
--- a/src/hb-machinery.hh
+++ b/src/hb-machinery.hh
@@ -135,7 +135,7 @@
 
 #define DEFINE_SIZE_ARRAY(size, array) \
   DEFINE_COMPILES_ASSERTION ((void) (array)[0].static_size) \
-  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + HB_VAR_ARRAY * sizeof ((array)[0])) \
+  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + (HB_VAR_ARRAY+0) * sizeof ((array)[0])) \
   static constexpr unsigned null_size = (size); \
   static constexpr unsigned min_size = (size)
 
diff --git a/src/hb-map.hh b/src/hb-map.hh
index 26e4930..8c8db4d 100644
--- a/src/hb-map.hh
+++ b/src/hb-map.hh
@@ -46,16 +46,13 @@
   static_assert (hb_is_integral (K) || hb_is_pointer (K), "");
   static_assert (hb_is_integral (V) || hb_is_pointer (V), "");
 
-  /* TODO If key type is a pointer, keep hash in item_t and use to:
-   * 1. avoid rehashing when resizing table, and
-   * 2. compare hash before comparing keys, for speed.
-   */
   struct item_t
   {
     K key;
     V value;
+    uint32_t hash;
 
-    void clear () { key = kINVALID; value = vINVALID; }
+    void clear () { key = kINVALID; value = vINVALID; hash = 0; }
 
     bool operator == (K o) { return hb_deref (key) == hb_deref (o); }
     bool operator == (const item_t &o) { return *this == o.key; }
@@ -137,7 +134,9 @@
     if (old_items)
       for (unsigned int i = 0; i < old_size; i++)
 	if (old_items[i].is_real ())
-	  set (old_items[i].key, old_items[i].value);
+	  set_with_hash (old_items[i].key,
+                         old_items[i].hash,
+                         old_items[i].value);
 
     free (old_items);
 
@@ -146,29 +145,9 @@
 
   void set (K key, V value)
   {
-    if (unlikely (!successful)) return;
-    if (unlikely (key == kINVALID)) return;
-    if ((occupancy + occupancy / 2) >= mask && !resize ()) return;
-    unsigned int i = bucket_for (key);
-
-    if (value == vINVALID && items[i].key != key)
-      return; /* Trying to delete non-existent key. */
-
-    if (!items[i].is_unused ())
-    {
-      occupancy--;
-      if (items[i].is_tombstone ())
-	population--;
-    }
-
-    items[i].key = key;
-    items[i].value = value;
-
-    occupancy++;
-    if (!items[i].is_tombstone ())
-      population++;
-
+    set_with_hash (key, hb_hash (key), value);
   }
+
   V get (K key) const
   {
     if (unlikely (!items)) return vINVALID;
@@ -237,14 +216,45 @@
 
   protected:
 
+  void set_with_hash (K key, uint32_t hash, V value)
+  {
+    if (unlikely (!successful)) return;
+    if (unlikely (key == kINVALID)) return;
+    if ((occupancy + occupancy / 2) >= mask && !resize ()) return;
+    unsigned int i = bucket_for_hash (key, hash);
+
+    if (value == vINVALID && items[i].key != key)
+      return; /* Trying to delete non-existent key. */
+
+    if (!items[i].is_unused ())
+    {
+      occupancy--;
+      if (items[i].is_tombstone ())
+	population--;
+    }
+
+    items[i].key = key;
+    items[i].value = value;
+    items[i].hash = hash;
+
+    occupancy++;
+    if (!items[i].is_tombstone ())
+      population++;
+  }
+
   unsigned int bucket_for (K key) const
   {
-    unsigned int i = hb_hash (key) % prime;
+    return bucket_for_hash (key, hb_hash (key));
+  }
+
+  unsigned int bucket_for_hash (K key, uint32_t hash) const
+  {
+    unsigned int i = hash % prime;
     unsigned int step = 0;
     unsigned int tombstone = (unsigned) -1;
     while (!items[i].is_unused ())
     {
-      if (items[i] == key)
+      if (items[i].hash == hash && items[i] == key)
 	return i;
       if (tombstone == (unsigned) -1 && items[i].is_tombstone ())
 	tombstone = i;
diff --git a/src/hb-mutex.hh b/src/hb-mutex.hh
index e136267..e7f8b1c 100644
--- a/src/hb-mutex.hh
+++ b/src/hb-mutex.hh
@@ -92,25 +92,7 @@
 #define hb_mutex_impl_finish(M)	HB_STMT_START {} HB_STMT_END
 
 
-#elif !defined(HB_NO_MT)
-
-#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD)
-# include <sched.h>
-# define HB_SCHED_YIELD() sched_yield ()
-#else
-# define HB_SCHED_YIELD() HB_STMT_START {} HB_STMT_END
-#endif
-
-#define HB_MUTEX_INT_NIL 1 /* Warn that fallback implementation is in use. */
-typedef volatile int hb_mutex_impl_t;
-#define HB_MUTEX_IMPL_INIT	0
-#define hb_mutex_impl_init(M)	*(M) = 0
-#define hb_mutex_impl_lock(M)	HB_STMT_START { while (*(M)) HB_SCHED_YIELD (); (*(M))++; } HB_STMT_END
-#define hb_mutex_impl_unlock(M)	(*(M))--
-#define hb_mutex_impl_finish(M)	HB_STMT_START {} HB_STMT_END
-
-
-#else /* HB_NO_MT */
+#elif defined(HB_NO_MT)
 
 typedef int hb_mutex_impl_t;
 #define HB_MUTEX_IMPL_INIT	0
@@ -120,6 +102,11 @@
 #define hb_mutex_impl_finish(M)	HB_STMT_START {} HB_STMT_END
 
 
+#else
+
+#error "Could not find any system to define mutex macros."
+#error "Check hb-mutex.hh for possible resolutions."
+
 #endif
 
 
diff --git a/src/hb-ot-cff1-table.cc b/src/hb-ot-cff1-table.cc
index d1e4625..55abd11 100644
--- a/src/hb-ot-cff1-table.cc
+++ b/src/hb-ot-cff1-table.cc
@@ -336,7 +336,7 @@
   else
   {
     extents->y_bearing = font->em_scalef_y (bounds.max.y.to_real ());
-    extents->height = font->em_scalef_x (bounds.min.y.to_real () - bounds.max.y.to_real ());
+    extents->height = font->em_scalef_y (bounds.min.y.to_real () - bounds.max.y.to_real ());
   }
 
   return true;
diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh
index e664615..7eddb2c 100644
--- a/src/hb-ot-cmap-table.hh
+++ b/src/hb-ot-cmap-table.hh
@@ -342,14 +342,22 @@
 	count--; /* Skip sentinel segment. */
       for (unsigned int i = 0; i < count; i++)
       {
+	hb_codepoint_t start = this->startCount[i];
+	hb_codepoint_t end = this->endCount[i];
 	unsigned int rangeOffset = this->idRangeOffset[i];
 	if (rangeOffset == 0)
-	  out->add_range (this->startCount[i], this->endCount[i]);
+	{
+	  for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++)
+	  {
+	    hb_codepoint_t gid = (codepoint + this->idDelta[i]) & 0xFFFFu;
+	    if (unlikely (!gid))
+	      continue;
+	    out->add (codepoint);
+	  }
+	}
 	else
 	{
-	  for (hb_codepoint_t codepoint = this->startCount[i];
-	       codepoint <= this->endCount[i];
-	       codepoint++)
+	  for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++)
 	  {
 	    unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount;
 	    if (unlikely (index >= this->glyphIdArrayLength))
@@ -522,10 +530,18 @@
 
   void collect_unicodes (hb_set_t *out) const
   {
-    for (unsigned int i = 0; i < this->groups.len; i++) {
-      out->add_range (this->groups[i].startCharCode,
-		      hb_min ((hb_codepoint_t) this->groups[i].endCharCode,
-			   (hb_codepoint_t) HB_UNICODE_MAX));
+    for (unsigned int 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);
+      for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++)
+      {
+	hb_codepoint_t gid = T::group_get_glyph (this->groups[i], codepoint);
+	if (unlikely (!gid))
+	  continue;
+	out->add (codepoint);
+      }
     }
   }
 
@@ -925,9 +941,9 @@
     if (unlikely (!c->extend_min (*this))) return;
     this->format = 14;
 
-    const CmapSubtableFormat14 *src_tbl = reinterpret_cast<const CmapSubtableFormat14*> (src_base);
-    for (const VariationSelectorRecord& _ : src_tbl->record)
-      c->copy (_, unicodes, glyphs, glyph_map, src_base, this);
+    auto src_tbl = reinterpret_cast<const CmapSubtableFormat14*> (src_base);
+    c->copy_all (hb_iter (src_tbl->record),
+		 unicodes, glyphs, glyph_map, src_base, this);
 
     if (c->length () - table_initpos == CmapSubtableFormat14::min_size)
       c->revert (snap);
diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh
index f5a64b0..571e50e 100644
--- a/src/hb-ot-glyf-table.hh
+++ b/src/hb-ot-glyf-table.hh
@@ -360,9 +360,9 @@
   {
     typedef const CompositeGlyphChain *__item_t__;
     composite_iter_t (hb_bytes_t glyph_, __item_t__ current_) :
-      glyph (glyph_), current (current_), checker (range_checker_t (glyph.arrayZ, glyph.length))
+      glyph (glyph_), current (current_)
     { if (!in_range (current)) current = nullptr; }
-    composite_iter_t () : glyph (hb_bytes_t ()), current (nullptr), checker (range_checker_t (nullptr, 0)) {}
+    composite_iter_t () : glyph (hb_bytes_t ()), current (nullptr) {}
 
     const CompositeGlyphChain &__item__ () const { return *current; }
     bool __more__ () const { return current; }
@@ -380,14 +380,13 @@
 
     bool in_range (const CompositeGlyphChain *composite) const
     {
-      return checker.in_range (composite, CompositeGlyphChain::min_size)
-	  && checker.in_range (composite, composite->get_size ());
+      return glyph.in_range (composite, CompositeGlyphChain::min_size)
+	  && glyph.in_range (composite, composite->get_size ());
     }
 
     private:
     hb_bytes_t glyph;
     __item_t__ current;
-    range_checker_t checker;
   };
 
   struct Glyph
@@ -537,7 +536,7 @@
       template <typename T>
       static bool read_points (const HBUINT8 *&p /* IN/OUT */,
 			       contour_point_vector_t &points_ /* IN/OUT */,
-			       const range_checker_t &checker)
+			       const hb_bytes_t &bytes)
       {
 	T coord_setter;
 	float v = 0;
@@ -546,7 +545,7 @@
 	  uint8_t flag = points_[i].flag;
 	  if (coord_setter.is_short (flag))
 	  {
-	    if (unlikely (!checker.in_range (p))) return false;
+	    if (unlikely (!bytes.in_range (p))) return false;
 	    if (coord_setter.is_same (flag))
 	      v += *p++;
 	    else
@@ -556,7 +555,7 @@
 	  {
 	    if (!coord_setter.is_same (flag))
 	    {
-	      if (unlikely (!checker.in_range ((const HBUINT16 *) p))) return false;
+	      if (unlikely (!bytes.in_range ((const HBUINT16 *) p))) return false;
 	      v += *(const HBINT16 *) p;
 	      p += HBINT16::static_size;
 	    }
@@ -571,9 +570,8 @@
 			       const bool phantom_only=false) const
       {
 	const HBUINT16 *endPtsOfContours = &StructAfter<HBUINT16> (header);
-	range_checker_t checker (bytes.arrayZ, bytes.length);
 	int num_contours = header.numberOfContours;
-	if (unlikely (!checker.in_range (&endPtsOfContours[num_contours + 1]))) return false;
+	if (unlikely (!bytes.in_range (&endPtsOfContours[num_contours + 1]))) return false;
 	unsigned int num_points = endPtsOfContours[num_contours - 1] + 1;
 
 	points_.resize (num_points + PHANTOM_COUNT);
@@ -593,12 +591,12 @@
 	/* Read flags */
 	for (unsigned int i = 0; i < num_points; i++)
 	{
-	  if (unlikely (!checker.in_range (p))) return false;
+	  if (unlikely (!bytes.in_range (p))) return false;
 	  uint8_t flag = *p++;
 	  points_[i].flag = flag;
 	  if (flag & FLAG_REPEAT)
 	  {
-	    if (unlikely (!checker.in_range (p))) return false;
+	    if (unlikely (!bytes.in_range (p))) return false;
 	    unsigned int repeat_count = *p++;
 	    while ((repeat_count-- > 0) && (++i < num_points))
 	      points_[i].flag = flag;
@@ -606,8 +604,8 @@
 	}
 
 	/* Read x & y coordinates */
-	return (read_points<x_setter_t> (p, points_, checker) &&
-		read_points<y_setter_t> (p, points_, checker));
+	return (read_points<x_setter_t> (p, points_, bytes) &&
+		read_points<y_setter_t> (p, points_, bytes));
       }
     };
 
@@ -808,20 +806,36 @@
 
     struct contour_bounds_t
     {
-      contour_bounds_t () { min.x = min.y = FLT_MAX; max.x = max.y = -FLT_MAX; }
+      contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; }
 
       void add (const contour_point_t &p)
       {
-	min.x = hb_min (min.x, p.x);
-	min.y = hb_min (min.y, p.y);
-	max.x = hb_max (max.x, p.x);
-	max.y = hb_max (max.y, p.y);
+	min_x = hb_min (min_x, p.x);
+	min_y = hb_min (min_y, p.y);
+	max_x = hb_max (max_x, p.x);
+	max_y = hb_max (max_y, p.y);
       }
 
-      bool empty () const { return (min.x >= max.x) || (min.y >= max.y); }
+      bool empty () const { return (min_x >= max_x) || (min_y >= max_y); }
 
-      contour_point_t	min;
-      contour_point_t	max;
+      void get_extents (hb_font_t *font, hb_glyph_extents_t *extents)
+      {
+	if (unlikely (empty ()))
+	{
+	  extents->width = 0;
+	  extents->x_bearing = 0;
+	  extents->height = 0;
+	  extents->y_bearing = 0;
+	  return;
+	}
+	extents->x_bearing = font->em_scalef_x (min_x);
+	extents->width = font->em_scalef_x (max_x - min_x);
+	extents->y_bearing = font->em_scalef_y (max_y);
+	extents->height = font->em_scalef_y (min_y - max_y);
+      }
+
+      protected:
+      float min_x, min_y, max_x, max_y;
     };
 
 #ifndef HB_NO_VAR
@@ -919,27 +933,7 @@
 	contour_bounds_t bounds;
 	for (unsigned int i = 0; i + PHANTOM_COUNT < all_points.length; i++)
 	  bounds.add (all_points[i]);
-
-	if (bounds.min.x > bounds.max.x)
-	{
-	  extents->width = 0;
-	  extents->x_bearing = 0;
-	}
-	else
-	{
-	  extents->x_bearing = font->em_scalef_x (bounds.min.x);
-	  extents->width = font->em_scalef_x (bounds.max.x - bounds.min.x);
-	}
-	if (bounds.min.y > bounds.max.y)
-	{
-	  extents->height = 0;
-	  extents->y_bearing = 0;
-	}
-	else
-	{
-	  extents->y_bearing = font->em_scalef_y (bounds.max.y);
-	  extents->height = font->em_scalef_y (bounds.min.y - bounds.max.y);
-	}
+	bounds.get_extents (font, extents);
       }
       if (phantoms)
 	for (unsigned int i = 0; i < PHANTOM_COUNT; i++)
@@ -953,7 +947,7 @@
 
     bool get_extents_var (hb_font_t *font, hb_codepoint_t gid,
 			  hb_glyph_extents_t *extents) const
-    { return get_var_extents_and_phantoms (font, gid,  extents); }
+    { return get_var_extents_and_phantoms (font, gid, extents); }
 #endif
 
     public:
diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh
index f2f3b05..fa08140 100644
--- a/src/hb-ot-layout-common.hh
+++ b/src/hb-ot-layout-common.hh
@@ -66,6 +66,23 @@
 
 #define NOT_COVERED		((unsigned int) -1)
 
+
+template<typename Iterator>
+static inline void Coverage_serialize (hb_serialize_context_t *c,
+				       Iterator it);
+
+template<typename Iterator>
+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_set_t &glyphset,
+                                          const hb_map_t &gid_klass_map,
+                                          hb_sorted_vector_t<HBGlyphID> glyphs,
+                                          hb_sorted_vector_t<unsigned> klasses,
+                                          hb_map_t *klass_map /*INOUT*/);
+
+
 template<typename OutputArray>
 struct subset_offset_array_t
 {
@@ -120,7 +137,6 @@
 }
 HB_FUNCOBJ (subset_offset_array);
 
-
 /*
  *
  * OpenType Layout Common Table Formats
@@ -137,6 +153,26 @@
   const void *list_base;
 };
 
+struct RecordList_subset_context_t {
+
+  RecordList_subset_context_t() : script_count (0), langsys_count (0)
+  {}
+
+  bool visitScript ()
+  {
+    return script_count++ < HB_MAX_SCRIPTS;
+  }
+
+  bool visitLangSys ()
+  {
+    return langsys_count++ < HB_MAX_LANGSYS;
+  }
+
+  private:
+  unsigned int script_count;
+  unsigned int langsys_count;
+};
+
 template <typename Type>
 struct Record
 {
@@ -193,11 +229,26 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    auto *out = c->serializer->embed (*this);
-    if (unlikely (!out)) return_trace (false);
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+
+    RecordList_subset_context_t record_list_context;
+
     unsigned int count = this->len;
     for (unsigned int i = 0; i < count; i++)
-      out->get_offset (i).serialize_subset (c, this->get_offset (i), this, out);
+    {
+      auto *record = out->serialize_append (c->serializer);
+      if (unlikely (!record)) return false;
+      auto snap = c->serializer->snapshot ();
+      if (record->offset.serialize_subset (c, this->get_offset (i), this, out, &record_list_context))
+      {
+        record->tag = this->get_tag(i);
+        continue;
+      }
+      out->pop ();
+      c->serializer->revert (snap);
+    }
+
     return_trace (true);
   }
 
@@ -262,7 +313,6 @@
 struct LangSys;
 struct Feature;
 
-
 struct LangSys
 {
   unsigned int get_feature_count () const
@@ -329,15 +379,33 @@
   bool has_default_lang_sys () const           { return defaultLangSys != 0; }
   const LangSys& get_default_lang_sys () const { return this+defaultLangSys; }
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c, RecordList_subset_context_t *record_list_context) const
   {
     TRACE_SUBSET (this);
-    auto *out = c->serializer->embed (*this);
-    if (unlikely (!out)) return_trace (false);
+    if (!record_list_context->visitScript ()) return_trace (false);
+
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+
     out->defaultLangSys.serialize_copy (c->serializer, defaultLangSys, this, out);
-    unsigned int count = langSys.len;
-    for (unsigned int i = 0; i < count; i++)
-      out->langSys.arrayZ[i].offset.serialize_copy (c->serializer, langSys[i].offset, this, out);
+
+    for (const auto &src: langSys)
+    {
+      if (!record_list_context->visitLangSys ()) {
+        continue;
+      }
+
+      auto snap = c->serializer->snapshot ();
+      auto *lang_sys = c->serializer->embed (src);
+
+      if (likely(lang_sys)
+          && lang_sys->offset.serialize_copy (c->serializer, src.offset, this, out))
+      {
+        out->langSys.len++;
+        continue;
+      }
+      c->serializer->revert (snap);
+    }
     return_trace (true);
   }
 
@@ -614,7 +682,7 @@
   const FeatureParams &get_feature_params () const
   { return this+featureParams; }
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c, RecordList_subset_context_t *r) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (*this);
@@ -1127,6 +1195,23 @@
     }
   }
 
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    auto it =
+    + iter ()
+    | hb_filter (glyphset)
+    | hb_map_retains_sorting (glyph_map)
+    ;
+
+    bool ret = bool (it);
+    Coverage_serialize (c->serializer, it);
+    return_trace (ret);
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -1245,15 +1330,51 @@
   DEFINE_SIZE_UNION (2, format);
 };
 
+template<typename Iterator>
+static inline void
+Coverage_serialize (hb_serialize_context_t *c,
+                    Iterator it)
+{ c->start_embed<Coverage> ()->serialize (c, it); }
+
+static void ClassDef_remap_and_serialize (hb_serialize_context_t *c,
+                                          const hb_set_t &glyphset,
+                                          const hb_map_t &gid_klass_map,
+                                          hb_sorted_vector_t<HBGlyphID> glyphs,
+                                          hb_sorted_vector_t<unsigned> klasses,
+                                          hb_map_t *klass_map /*INOUT*/)
+{
+  bool has_no_match = glyphset.get_population () > gid_klass_map.get_population ();
+  
+  hb_map_t m;
+  if (!klass_map) klass_map = &m;
+
+  if (has_no_match) klass_map->set (0, 0);
+  unsigned idx = klass_map->has (0) ? 1 : 0;
+  for (const unsigned k: klasses.iter ())
+  {
+    if (klass_map->has (k)) continue;
+    klass_map->set (k, idx);
+    idx++;
+  }
+  
+  auto it =
+  + glyphs.iter ()
+  | hb_map_retains_sorting ([&] (const HBGlyphID& gid) -> hb_pair_t<hb_codepoint_t, HBUINT16>
+                            {
+                              HBUINT16 new_klass;
+                              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);
+}
 
 /*
  * Class Definition Table
  */
 
-static inline void ClassDef_serialize (hb_serialize_context_t *c,
-				       hb_array_t<const HBGlyphID> glyphs,
-				       hb_array_t<const HBUINT16> klasses);
-
 struct ClassDefFormat1
 {
   friend struct ClassDef;
@@ -1264,53 +1385,53 @@
     return classValue[(unsigned int) (glyph_id - startGlyph)];
   }
 
+  template<typename Iterator,
+	   hb_requires (hb_is_iterator (Iterator))>
   bool serialize (hb_serialize_context_t *c,
-		  hb_array_t<const HBGlyphID> glyphs,
-		  hb_array_t<const HBUINT16> klasses)
+                  Iterator it)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
 
-    if (unlikely (!glyphs))
+    if (unlikely (!it))
     {
       startGlyph = 0;
       classValue.len = 0;
       return_trace (true);
     }
 
-    hb_codepoint_t glyph_min = +glyphs | hb_reduce (hb_min, 0xFFFFu);
-    hb_codepoint_t glyph_max = +glyphs | hb_reduce (hb_max, 0u);
-
-    startGlyph = glyph_min;
-    c->check_assign (classValue.len, glyph_max - glyph_min + 1);
-    if (unlikely (!c->extend (classValue))) return_trace (false);
-
-    for (unsigned int i = 0; i < glyphs.length; i++)
-      classValue[glyphs[i] - glyph_min] = klasses[i];
-
+    startGlyph = (*it).first;
+    classValue.serialize (c, + it
+                             | hb_map (hb_second));
     return_trace (true);
   }
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               hb_map_t *klass_map = nullptr /*OUT*/) const
   {
     TRACE_SUBSET (this);
     const hb_set_t &glyphset = *c->plan->glyphset ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
+   
     hb_sorted_vector_t<HBGlyphID> glyphs;
-    hb_vector_t<HBUINT16> klasses;
+    hb_sorted_vector_t<unsigned> orig_klasses;
+    hb_map_t gid_org_klass_map;
 
     hb_codepoint_t start = startGlyph;
     hb_codepoint_t end   = start + classValue.len;
-    for (hb_codepoint_t g = start; g < end; g++)
+    for (const hb_codepoint_t gid : + hb_range (start, end)
+				    | hb_filter (glyphset))
     {
-      if (!glyphset.has (g)) continue;
-      unsigned int value = classValue[g - start];
-      if (!value) continue;
-      glyphs.push(glyph_map[g]);
-      klasses.push(value);
+      unsigned klass = classValue[gid - start];
+      if (!klass) continue;
+
+      glyphs.push (glyph_map[gid]);
+      gid_org_klass_map.set (glyph_map[gid], klass);
+      orig_klasses.push (klass);
     }
-    c->serializer->propagate_error (glyphs, klasses);
-    ClassDef_serialize (c->serializer, glyphs, klasses);
+
+    ClassDef_remap_and_serialize (c->serializer, glyphset, gid_org_klass_map,
+                                  glyphs, orig_klasses, klass_map);
     return_trace ((bool) glyphs);
   }
 
@@ -1400,70 +1521,89 @@
     return rangeRecord.bsearch (glyph_id).value;
   }
 
+  template<typename Iterator,
+	   hb_requires (hb_is_iterator (Iterator))>
   bool serialize (hb_serialize_context_t *c,
-		  hb_array_t<const HBGlyphID> glyphs,
-		  hb_array_t<const HBUINT16> klasses)
+                  Iterator it)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
 
-    if (unlikely (!glyphs))
+    if (unlikely (!it))
     {
       rangeRecord.len = 0;
       return_trace (true);
     }
 
-    unsigned int count = glyphs.len ();
-    unsigned int num_ranges = 1;
-    for (unsigned int i = 1; i < count; i++)
-      if (glyphs[i - 1] + 1 != glyphs[i] ||
-	  klasses[i - 1] != klasses[i])
-	num_ranges++;
-    rangeRecord.len = num_ranges;
-    if (unlikely (!c->extend (rangeRecord))) return_trace (false);
+    unsigned num_ranges = 1;
+    hb_codepoint_t prev_gid = (*it).first;
+    unsigned prev_klass = (*it).second;
 
-    unsigned int range = 0;
-    rangeRecord[range].start = glyphs[0];
-    rangeRecord[range].value = klasses[0];
-    for (unsigned int i = 1; i < count; i++)
+    RangeRecord range_rec;
+    range_rec.start = prev_gid;
+    range_rec.end = prev_gid;
+    range_rec.value = prev_klass;
+
+    RangeRecord *record = c->copy (range_rec);
+    if (unlikely (!record)) return_trace (false);
+
+    for (const auto gid_klass_pair : + (++it))
     {
-      if (glyphs[i - 1] + 1 != glyphs[i] ||
-	  klasses[i - 1] != klasses[i])
+      hb_codepoint_t cur_gid = gid_klass_pair.first;
+      unsigned cur_klass = gid_klass_pair.second;
+
+      if (cur_gid != prev_gid + 1 ||
+          cur_klass != prev_klass)
       {
-	rangeRecord[range].end = glyphs[i - 1];
-	range++;
-	rangeRecord[range].start = glyphs[i];
-	rangeRecord[range].value = klasses[i];
+        if (unlikely (!record)) break;
+        record->end = prev_gid;
+        num_ranges++;
+
+        range_rec.start = cur_gid;
+        range_rec.end = cur_gid;
+        range_rec.value = cur_klass;
+
+        record = c->copy (range_rec);
       }
+
+      prev_klass = cur_klass;
+      prev_gid = cur_gid;
     }
-    rangeRecord[range].end = glyphs[count - 1];
+
+    if (likely (record)) record->end = prev_gid;
+    rangeRecord.len = num_ranges;
     return_trace (true);
   }
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               hb_map_t *klass_map = nullptr /*OUT*/) const
   {
     TRACE_SUBSET (this);
     const hb_set_t &glyphset = *c->plan->glyphset ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
-    hb_vector_t<HBGlyphID> glyphs;
-    hb_vector_t<HBUINT16> klasses;
 
-    unsigned int count = rangeRecord.len;
-    for (unsigned int i = 0; i < count; i++)
+    hb_sorted_vector_t<HBGlyphID> glyphs;
+    hb_sorted_vector_t<unsigned> orig_klasses;
+    hb_map_t gid_org_klass_map;
+
+    unsigned count = rangeRecord.len;
+    for (unsigned i = 0; i < count; i++)
     {
-      unsigned int value = rangeRecord[i].value;
-      if (!value) continue;
+      unsigned klass = rangeRecord[i].value;
+      if (!klass) continue;
       hb_codepoint_t start = rangeRecord[i].start;
       hb_codepoint_t end   = rangeRecord[i].end + 1;
       for (hb_codepoint_t g = start; g < end; g++)
       {
 	if (!glyphset.has (g)) continue;
 	glyphs.push (glyph_map[g]);
-	klasses.push (value);
+        gid_org_klass_map.set (glyph_map[g], klass);
+        orig_klasses.push (klass);
       }
     }
-    c->serializer->propagate_error (glyphs, klasses);
-    ClassDef_serialize (c->serializer, glyphs, klasses);
+
+    ClassDef_remap_and_serialize (c->serializer, glyphset, gid_org_klass_map,
+                                  glyphs, orig_klasses, klass_map);
     return_trace ((bool) glyphs);
   }
 
@@ -1560,25 +1700,36 @@
     }
   }
 
-  bool serialize (hb_serialize_context_t *c,
-		  hb_array_t<const HBGlyphID> glyphs,
-		  hb_array_t<const HBUINT16> klasses)
+  template<typename Iterator,
+	   hb_requires (hb_is_iterator (Iterator))>
+  bool serialize (hb_serialize_context_t *c, Iterator it)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
 
-    unsigned int format = 2;
-    if (likely (glyphs))
+    unsigned format = 2;
+    if (likely (it))
     {
-      hb_codepoint_t glyph_min = +glyphs | hb_reduce (hb_min, 0xFFFFu);
-      hb_codepoint_t glyph_max = +glyphs | hb_reduce (hb_max, 0u);
+      hb_codepoint_t glyph_min = (*it).first;
+      hb_codepoint_t glyph_max = + it
+				 | hb_map (hb_first)
+                                 | hb_reduce (hb_max, 0u);
 
-      unsigned int count = glyphs.len ();
-      unsigned int num_ranges = 1;
-      for (unsigned int i = 1; i < count; i++)
-	if (glyphs[i - 1] + 1 != glyphs[i] ||
-	    klasses[i - 1] != klasses[i])
-	  num_ranges++;
+      unsigned num_ranges = 1;
+      hb_codepoint_t prev_gid = glyph_min;
+      unsigned prev_klass = (*it).second;
+
+      for (const auto gid_klass_pair : it)
+      {
+        hb_codepoint_t cur_gid = gid_klass_pair.first;
+        unsigned cur_klass = gid_klass_pair.second;
+        if (cur_gid != prev_gid + 1 ||
+            cur_klass != prev_klass)
+          num_ranges++;
+
+        prev_gid = cur_gid;
+        prev_klass = cur_klass;
+      }
 
       if (1 + (glyph_max - glyph_min + 1) < num_ranges * 3)
 	format = 1;
@@ -1587,18 +1738,19 @@
 
     switch (u.format)
     {
-    case 1: return_trace (u.format1.serialize (c, glyphs, klasses));
-    case 2: return_trace (u.format2.serialize (c, glyphs, klasses));
+    case 1: return_trace (u.format1.serialize (c, it));
+    case 2: return_trace (u.format2.serialize (c, it));
     default:return_trace (false);
     }
   }
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               hb_map_t *klass_map = nullptr /*OUT*/) const
   {
     TRACE_SUBSET (this);
     switch (u.format) {
-    case 1: return_trace (u.format1.subset (c));
-    case 2: return_trace (u.format2.subset (c));
+    case 1: return_trace (u.format1.subset (c, klass_map));
+    case 2: return_trace (u.format2.subset (c, klass_map));
     default:return_trace (false);
     }
   }
@@ -1665,10 +1817,10 @@
   DEFINE_SIZE_UNION (2, format);
 };
 
+template<typename Iterator>
 static inline void ClassDef_serialize (hb_serialize_context_t *c,
-				       hb_array_t<const HBGlyphID> glyphs,
-				       hb_array_t<const HBUINT16> klasses)
-{ c->start_embed<ClassDef> ()->serialize (c, glyphs, klasses); }
+                                       Iterator it)
+{ c->start_embed<ClassDef> ()->serialize (c, it); }
 
 
 /*
diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index 2b535af..416d79c 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -544,8 +544,7 @@
     if (unlikely (!c->extend_min (*this))) return;
     if (unlikely (!c->check_assign (valueFormat, valFormat))) return;
 
-    for (const auto &_ : hb_second (*it))
-      c->copy (_);
+    c->copy_all (hb_second (*it));
 
     auto glyphs =
     + it
@@ -558,7 +557,7 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+    const hb_set_t &glyphset = *c->plan->glyphset ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     auto it =
@@ -632,9 +631,7 @@
     if (unlikely (!c->check_assign (valueFormat, valFormat))) return;
     if (unlikely (!c->check_assign (valueCount, it.len ()))) return;
 
-    for (const auto iter : it)
-      for (const auto &_ : iter.second)
-	c->copy (_);
+    for (auto iter : it) c->copy_all (iter.second);
 
     auto glyphs =
     + it
@@ -647,7 +644,7 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+    const hb_set_t &glyphset = *c->plan->glyphset ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     unsigned sub_length = valueFormat.get_len ();
@@ -761,6 +758,18 @@
 {
   friend struct PairSet;
 
+  bool serialize (hb_serialize_context_t *c,
+                  unsigned length,
+                  const hb_map_t &glyph_map) const
+  {
+    TRACE_SERIALIZE (this);
+    auto *out = c->start_embed (*this);
+    if (unlikely (!c->extend_min (out))) return_trace (false);
+    
+    out->secondGlyph = glyph_map[secondGlyph];
+    return_trace (c->copy (values, length));
+  }
+
   protected:
   HBGlyphID	secondGlyph;		/* GlyphID of second glyph in the
 					 * pair--first glyph is listed in the
@@ -846,6 +855,37 @@
     return_trace (false);
   }
 
+  bool subset (hb_subset_context_t *c,
+               const ValueFormat valueFormats[2]) const
+  {
+    TRACE_SUBSET (this);
+    auto snap = c->serializer->snapshot ();
+
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+    out->len = 0;
+
+    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    unsigned len1 = valueFormats[0].get_len ();
+    unsigned len2 = valueFormats[1].get_len ();
+    unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2);
+
+    const PairValueRecord *record = &firstPairValueRecord;
+    unsigned count = len, num = 0;
+    for (unsigned i = 0; i < count; i++)
+    {
+      if (!glyphset.has (record->secondGlyph)) continue;
+      if (record->serialize (c->serializer, len1 + len2, glyph_map)) num++;
+      record = &StructAtOffset<const PairValueRecord> (record, record_size);
+    }
+
+    out->len = num;
+    if (!num) c->serializer->revert (snap);
+    return_trace (num);
+  }
+
   struct sanitize_closure_t
   {
     const void *base;
@@ -919,8 +959,43 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    // TODO(subset)
-    return_trace (false);
+
+    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+    out->format = format;
+    out->valueFormat[0] = valueFormat[0];
+    out->valueFormat[1] = valueFormat[1];
+
+    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
+
+    + hb_zip (this+coverage, pairSet)
+    | hb_filter (glyphset, hb_first)
+    | hb_filter ([this, c, out] (const OffsetTo<PairSet>& _)
+		 {
+		   auto *o = out->pairSet.serialize_append (c->serializer);
+		   if (unlikely (!o)) return false;
+		   auto snap = c->serializer->snapshot ();
+		   bool ret = o->serialize_subset (c, _, this, out, valueFormat);
+		   if (!ret)
+		   {
+		     out->pairSet.pop ();
+		     c->serializer->revert (snap);
+		   }
+		   return ret;
+		 },
+		 hb_second)
+    | hb_map (hb_first)
+    | hb_map (glyph_map)
+    | hb_sink (new_coverage)
+    ;
+
+    out->coverage.serialize (c->serializer, out)
+		 .serialize (c->serializer, new_coverage.iter ());
+
+    return_trace (bool (new_coverage));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -1011,8 +1086,49 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    // TODO(subset)
-    return_trace (false);
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+    out->format = format;
+    out->valueFormat1 = valueFormat1;
+    out->valueFormat2 = valueFormat2;
+
+    hb_map_t klass1_map;
+    out->classDef1.serialize_subset (c, classDef1, this, out, &klass1_map);
+    out->class1Count = klass1_map.get_population ();
+
+    hb_map_t klass2_map;
+    out->classDef2.serialize_subset (c, classDef2, this, out, &klass2_map);
+    out->class2Count = klass2_map.get_population ();
+
+    unsigned record_len = valueFormat1.get_len () + valueFormat2.get_len ();
+
+    + hb_range ((unsigned) class1Count)
+    | hb_filter (klass1_map)
+    | hb_apply ([&] (const unsigned class1_idx)
+                {
+                  + hb_range ((unsigned) class2Count)
+                  | hb_filter (klass2_map)
+                  | hb_apply ([&] (const unsigned class2_idx)
+                              {
+                                unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * record_len;
+                                for (unsigned i = 0; i < record_len; i++)
+                                  c->serializer->copy (values[idx+i]);
+                              })
+                  ;
+                })
+    ;
+
+    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    auto it =
+    + hb_iter (this+coverage)
+    | hb_filter (glyphset)
+    | hb_map_retains_sorting (glyph_map)
+    ;
+
+    out->coverage.serialize (c->serializer, out).serialize (c->serializer, it);
+    return_trace (out->class1Count && out->class2Count && bool (it));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
diff --git a/src/hb-ot-layout-gsubgpos.hh b/src/hb-ot-layout-gsubgpos.hh
index c6e6ac9..82cf969 100644
--- a/src/hb-ot-layout-gsubgpos.hh
+++ b/src/hb-ot-layout-gsubgpos.hh
@@ -363,7 +363,11 @@
       matcher.set_syllable (start_index_ == c->buffer->idx ? c->buffer->cur().syllable () : 0);
     }
 
-    void reject () { num_items++; match_glyph_data--; }
+    void reject ()
+    {
+      num_items++;
+      if (match_glyph_data) match_glyph_data--;
+    }
 
     matcher_t::may_skip_t
     may_skip (const hb_glyph_info_t &info) const
@@ -387,7 +391,7 @@
 	     skip == matcher_t::SKIP_NO))
 	{
 	  num_items--;
-	  match_glyph_data++;
+	  if (match_glyph_data) match_glyph_data++;
 	  return true;
 	}
 
@@ -414,7 +418,7 @@
 	     skip == matcher_t::SKIP_NO))
 	{
 	  num_items--;
-	  match_glyph_data++;
+	  if (match_glyph_data) match_glyph_data++;
 	  return true;
 	}
 
@@ -712,11 +716,9 @@
 				     intersects_func_t intersects_func,
 				     const void *intersects_data)
 {
-  return
-  + hb_iter (values, count)
-  | hb_map ([&] (const HBUINT16 &_) { return intersects_func (glyphs, _, intersects_data); })
-  | hb_any
-  ;
+  for (const HBUINT16 &_ : + hb_iter (values, count))
+    if (intersects_func (glyphs, _, intersects_data)) return true;
+  return false;
 }
 
 
@@ -2002,6 +2004,83 @@
 					      lookup.arrayZ, lookup_context));
   }
 
+  template<typename Iterator,
+	   hb_requires (hb_is_iterator (Iterator))>
+  void serialize_array (hb_serialize_context_t *c,
+                        HBUINT16 len,
+                        Iterator it) const
+  {
+    c->copy (len);
+    for (const auto g : it)
+    {
+      HBUINT16 gid;
+      gid = g;
+      c->copy (gid);
+    }
+  }
+
+  ChainRule* copy (hb_serialize_context_t *c,
+		   const hb_map_t *backtrack_map,
+		   const hb_map_t *input_map = nullptr,
+		   const hb_map_t *lookahead_map = nullptr) const
+  {
+    TRACE_SERIALIZE (this);
+    auto *out = c->start_embed (this);
+    if (unlikely (!out)) return_trace (nullptr);
+
+    const hb_map_t *mapping = backtrack_map;
+    serialize_array (c, backtrack.len, + backtrack.iter ()
+				       | hb_map (mapping));
+
+    const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
+    if (input_map) mapping = input_map;
+    serialize_array (c, input.lenP1, + input.iter ()
+				     | hb_map (mapping));
+
+    const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (input);
+    if (lookahead_map) mapping = lookahead_map;
+    serialize_array (c, lookahead.len, + lookahead.iter ()
+				       | hb_map (mapping));
+
+    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
+    c->copy (lookup);
+
+    return_trace (out);
+  }
+
+  bool subset (hb_subset_context_t *c,
+               const hb_map_t *backtrack_map = nullptr,
+               const hb_map_t *input_map = nullptr,
+               const hb_map_t *lookahead_map = nullptr) const
+  {
+    TRACE_SUBSET (this);
+
+    const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
+    const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (input);
+
+    if (!backtrack_map)
+    {
+      const hb_set_t &glyphset = *c->plan->glyphset ();
+      if (!hb_all (backtrack, glyphset) ||
+          !hb_all (input, glyphset) ||
+          !hb_all (lookahead, glyphset))
+        return_trace (false);
+
+      copy (c->serializer, c->plan->glyph_map);
+    }
+    else
+    {
+      if (!hb_all (backtrack, backtrack_map) ||
+          !hb_all (input, input_map) ||
+          !hb_all (lookahead, lookahead_map))
+        return_trace (false);
+
+      copy (c->serializer, backtrack_map, input_map, lookahead_map);
+    }
+
+    return_trace (true);
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -2083,6 +2162,40 @@
     ;
   }
 
+  bool subset (hb_subset_context_t *c,
+               const hb_map_t *backtrack_klass_map = nullptr,
+               const hb_map_t *input_klass_map = nullptr,
+               const hb_map_t *lookahead_klass_map = nullptr) const
+  {
+    TRACE_SUBSET (this);
+
+    auto snap = c->serializer->snapshot ();
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+
+    for (const OffsetTo<ChainRule>& _ : rule)
+    {
+      if (!_) continue;
+      auto *o = out->rule.serialize_append (c->serializer);
+      if (unlikely (!o)) continue;
+
+      auto o_snap = c->serializer->snapshot ();
+      if (!o->serialize_subset (c, _, this, out,
+                                backtrack_klass_map,
+                                input_klass_map,
+                                lookahead_klass_map))
+      {
+        out->rule.pop ();
+        c->serializer->revert (o_snap);
+      }
+    }
+
+    bool ret = bool (out->rule);
+    if (!ret) c->serializer->revert (snap);
+
+    return_trace (ret);
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -2175,8 +2288,25 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    // TODO(subset)
-    return_trace (false);
+    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+    out->format = format;
+
+    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
+    + hb_zip (this+coverage, ruleSet)
+    | hb_filter (glyphset, hb_first)
+    | hb_filter (subset_offset_array (c, out->ruleSet, this, out), hb_second)
+    | hb_map (hb_first)
+    | hb_map (glyph_map)
+    | hb_sink (new_coverage)
+    ;
+
+    out->coverage.serialize (c->serializer, out)
+		 .serialize (c->serializer, new_coverage.iter ());
+    return_trace (bool (new_coverage));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -2314,8 +2444,54 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    // TODO(subset)
-    return_trace (false);
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+    out->format = format;
+    out->coverage.serialize_subset (c, coverage, this, out);
+
+    hb_map_t backtrack_klass_map;
+    out->backtrackClassDef.serialize_subset (c, backtrackClassDef, this, out, &backtrack_klass_map);
+
+    // subset inputClassDef based on glyphs survived in Coverage subsetting
+    hb_map_t input_klass_map;
+    out->inputClassDef.serialize_subset (c, inputClassDef, this, out, &input_klass_map);
+
+    hb_map_t lookahead_klass_map;
+    out->lookaheadClassDef.serialize_subset (c, lookaheadClassDef, this, out, &lookahead_klass_map);
+
+    hb_vector_t<unsigned> rulesets;
+    bool ret = true;
+    for (const OffsetTo<ChainRuleSet>& _ : + hb_enumerate (ruleSet)
+					   | hb_filter (input_klass_map, hb_first)
+					   | hb_map (hb_second))
+    {
+      auto *o = out->ruleSet.serialize_append (c->serializer);
+      if (unlikely (!o))
+      {
+        ret = false;
+        break;
+      }
+      if (!o->serialize_subset (c, _, this, out,
+                                &backtrack_klass_map,
+                                &input_klass_map,
+                                &lookahead_klass_map))
+      {
+        rulesets.push (0);
+      }
+      else rulesets.push (1);
+    }
+
+    if (!ret) return_trace (ret);
+
+    //prune empty trailing ruleSets
+    unsigned count = rulesets.length;
+    while (count > 0 && rulesets[count-1] == 0)
+    {
+      out->ruleSet.pop ();
+      count--;
+    }
+
+    return_trace (bool (out->ruleSet));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -2457,11 +2633,46 @@
 					      lookup.len, lookup.arrayZ, lookup_context));
   }
 
+  template<typename Iterator,
+	   hb_requires (hb_is_iterator (Iterator))>
+  bool serialize_coverage_offsets (hb_subset_context_t *c,
+                                   Iterator it,
+				   const void* src_base,
+				   const void* dst_base) const
+  {
+    TRACE_SERIALIZE (this);
+    auto *out = c->serializer->start_embed<OffsetArrayOf<Coverage>> ();
+
+    if (unlikely (!c->serializer->allocate_size<HBUINT16> (HBUINT16::static_size))) return_trace (false);
+
+    + it
+    | hb_apply (subset_offset_array (c, *out, src_base, dst_base))
+    ;
+
+    return_trace (out->len);
+  }
+
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    // TODO(subset)
-    return_trace (false);
+
+    auto *out = c->serializer->start_embed (this);
+    if (unlikely (!out)) return_trace (false);
+    if (unlikely (!c->serializer->embed (this->format))) return_trace (false);
+
+    if (!serialize_coverage_offsets (c, backtrack.iter (), this, out))
+      return_trace (false);
+
+    const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
+    if (!serialize_coverage_offsets (c, input.iter (), this, out))
+      return_trace (false);
+
+    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (input);
+    if (!serialize_coverage_offsets (c, lookahead.iter (), this, out))
+      return_trace (false);
+
+    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
+    return_trace (c->serializer->copy (lookup));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index fba3ad1..ac6551b 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -1223,7 +1223,7 @@
  * @lookup_index: The index of the feature lookup to query
  * @glyphs_before: (out): Array of glyphs preceding the substitution range
  * @glyphs_input: (out): Array of input glyphs that would be substituted by the lookup
- * @glyphs_after: (out): Array of glyphs following the substition range
+ * @glyphs_after: (out): Array of glyphs following the substitution range
  * @glyphs_output: (out): Array of glyphs that would be the substitued output of the lookup
  *
  * Fetches a list of all glyphs affected by the specified lookup in the
@@ -1957,7 +1957,7 @@
  *
  * Fetches a baseline value from the face.
  *
- * Return value: if found baseline value in the the font.
+ * Return value: if found baseline value in the font.
  *
  * Since: 2.6.0
  **/
diff --git a/src/hb-ot-name-table.hh b/src/hb-ot-name-table.hh
index 84be04c..c0f9fbc 100644
--- a/src/hb-ot-name-table.hh
+++ b/src/hb-ot-name-table.hh
@@ -191,7 +191,7 @@
 
     const void *dst_string_pool = &(this + this->stringOffset);
 
-    for (const auto &_ : it) c->copy (_, src_string_pool, dst_string_pool);
+    c->copy_all (it, src_string_pool, dst_string_pool);
 
     if (unlikely (c->ran_out_of_room)) return_trace (false);
 
diff --git a/src/hb-ot-post-table.hh b/src/hb-ot-post-table.hh
index 38302f5..661a917 100644
--- a/src/hb-ot-post-table.hh
+++ b/src/hb-ot-post-table.hh
@@ -35,8 +35,6 @@
 #undef HB_STRING_ARRAY_LIST
 #undef HB_STRING_ARRAY_NAME
 
-#define NUM_FORMAT1_NAMES 258
-
 /*
  * post -- PostScript
  * https://docs.microsoft.com/en-us/typography/opentype/spec/post
@@ -185,7 +183,7 @@
     unsigned int get_glyph_count () const
     {
       if (version == 0x00010000)
-	return NUM_FORMAT1_NAMES;
+	return format1_names_length;
 
       if (version == 0x00020000)
 	return glyphNameIndex->len;
@@ -213,7 +211,7 @@
     {
       if (version == 0x00010000)
       {
-	if (glyph >= NUM_FORMAT1_NAMES)
+	if (glyph >= format1_names_length)
 	  return hb_bytes_t ();
 
 	return format1_names (glyph);
@@ -223,9 +221,9 @@
 	return hb_bytes_t ();
 
       unsigned int index = glyphNameIndex->arrayZ[glyph];
-      if (index < NUM_FORMAT1_NAMES)
+      if (index < format1_names_length)
 	return format1_names (index);
-      index -= NUM_FORMAT1_NAMES;
+      index -= format1_names_length;
 
       if (index >= index_to_offset.length)
 	return hb_bytes_t ();
diff --git a/src/hb-ot-shape-complex-vowel-constraints.cc b/src/hb-ot-shape-complex-vowel-constraints.cc
index 2f80413..fc09ecc 100644
--- a/src/hb-ot-shape-complex-vowel-constraints.cc
+++ b/src/hb-ot-shape-complex-vowel-constraints.cc
@@ -2,15 +2,16 @@
 /*
  * The following functions are generated by running:
  *
- *   ./gen-vowel-constraints.py use Scripts.txt
+ *   ./gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt
  *
  * on files with these headers:
  *
- * # Copied from https://docs.microsoft.com/en-us/typography/script-development/use
- * # On October 23, 2018; with documentd dated 02/07/2018.
+ * # IndicShapingInvalidCluster.txt
+ * # Date: 2015-03-12, 21:17:00 GMT [AG]
+ * # Date: 2019-11-08, 23:22:00 GMT [AG]
  *
- * # Scripts-12.0.0.txt
- * # Date: 2019-01-28, 22:16:47 GMT
+ * # Scripts-12.1.0.txt
+ * # Date: 2019-04-01, 09:10:42 GMT
  */
 
 #include "hb.hh"
@@ -211,6 +212,22 @@
       processed = true;
       break;
 
+    case HB_SCRIPT_TAMIL:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	if (0x0B85u == buffer->cur ().codepoint &&
+	    0x0BC2u == buffer->cur (1).codepoint)
+	{
+	  buffer->next_glyph ();
+	  _output_dotted_circle (buffer);
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
     case HB_SCRIPT_TELUGU:
       for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
       {
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 5d9a70c..77358e5 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -166,7 +166,7 @@
     plan.apply_kerx = true;
 #endif
 
-  if (!plan.apply_kerx && !has_gpos_kern)
+  if (!plan.apply_kerx && (!has_gpos_kern || !plan.apply_gpos))
   {
     /* Apparently Apple applies kerx if GPOS kern was not applied. */
 #ifndef HB_NO_AAT_SHAPE
diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh
index 666b308..a76121d 100644
--- a/src/hb-ot-var-gvar-table.hh
+++ b/src/hb-ot-var-gvar-table.hh
@@ -78,23 +78,6 @@
   }
 };
 
-struct range_checker_t
-{
-  range_checker_t (const void *data_, unsigned int length_)
-    : data ((const char *) data_), length (length_) {}
-
-  template <typename T>
-  bool in_range (const T *p, unsigned int size = T::static_size) const
-  {
-    return ((const char *) p) >= data
-	&& ((const char *) p + size) <= data + length;
-  }
-
-  protected:
-  const char *data;
-  const unsigned int length;
-};
-
 struct Tuple : UnsizedArrayOf<F2DOT14> {};
 
 struct TuppleIndex : HBUINT16
@@ -233,10 +216,10 @@
     {
       if (var_data->has_shared_point_numbers ())
       {
-	range_checker_t checker (var_data, length);
+	hb_bytes_t bytes ((const char *) var_data, length);
 	const HBUINT8 *base = &(var_data+var_data->data);
 	const HBUINT8 *p = base;
-	if (!unpack_points (p, shared_indices, checker)) return false;
+	if (!unpack_points (p, shared_indices, bytes)) return false;
 	data_offset = p - base;
       }
       return true;
@@ -292,7 +275,7 @@
 
   static bool unpack_points (const HBUINT8 *&p /* IN/OUT */,
 			     hb_vector_t<unsigned int> &points /* OUT */,
-			     const range_checker_t &check)
+			     const hb_bytes_t &bytes)
   {
     enum packed_point_flag_t
     {
@@ -300,12 +283,12 @@
       POINT_RUN_COUNT_MASK = 0x7F
     };
 
-    if (unlikely (!check.in_range (p))) return false;
+    if (unlikely (!bytes.in_range (p))) return false;
 
     uint16_t count = *p++;
     if (count & POINTS_ARE_WORDS)
     {
-      if (unlikely (!check.in_range (p))) return false;
+      if (unlikely (!bytes.in_range (p))) return false;
       count = ((count & POINT_RUN_COUNT_MASK) << 8) | *p++;
     }
     points.resize (count);
@@ -314,7 +297,7 @@
     uint16_t i = 0;
     while (i < count)
     {
-      if (unlikely (!check.in_range (p))) return false;
+      if (unlikely (!bytes.in_range (p))) return false;
       uint16_t j;
       uint8_t control = *p++;
       uint16_t run_count = (control & POINT_RUN_COUNT_MASK) + 1;
@@ -322,7 +305,7 @@
       {
 	for (j = 0; j < run_count && i < count; j++, i++)
 	{
-	  if (unlikely (!check.in_range ((const HBUINT16 *) p)))
+	  if (unlikely (!bytes.in_range ((const HBUINT16 *) p)))
 	    return false;
 	  n += *(const HBUINT16 *)p;
 	  points[i] = n;
@@ -333,7 +316,7 @@
       {
 	for (j = 0; j < run_count && i < count; j++, i++)
 	{
-	  if (unlikely (!check.in_range (p))) return false;
+	  if (unlikely (!bytes.in_range (p))) return false;
 	  n += *p++;
 	  points[i] = n;
 	}
@@ -345,7 +328,7 @@
 
   static bool unpack_deltas (const HBUINT8 *&p /* IN/OUT */,
 			     hb_vector_t<int> &deltas /* IN/OUT */,
-			     const range_checker_t &check)
+			     const hb_bytes_t &bytes)
   {
     enum packed_delta_flag_t
     {
@@ -358,7 +341,7 @@
     unsigned int count = deltas.length;
     while (i < count)
     {
-      if (unlikely (!check.in_range (p))) return false;
+      if (unlikely (!bytes.in_range (p))) return false;
       uint8_t control = *p++;
       unsigned int run_count = (control & DELTA_RUN_COUNT_MASK) + 1;
       unsigned int j;
@@ -368,7 +351,7 @@
       else if (control & DELTAS_ARE_WORDS)
 	for (j = 0; j < run_count && i < count; j++, i++)
 	{
-	  if (unlikely (!check.in_range ((const HBUINT16 *) p)))
+	  if (unlikely (!bytes.in_range ((const HBUINT16 *) p)))
 	    return false;
 	  deltas[i] = *(const HBINT16 *) p;
 	  p += HBUINT16::static_size;
@@ -376,7 +359,7 @@
       else
 	for (j = 0; j < run_count && i < count; j++, i++)
 	{
-	  if (unlikely (!check.in_range (p)))
+	  if (unlikely (!bytes.in_range (p)))
 	    return false;
 	  deltas[i] = *(const HBINT8 *) p++;
 	}
@@ -611,10 +594,10 @@
 	if (unlikely (!iterator.in_range (p, length)))
 	  return false;
 
-	range_checker_t checker (p, length);
+	hb_bytes_t bytes ((const char *) p, length);
 	hb_vector_t<unsigned int> private_indices;
 	if (iterator.current_tuple->has_private_points () &&
-	    !GlyphVarData::unpack_points (p, private_indices, checker))
+	    !GlyphVarData::unpack_points (p, private_indices, bytes))
 	  return false;
 	const hb_array_t<unsigned int> &indices = private_indices.length ? private_indices : shared_indices;
 
@@ -622,11 +605,11 @@
 	unsigned int num_deltas = apply_to_all ? points.length : indices.length;
 	hb_vector_t<int> x_deltas;
 	x_deltas.resize (num_deltas);
-	if (!GlyphVarData::unpack_deltas (p, x_deltas, checker))
+	if (!GlyphVarData::unpack_deltas (p, x_deltas, bytes))
 	  return false;
 	hb_vector_t<int> y_deltas;
 	y_deltas.resize (num_deltas);
-	if (!GlyphVarData::unpack_deltas (p, y_deltas, checker))
+	if (!GlyphVarData::unpack_deltas (p, y_deltas, bytes))
 	  return false;
 
 	for (unsigned int i = 0; i < deltas.length; i++)
diff --git a/src/hb-ot-vorg-table.hh b/src/hb-ot-vorg-table.hh
index a4d6b06..ff67983 100644
--- a/src/hb-ot-vorg-table.hh
+++ b/src/hb-ot-vorg-table.hh
@@ -84,7 +84,7 @@
     this->defaultVertOriginY = defaultVertOriginY;
     this->vertYOrigins.len = it.len ();
 
-    for (const auto _ : it) c->copy (_);
+    c->copy_all (it);
   }
 
   bool subset (hb_subset_context_t *c) const
diff --git a/src/hb-serialize.hh b/src/hb-serialize.hh
index 4c674b1..ea6b3fc 100644
--- a/src/hb-serialize.hh
+++ b/src/hb-serialize.hh
@@ -387,6 +387,12 @@
   Type *copy (const Type *src, Ts&&... ds)
   { return copy (*src, hb_forward<Ts> (ds)...); }
 
+  template<typename Iterator,
+	   hb_requires (hb_is_iterator (Iterator)),
+	   typename ...Ts>
+  void copy_all (Iterator it, Ts&&... ds)
+  { for (decltype (*it) _ : it) copy (_, hb_forward<Ts> (ds)...); }
+
   template <typename Type>
   hb_serialize_context_t& operator << (const Type &obj) & { embed (obj); return *this; }
 
diff --git a/src/hb-set.hh b/src/hb-set.hh
index 36d11c0..21cb172 100644
--- a/src/hb-set.hh
+++ b/src/hb-set.hh
@@ -135,7 +135,11 @@
       unsigned int i = m / ELT_BITS;
       unsigned int j = m & ELT_MASK;
 
-      const elt_t vv = v[i] & ((elt_t (1) << (j + 1)) - 1);
+      /* Fancy mask to avoid shifting by elt_t bitsize, which is undefined. */
+      const elt_t mask = j < 8 * sizeof (elt_t) - 1 ?
+			 ((elt_t (1) << (j + 1)) - 1) :
+			 (elt_t) -1;
+      const elt_t vv = v[i] & mask;
       const elt_t *p = &vv;
       while (true)
       {
@@ -698,8 +702,15 @@
   struct iter_t : hb_iter_with_fallback_t<iter_t, hb_codepoint_t>
   {
     static constexpr bool is_sorted_iterator = true;
-    iter_t (const hb_set_t &s_ = Null(hb_set_t)) :
-      s (&s_), v (INVALID), l (s->get_population () + 1) { __next__ (); }
+    iter_t (const hb_set_t &s_ = Null(hb_set_t),
+	    bool init = true) : s (&s_), v (INVALID), l(0)
+    {
+      if (init)
+      {
+	l = s->get_population () + 1;
+	__next__ ();
+      }
+    }
 
     typedef hb_codepoint_t __item_t__;
     hb_codepoint_t __item__ () const { return v; }
@@ -707,7 +718,7 @@
     void __next__ () { s->next (&v); if (l) l--; }
     void __prev__ () { s->previous (&v); }
     unsigned __len__ () const { return l; }
-    iter_t end () const { return iter_t (*s); }
+    iter_t end () const { return iter_t (*s, false); }
     bool operator != (const iter_t& o) const
     { return s != o.s || v != o.v; }
 
diff --git a/src/hb-string-array.hh b/src/hb-string-array.hh
index 1c67ab4..e7ac119 100644
--- a/src/hb-string-array.hh
+++ b/src/hb-string-array.hh
@@ -37,6 +37,7 @@
 #define HB_STRING_ARRAY_TYPE_NAME	HB_PASTE(HB_STRING_ARRAY_NAME, _msgstr_t)
 #define HB_STRING_ARRAY_POOL_NAME	HB_PASTE(HB_STRING_ARRAY_NAME, _msgstr)
 #define HB_STRING_ARRAY_OFFS_NAME	HB_PASTE(HB_STRING_ARRAY_NAME, _msgidx)
+#define HB_STRING_ARRAY_LENG_NAME	HB_PASTE(HB_STRING_ARRAY_NAME, _length)
 
 static const union HB_STRING_ARRAY_TYPE_NAME {
   struct {
@@ -66,6 +67,8 @@
   sizeof (HB_STRING_ARRAY_TYPE_NAME)
 };
 
+static const unsigned int HB_STRING_ARRAY_LENG_NAME = ARRAY_LENGTH_CONST (HB_STRING_ARRAY_OFFS_NAME) - 1;
+
 static inline hb_bytes_t
 HB_STRING_ARRAY_NAME (unsigned int i)
 {
@@ -77,5 +80,6 @@
 #undef HB_STRING_ARRAY_TYPE_NAME
 #undef HB_STRING_ARRAY_POOL_NAME
 #undef HB_STRING_ARRAY_OFFS_NAME
+#undef HB_STRING_ARRAY_LENG_NAME
 
 #endif /* HB_STRING_ARRAY_HH */
diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc
index e537772..f4912f8 100644
--- a/src/hb-subset-plan.cc
+++ b/src/hb-subset-plan.cc
@@ -71,7 +71,10 @@
 	       const hb_set_t      *unicodes,
 	       hb_set_t            *glyphset)
 {
-  face->table.cmap->table->closure_glyphs (unicodes, glyphset);
+  OT::cmap::accelerator_t cmap;
+  cmap.init (face);
+  cmap.table->closure_glyphs (unicodes, glyphset);
+  cmap.fini ();
 }
 
 static inline void
diff --git a/src/hb-unicode.hh b/src/hb-unicode.hh
index 0c355f1..34d66d7 100644
--- a/src/hb-unicode.hh
+++ b/src/hb-unicode.hh
@@ -324,10 +324,10 @@
  * Modify Telugu length marks (ccc=84, ccc=91).
  * These are the only matras in the main Indic scripts range that have
  * a non-zero ccc.  That makes them reorder with the Halant (ccc=9).
- * Assign 5 and 6, which are otherwise unassigned.
+ * Assign 4 and 5, which are otherwise unassigned.
  */
-#define HB_MODIFIED_COMBINING_CLASS_CCC84 5 /* length mark */
-#define HB_MODIFIED_COMBINING_CLASS_CCC91 6 /* ai length mark */
+#define HB_MODIFIED_COMBINING_CLASS_CCC84 4 /* length mark */
+#define HB_MODIFIED_COMBINING_CLASS_CCC91 5 /* ai length mark */
 
 /* Thai
  *
diff --git a/src/hb-version.h b/src/hb-version.h
index 4081ca8..a564e9f 100644
--- a/src/hb-version.h
+++ b/src/hb-version.h
@@ -38,9 +38,9 @@
 
 #define HB_VERSION_MAJOR 2
 #define HB_VERSION_MINOR 6
-#define HB_VERSION_MICRO 2
+#define HB_VERSION_MICRO 4
 
-#define HB_VERSION_STRING "2.6.2"
+#define HB_VERSION_STRING "2.6.4"
 
 #define HB_VERSION_ATLEAST(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) <= \
diff --git a/src/hb-warning.cc b/src/hb-warning.cc
deleted file mode 100644
index 60c7445..0000000
--- a/src/hb-warning.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright © 2012  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#include "hb.hh"
-
-#ifdef HB_ATOMIC_INT_NIL
-#error "Could not find any system to define atomic_int macros, library WILL NOT be thread-safe"
-#error "Check hb-atomic.hh for possible resolutions."
-#endif
-
-#ifdef HB_MUTEX_IMPL_NIL
-#error "Could not find any system to define mutex macros, library WILL NOT be thread-safe"
-#error "Check hb-mutex.hh for possible resolutions."
-#endif
diff --git a/src/hb.hh b/src/hb.hh
index f316512..fcbd330 100644
--- a/src/hb.hh
+++ b/src/hb.hh
@@ -370,10 +370,12 @@
 #define getenv(Name) nullptr
 #endif
 
-#ifdef HB_NO_ERRNO
-static int errno = 0; /* Use something better? */
+#ifndef HB_NO_ERRNO
+#  include <errno.h>
 #else
-#include <errno.h>
+static int HB_UNUSED _hb_errno = 0;
+#  undef errno
+#  define errno _hb_errno
 #endif
 
 #if defined(HAVE_ATEXIT) && !defined(HB_USE_ATEXIT)
diff --git a/src/ms-use/COPYING b/src/ms-use/COPYING
new file mode 100644
index 0000000..9e841e7
--- /dev/null
+++ b/src/ms-use/COPYING
@@ -0,0 +1,21 @@
+    MIT License
+
+    Copyright (c) Microsoft Corporation.
+
+    Permission is hereby granted, free of charge, to any person obtaining a copy
+    of this software and associated documentation files (the "Software"), to deal
+    in the Software without restriction, including without limitation the rights
+    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+    copies of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included in all
+    copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+    SOFTWARE
diff --git a/src/HBIndicVowelConstraints.txt b/src/ms-use/IndicShapingInvalidCluster.txt
similarity index 94%
rename from src/HBIndicVowelConstraints.txt
rename to src/ms-use/IndicShapingInvalidCluster.txt
index 146ae1c..8a177fd 100644
--- a/src/HBIndicVowelConstraints.txt
+++ b/src/ms-use/IndicShapingInvalidCluster.txt
@@ -1,5 +1,12 @@
-# Copied from https://docs.microsoft.com/en-us/typography/script-development/use
-# On October 23, 2018; with documentd dated 02/07/2018.
+# IndicShapingInvalidCluster.txt
+# Date: 2015-03-12, 21:17:00 GMT [AG]
+# Date: 2019-11-08, 23:22:00 GMT [AG]
+#
+# This file defines the following property:
+#
+#    Indic_Shaping_Invalid_Cluster
+#
+# Scope: This file enumerates sequences of characters that should be treated as invalid clusters
 
   0905 0946       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN SHORT E
   0905 093E       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN AA
@@ -48,6 +55,7 @@
   0B05 0B3E       ; # ORIYA LETTER A, ORIYA VOWEL SIGN AA
   0B0F 0B57       ; # ORIYA LETTER E, ORIYA AU LENGTH MARK
   0B13 0B57       ; # ORIYA LETTER O, ORIYA AU LENGTH MARK
+  0B85 0BC2       ; # TAMIL LETTER A, TAMIL VOWEL SIGN UU
   0C12 0C55       ; # TELUGU LETTER O, TELUGU LENGTH MARK
   0C12 0C4C       ; # TELUGU LETTER O, TELUGU VOWEL SIGN AU
   0C3F 0C55       ; # TELUGU VOWEL SIGN I, TELUGU LENGTH MARK
diff --git a/test/api/fonts/cmunrm.otf b/test/api/fonts/cmunrm.otf
new file mode 100644
index 0000000..b449df0
--- /dev/null
+++ b/test/api/fonts/cmunrm.otf
Binary files differ
diff --git a/test/api/test-collect-unicodes.c b/test/api/test-collect-unicodes.c
index 50965a9..8a857e1 100644
--- a/test/api/test-collect-unicodes.c
+++ b/test/api/test-collect-unicodes.c
@@ -50,6 +50,27 @@
 }
 
 static void
+test_collect_unicodes_format12_notdef (void)
+{
+  hb_face_t *face = hb_test_open_font_file ("fonts/cmunrm.otf");
+  hb_set_t *codepoints = hb_set_create();
+  hb_codepoint_t cp;
+
+  hb_face_collect_unicodes (face, codepoints);
+
+  cp = HB_SET_VALUE_INVALID;
+  g_assert (hb_set_next (codepoints, &cp));
+  g_assert_cmpuint (0x20, ==, cp);
+  g_assert (hb_set_next (codepoints, &cp));
+  g_assert_cmpuint (0x21, ==, cp);
+  g_assert (hb_set_next (codepoints, &cp));
+  g_assert_cmpuint (0x22, ==, cp);
+
+  hb_set_destroy (codepoints);
+  hb_face_destroy (face);
+}
+
+static void
 test_collect_unicodes_format12 (void)
 {
   hb_face_t *face = hb_test_open_font_file ("fonts/Roboto-Regular.abc.format12.ttf");
@@ -101,6 +122,7 @@
   hb_test_add (test_collect_unicodes);
   hb_test_add (test_collect_unicodes_format4);
   hb_test_add (test_collect_unicodes_format12);
+  hb_test_add (test_collect_unicodes_format12_notdef);
 
   return hb_test_run();
 }
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-hb-subset-fuzzer-5717414645334016 b/test/fuzzing/fonts/clusterfuzz-testcase-hb-subset-fuzzer-5717414645334016
new file mode 100644
index 0000000..9cde3b9
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-hb-subset-fuzzer-5717414645334016
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-harfbuzz_fuzzer-5740518101090304 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-harfbuzz_fuzzer-5740518101090304
new file mode 100644
index 0000000..951bc6c
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-harfbuzz_fuzzer-5740518101090304
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5643107869917184 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5643107869917184
new file mode 100644
index 0000000..b11bd87
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5643107869917184
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5659903036751872 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5659903036751872
new file mode 100644
index 0000000..51ab2fe
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5659903036751872
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5677906231033856 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5677906231033856
new file mode 100644
index 0000000..72147f6
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5677906231033856
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5721073428987904 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5721073428987904
new file mode 100644
index 0000000..683ef99
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5721073428987904
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5763024094232576 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5763024094232576
new file mode 100644
index 0000000..da1b718
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5763024094232576
Binary files differ
diff --git a/test/fuzzing/run-shape-fuzzer-tests.py b/test/fuzzing/run-shape-fuzzer-tests.py
index a2f8b5c..94fc877 100755
--- a/test/fuzzing/run-shape-fuzzer-tests.py
+++ b/test/fuzzing/run-shape-fuzzer-tests.py
@@ -33,7 +33,8 @@
 		def timeout (p, is_killed):
 			is_killed['value'] = True
 			p.kill ()
-		timer = threading.Timer (2, timeout, [p, is_killed])
+		timeout_seconds = int (os.environ.get ("HB_TEST_SHAPE_FUZZER_TIMEOUT", "2"))
+		timer = threading.Timer (timeout_seconds, timeout, [p, is_killed])
 
 		try:
 			timer.start()
diff --git a/test/fuzzing/run-subset-fuzzer-tests.py b/test/fuzzing/run-subset-fuzzer-tests.py
index 297398c..f290e6e 100755
--- a/test/fuzzing/run-subset-fuzzer-tests.py
+++ b/test/fuzzing/run-subset-fuzzer-tests.py
@@ -33,7 +33,8 @@
 		def timeout(p, is_killed):
 			is_killed['value'] = True
 			p.kill()
-		timer = threading.Timer (16, timeout, [p, is_killed])
+		timeout_seconds = int (os.environ.get ("HB_TEST_SUBSET_FUZZER_TIMEOUT", "8"))
+		timer = threading.Timer (timeout_seconds, timeout, [p, is_killed])
 
 		try:
 			timer.start()
diff --git a/test/subset/data/Makefile.am b/test/subset/data/Makefile.am
index 0b27452..4508fcd 100644
--- a/test/subset/data/Makefile.am
+++ b/test/subset/data/Makefile.am
@@ -14,7 +14,9 @@
 	expected/cff-japanese \
 	expected/layout \
 	expected/layout.gpos \
+	expected/layout.gpos2 \
 	expected/layout.gpos3 \
+	expected/layout.gsub6 \
 	expected/cmap14 \
 	fonts \
 	profiles \
diff --git a/test/subset/data/Makefile.sources b/test/subset/data/Makefile.sources
index c6a3042..5b93f27 100644
--- a/test/subset/data/Makefile.sources
+++ b/test/subset/data/Makefile.sources
@@ -6,7 +6,9 @@
 	tests/cff-japanese.tests \
 	tests/layout.tests \
 	tests/layout.gpos.tests \
+	tests/layout.gpos2.tests \
 	tests/layout.gpos3.tests \
+	tests/layout.gsub6.tests \
 	tests/cmap14.tests \
 	$(NULL)
 
diff --git a/test/subset/data/expected/layout.gpos2/gpos2_1_font7.keep-layout-retain-gids.21,23,25.otf b/test/subset/data/expected/layout.gpos2/gpos2_1_font7.keep-layout-retain-gids.21,23,25.otf
new file mode 100644
index 0000000..49039fe
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos2/gpos2_1_font7.keep-layout-retain-gids.21,23,25.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos2/gpos2_1_font7.keep-layout-retain-gids.21,23.otf b/test/subset/data/expected/layout.gpos2/gpos2_1_font7.keep-layout-retain-gids.21,23.otf
new file mode 100644
index 0000000..68cb0ec
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos2/gpos2_1_font7.keep-layout-retain-gids.21,23.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos2/gpos2_1_font7.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos2/gpos2_1_font7.keep-layout-retain-gids.retain-all-codepoint.otf
new file mode 100644
index 0000000..8f18b89
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos2/gpos2_1_font7.keep-layout-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos2/gpos2_1_font7.keep-layout.21,23,25.otf b/test/subset/data/expected/layout.gpos2/gpos2_1_font7.keep-layout.21,23,25.otf
new file mode 100644
index 0000000..47fea1a
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos2/gpos2_1_font7.keep-layout.21,23,25.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos2/gpos2_1_font7.keep-layout.21,23.otf b/test/subset/data/expected/layout.gpos2/gpos2_1_font7.keep-layout.21,23.otf
new file mode 100644
index 0000000..99e813f
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos2/gpos2_1_font7.keep-layout.21,23.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos2/gpos2_1_font7.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos2/gpos2_1_font7.keep-layout.retain-all-codepoint.otf
new file mode 100644
index 0000000..8f18b89
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos2/gpos2_1_font7.keep-layout.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos2/gpos2_2_font5.keep-layout-retain-gids.21,23,25.otf b/test/subset/data/expected/layout.gpos2/gpos2_2_font5.keep-layout-retain-gids.21,23,25.otf
new file mode 100644
index 0000000..b34a49f
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos2/gpos2_2_font5.keep-layout-retain-gids.21,23,25.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos2/gpos2_2_font5.keep-layout-retain-gids.21,23.otf b/test/subset/data/expected/layout.gpos2/gpos2_2_font5.keep-layout-retain-gids.21,23.otf
new file mode 100644
index 0000000..2ad1d29
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos2/gpos2_2_font5.keep-layout-retain-gids.21,23.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos2/gpos2_2_font5.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos2/gpos2_2_font5.keep-layout-retain-gids.retain-all-codepoint.otf
new file mode 100644
index 0000000..88e6046
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos2/gpos2_2_font5.keep-layout-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos2/gpos2_2_font5.keep-layout.21,23,25.otf b/test/subset/data/expected/layout.gpos2/gpos2_2_font5.keep-layout.21,23,25.otf
new file mode 100644
index 0000000..195c8dc
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos2/gpos2_2_font5.keep-layout.21,23,25.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos2/gpos2_2_font5.keep-layout.21,23.otf b/test/subset/data/expected/layout.gpos2/gpos2_2_font5.keep-layout.21,23.otf
new file mode 100644
index 0000000..d10d362
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos2/gpos2_2_font5.keep-layout.21,23.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos2/gpos2_2_font5.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos2/gpos2_2_font5.keep-layout.retain-all-codepoint.otf
new file mode 100644
index 0000000..88e6046
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos2/gpos2_2_font5.keep-layout.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout-retain-gids.30,31,32,33.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout-retain-gids.30,31,32,33.otf
new file mode 100644
index 0000000..e10d863
--- /dev/null
+++ b/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout-retain-gids.30,31,32,33.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
new file mode 100644
index 0000000..1f90754
--- /dev/null
+++ b/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout.30,31,32,33.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout.30,31,32,33.otf
new file mode 100644
index 0000000..bdaa805
--- /dev/null
+++ b/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout.30,31,32,33.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
new file mode 100644
index 0000000..1f90754
--- /dev/null
+++ b/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout-retain-gids.30,31,32,33.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout-retain-gids.30,31,32,33.otf
new file mode 100644
index 0000000..856249e
--- /dev/null
+++ b/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout-retain-gids.30,31,32,33.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
new file mode 100644
index 0000000..e764393
--- /dev/null
+++ b/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout.30,31,32,33.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout.30,31,32,33.otf
new file mode 100644
index 0000000..a53b114
--- /dev/null
+++ b/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout.30,31,32,33.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
new file mode 100644
index 0000000..e764393
--- /dev/null
+++ b/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout-retain-gids.30,31,32,33.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout-retain-gids.30,31,32,33.otf
new file mode 100644
index 0000000..2d08eb0
--- /dev/null
+++ b/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout-retain-gids.30,31,32,33.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout-retain-gids.retain-all-codepoint.otf
new file mode 100644
index 0000000..737f85a
--- /dev/null
+++ b/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout.30,31,32,33.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout.30,31,32,33.otf
new file mode 100644
index 0000000..fbd9a44
--- /dev/null
+++ b/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout.30,31,32,33.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout.retain-all-codepoint.otf
new file mode 100644
index 0000000..737f85a
--- /dev/null
+++ b/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/fonts/gpos2_1_font7.otf b/test/subset/data/fonts/gpos2_1_font7.otf
new file mode 100644
index 0000000..22b54ea
--- /dev/null
+++ b/test/subset/data/fonts/gpos2_1_font7.otf
Binary files differ
diff --git a/test/subset/data/fonts/gpos2_2_font5.otf b/test/subset/data/fonts/gpos2_2_font5.otf
new file mode 100644
index 0000000..63af3bc
--- /dev/null
+++ b/test/subset/data/fonts/gpos2_2_font5.otf
Binary files differ
diff --git a/test/subset/data/fonts/gsub_chaining1_multiple_subrules_f1.otf b/test/subset/data/fonts/gsub_chaining1_multiple_subrules_f1.otf
new file mode 100644
index 0000000..74b9945
--- /dev/null
+++ b/test/subset/data/fonts/gsub_chaining1_multiple_subrules_f1.otf
Binary files differ
diff --git a/test/subset/data/fonts/gsub_chaining2_multiple_subrules_f1.otf b/test/subset/data/fonts/gsub_chaining2_multiple_subrules_f1.otf
new file mode 100644
index 0000000..a3a1846
--- /dev/null
+++ b/test/subset/data/fonts/gsub_chaining2_multiple_subrules_f1.otf
Binary files differ
diff --git a/test/subset/data/fonts/gsub_chaining3_simple_f2.otf b/test/subset/data/fonts/gsub_chaining3_simple_f2.otf
new file mode 100644
index 0000000..bc9ed9a
--- /dev/null
+++ b/test/subset/data/fonts/gsub_chaining3_simple_f2.otf
Binary files differ
diff --git a/test/subset/data/tests/layout.gpos2.tests b/test/subset/data/tests/layout.gpos2.tests
new file mode 100644
index 0000000..94fe78a
--- /dev/null
+++ b/test/subset/data/tests/layout.gpos2.tests
@@ -0,0 +1,12 @@
+FONTS:
+gpos2_1_font7.otf
+gpos2_2_font5.otf
+
+PROFILES:
+keep-layout.txt
+keep-layout-retain-gids.txt
+
+SUBSETS:
+!#
+!#%
+*
diff --git a/test/subset/data/tests/layout.gsub6.tests b/test/subset/data/tests/layout.gsub6.tests
new file mode 100644
index 0000000..47399b8
--- /dev/null
+++ b/test/subset/data/tests/layout.gsub6.tests
@@ -0,0 +1,12 @@
+FONTS:
+gsub_chaining1_multiple_subrules_f1.otf
+gsub_chaining2_multiple_subrules_f1.otf
+gsub_chaining3_simple_f2.otf
+
+PROFILES:
+keep-layout.txt
+keep-layout-retain-gids.txt
+
+SUBSETS:
+0123
+*