Merge "Update harfbuzz to 1.8.8"
diff --git a/NEWS b/NEWS
index 9031766..3ae857e 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,57 @@
+Overview of changes leading to 1.8.8
+Tuesday, August 14, 2018
+====================================
+- Fix hb-icu crash on architectures where compare_exchange_weak() can
+  fail falsely.  This bug was introduced in 1.8.4.
+  https://bugs.chromium.org/p/chromium/issues/detail?id=873568
+- More internal refactoring of atomic operations and singletons.
+- API changes:
+  The following functions do NOT reference their return value before
+  returning:
+  * hb_unicode_funcs_get_default()
+  * hb_glib_get_unicode_funcs()
+  * hb_icu_get_unicode_funcs()
+  This is consistent with their naming ("get", instead of "reference")
+  as well as how they are used in the wild (ie. no one calls destroy()
+  on their return value.)
+
+
+Overview of changes leading to 1.8.7
+Wednesday, August 8, 2018
+====================================
+- Fix assertion failure with GDEF-blacklisted fonts.
+
+
+Overview of changes leading to 1.8.6
+Tuesday, August 7, 2018
+====================================
+- Internal code shuffling.
+- New API to speed up getting advance widths for implementations
+  that have heavy overhead in get_h_advance callback:
++hb_font_funcs_set_glyph_h_advances_func
++hb_font_funcs_set_glyph_v_advances_func
++hb_font_get_glyph_advances_for_direction
++hb_font_get_glyph_h_advances
++hb_font_get_glyph_h_advances_func_t
++hb_font_get_glyph_v_advances
++hb_font_get_glyph_v_advances_func_t
+
+
+Overview of changes leading to 1.8.5
+Wednesday, August 1, 2018
+====================================
+- Major Khmer shaper improvements to better match Microsoft.
+- Indic bug fixes.
+- Internal improvements to atomic operations.
+
+
+Overview of changes leading to 1.8.4
+Tuesday, July 17, 2018
+====================================
+- Fix build on non-C++11.
+- Use C++-style GCC atomics and C++11 atomics.
+
+
 Overview of changes leading to 1.8.3
 Wednesday, July 11, 2018
 ====================================
diff --git a/OWNERS b/OWNERS
index dd9112b..9bc89fb 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,4 +1,3 @@
-roozbeh@google.com
 behdad@google.com
 siyamed@google.com
 nona@google.com
diff --git a/README.version b/README.version
index 6d052f6..457bf26 100644
--- a/README.version
+++ b/README.version
@@ -1,3 +1,3 @@
-URL: https://github.com/harfbuzz/harfbuzz/commit/2b76767bf572364d3d647cdd139f2044a7ad06b2 
-Version: 1.8.3
+URL: https://github.com/harfbuzz/harfbuzz/commit/63be5dcdde61275822d931b2924425478bc1dac1
+Version: 1.8.8
 BugComponent: 25699
diff --git a/RELEASING.md b/RELEASING.md
index 0aef610..d431871 100644
--- a/RELEASING.md
+++ b/RELEASING.md
@@ -25,11 +25,14 @@
 
 6. Do "make distcheck", if it passes, you get a tarball.
    Otherwise, fix things and commit them separately before making release,
+   Note: Check src/hb-version.h and make sure the new version number is
+   there.  Sometimes, it does not get updated.  If that's the case,
+   "touch configure.ac" and rebuild.  TODO: debug.
 
 7. "make release-files".  Enter your GPG password.  This creates a sha256 hash
    and signs it.
 
-8. Now that you have release files built, commit NEWS and configure.ac changes,
+8. Now that you have release files, commit NEWS, configure.ac, and src/hb-version.h,
    as well as any REPLACEME changes you made.  The commit message is simply the
    release number.  Eg. "1.4.7"
 
diff --git a/appveyor.yml b/appveyor.yml
index 05a72d9..cc4acec 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -29,6 +29,7 @@
       MSYS2_ARCH: i686
 
 install:
+  - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman -Syu --noconfirm"'
   - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-x86_64-ragel"
 
 build_script:
@@ -43,7 +44,8 @@
   - 'if "%compiler%"=="msvc" msbuild harfbuzz.sln /p:Configuration=%configuration% /p:Platform=%platform%'
   - 'if "%compiler%"=="msvc" if not "%platform%"=="ARM" ctest --output-on-failure -C %configuration%'
 
-  - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-$MSYS2_ARCH-{freetype,cairo,icu,gettext,gobject-introspection,gcc,gcc-libs,glib2,graphite2,pkg-config,python2}"'
+  - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syyu mingw-w64-$MSYS2_ARCH-gcc"'
+  - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S --needed mingw-w64-$MSYS2_ARCH-{freetype,cairo,icu,gettext,gobject-introspection,gcc,gcc-libs,glib2,graphite2,pkg-config,python2}"'
   - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "curl https://raw.githubusercontent.com/mirror/mingw-w64/023eb04c396d4e8d8fcf604cfababc53dae13398/mingw-w64-headers/include/dwrite_1.h > %MINGW_PREFIX%/%MINGW_CHOST%/include/dwrite_1.h"'
   - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "cd $APPVEYOR_BUILD_FOLDER; PATH=$PATH:/mingw64/bin:/mingw32/bin; ./autogen.sh --with-uniscribe --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2 --with-directwrite --build=%MINGW_CHOST% --host=%MINGW_CHOST% --prefix=%MINGW_PREFIX%; make; make check || .ci/fail.sh"'
 
diff --git a/configure.ac b/configure.ac
index b6fcfc0..55cc12b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [1.8.3],
+        [1.8.8],
         [https://github.com/harfbuzz/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt
index 5715d77..16b6627 100644
--- a/docs/harfbuzz-sections.txt
+++ b/docs/harfbuzz-sections.txt
@@ -208,10 +208,12 @@
 hb_font_funcs_set_glyph_extents_func
 hb_font_funcs_set_glyph_from_name_func
 hb_font_funcs_set_glyph_h_advance_func
+hb_font_funcs_set_glyph_h_advances_func
 hb_font_funcs_set_glyph_h_kerning_func
 hb_font_funcs_set_glyph_h_origin_func
 hb_font_funcs_set_glyph_name_func
 hb_font_funcs_set_glyph_v_advance_func
+hb_font_funcs_set_glyph_v_advances_func
 hb_font_funcs_set_glyph_v_kerning_func
 hb_font_funcs_set_glyph_v_origin_func
 hb_font_funcs_set_nominal_glyph_func
@@ -223,6 +225,7 @@
 hb_font_get_glyph
 hb_font_get_glyph_advance_for_direction
 hb_font_get_glyph_advance_func_t
+hb_font_get_glyph_advances_for_direction
 hb_font_get_glyph_contour_point
 hb_font_get_glyph_contour_point_for_origin
 hb_font_get_glyph_contour_point_func_t
@@ -233,6 +236,8 @@
 hb_font_get_glyph_from_name_func_t
 hb_font_get_glyph_h_advance
 hb_font_get_glyph_h_advance_func_t
+hb_font_get_glyph_h_advances
+hb_font_get_glyph_h_advances_func_t
 hb_font_get_glyph_h_kerning
 hb_font_get_glyph_h_kerning_func_t
 hb_font_get_glyph_h_origin
@@ -245,6 +250,8 @@
 hb_font_get_glyph_origin_func_t
 hb_font_get_glyph_v_advance
 hb_font_get_glyph_v_advance_func_t
+hb_font_get_glyph_v_advances
+hb_font_get_glyph_v_advances_func_t
 hb_font_get_glyph_v_kerning
 hb_font_get_glyph_v_kerning_func_t
 hb_font_get_glyph_v_origin
@@ -327,6 +334,7 @@
 HB_GOBJECT_TYPE_FONT
 HB_GOBJECT_TYPE_FONT_FUNCS
 HB_GOBJECT_TYPE_GLYPH_FLAGS
+HB_GOBJECT_TYPE_MAP
 HB_GOBJECT_TYPE_MEMORY_MODE
 HB_GOBJECT_TYPE_OT_LAYOUT_GLYPH_CLASS
 HB_GOBJECT_TYPE_OT_MATH_CONSTANT
@@ -358,6 +366,7 @@
 hb_gobject_font_funcs_get_type
 hb_gobject_font_get_type
 hb_gobject_glyph_flags_get_type
+hb_gobject_map_get_type
 hb_gobject_memory_mode_get_type
 hb_gobject_ot_layout_glyph_class_get_type
 hb_gobject_ot_math_constant_get_type
@@ -447,6 +456,7 @@
 HB_OT_TAG_GSUB
 HB_OT_TAG_JSTF
 hb_ot_layout_collect_lookups
+hb_ot_layout_collect_features
 hb_ot_layout_feature_get_lookups
 hb_ot_layout_feature_with_variations_get_lookups
 hb_ot_layout_get_attach_points
diff --git a/src/Makefile.am b/src/Makefile.am
index 6dfec3b..9e7fd29 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -14,6 +14,7 @@
 
 # Convenience targets:
 lib: $(BUILT_SOURCES) libharfbuzz.la libharfbuzz-subset.la
+libs: $(BUILT_SOURCES) $(lib_LTLIBRARIES)
 fuzzing: $(BUILT_SOURCES) libharfbuzz-fuzzing.la libharfbuzz-subset-fuzzing.la
 
 lib_LTLIBRARIES = libharfbuzz.la
@@ -373,15 +374,11 @@
 endif
 
 check_PROGRAMS += \
-	dump-fon \
 	dump-indic-data \
 	dump-khmer-data \
 	dump-myanmar-data \
 	dump-use-data \
 	$(NULL)
-dump_fon_SOURCES = dump-fon.cc
-dump_fon_CPPFLAGS = $(HBCFLAGS)
-dump_fon_LDADD = libharfbuzz.la $(HBLIBS)
 dump_indic_data_SOURCES = dump-indic-data.cc hb-ot-shape-complex-indic-table.cc
 dump_indic_data_CPPFLAGS = $(HBCFLAGS)
 dump_indic_data_LDADD = libharfbuzz.la $(HBLIBS)
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 0bc9e58..b981b0e 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -14,9 +14,12 @@
 	hb-face.cc \
 	hb-font-private.hh \
 	hb-font.cc \
+	hb-iter-private.hh \
 	hb-map-private.hh \
 	hb-map.cc \
+	hb-machinery-private.hh \
 	hb-mutex-private.hh \
+	hb-null.hh \
 	hb-object-private.hh \
 	hb-open-file-private.hh \
 	hb-open-type-private.hh \
@@ -50,6 +53,7 @@
 	hb-string-array.hh \
 	hb-unicode-private.hh \
 	hb-unicode.cc \
+	hb-vector-private.hh \
 	hb-utf-private.hh \
 	hb-warning.cc \
 	$(NULL)
@@ -93,8 +97,6 @@
 	hb-aat-layout-morx-table.hh \
 	hb-aat-layout-trak-table.hh \
 	hb-aat-layout-private.hh \
-	hb-aat-fmtx-table.hh \
-	hb-aat-gcid-table.hh \
 	hb-aat-ltag-table.hh \
 	hb-ot-font.cc \
 	hb-ot-layout.cc \
diff --git a/src/dump-emoji.cc b/src/dump-emoji.cc
index 99e8ef9..6521469 100644
--- a/src/dump-emoji.cc
+++ b/src/dump-emoji.cc
@@ -233,12 +233,10 @@
   svg.dump (svg_callback);
   svg.fini ();
 
-  OT::Sanitizer<OT::COLR> sanitizerCOLR;
-  hb_blob_t* colr_blob = sanitizerCOLR.sanitize (face->reference_table (HB_OT_TAG_COLR));
+  hb_blob_t* colr_blob = hb_sanitize_context_t ().reference_table<OT::COLR> (face);
   const OT::COLR *colr = colr_blob->as<OT::COLR> ();
 
-  OT::Sanitizer<OT::CPAL> sanitizerCPAL;
-  hb_blob_t* cpal_blob = sanitizerCPAL.sanitize (face->reference_table (HB_OT_TAG_CPAL));
+  hb_blob_t* cpal_blob = hb_sanitize_context_t ().reference_table<OT::CPAL> (face);
   const OT::CPAL *cpal = cpal_blob->as<OT::CPAL> ();
 
   cairo_font_face_t *cairo_face;
diff --git a/src/dump-fon.cc b/src/dump-fon.cc
deleted file mode 100644
index 4015409..0000000
--- a/src/dump-fon.cc
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Copyright © 2018  Ebrahim Byagowi
- *
- *  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.
- */
-
-#include "hb-static.cc"
-#include <stdio.h>
-#include <stdlib.h>
-#include "hb-open-type-private.hh"
-
-template <typename Type, int Bytes> struct LEInt;
-
-template <typename Type>
-struct LEInt<Type, 1>
-{
-  public:
-  inline void set (Type V)
-  {
-    v = V;
-  }
-  inline operator Type (void) const
-  {
-    return v;
-  }
-  private: uint8_t v;
-};
-template <typename Type>
-struct LEInt<Type, 2>
-{
-  public:
-  inline void set (Type V)
-  {
-    v[1] = (V >>  8) & 0xFF;
-    v[0] = (V      ) & 0xFF;
-  }
-  inline operator Type (void) const
-  {
-    return (v[1] <<  8)
-         + (v[0]      );
-  }
-  private: uint8_t v[2];
-};
-template <typename Type>
-struct LEInt<Type, 3>
-{
-  public:
-  inline void set (Type V)
-  {
-    v[2] = (V >> 16) & 0xFF;
-    v[1] = (V >>  8) & 0xFF;
-    v[0] = (V      ) & 0xFF;
-  }
-  inline operator Type (void) const
-  {
-    return (v[2] << 16)
-         + (v[1] <<  8)
-         + (v[0]      );
-  }
-  private: uint8_t v[3];
-};
-template <typename Type>
-struct LEInt<Type, 4>
-{
-  public:
-  inline void set (Type V)
-  {
-    v[3] = (V >> 24) & 0xFF;
-    v[2] = (V >> 16) & 0xFF;
-    v[1] = (V >>  8) & 0xFF;
-    v[0] = (V      ) & 0xFF;
-  }
-  inline operator Type (void) const
-  {
-    return (v[3] << 24)
-         + (v[2] << 16)
-         + (v[1] <<  8)
-         + (v[0]      );
-  }
-  private: uint8_t v[4];
-};
-
-template <typename Type, unsigned int Size>
-struct LEIntType
-{
-  inline void set (Type i) { v.set (i); }
-  inline operator Type(void) const { return v; }
-  inline bool sanitize (OT::hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
-  }
-  protected:
-  LEInt<Type, Size> v;
-  public:
-  DEFINE_SIZE_STATIC (Size);
-};
-
-typedef LEIntType<uint8_t,  1> LEUINT8;		/* 8-bit unsigned integer. */
-typedef LEIntType<int8_t,   1> LEINT8;		/* 8-bit signed integer. */
-typedef LEIntType<uint16_t, 2> LEUINT16;	/* 16-bit unsigned integer. */
-typedef LEIntType<int16_t,  2> LEINT16;		/* 16-bit signed integer. */
-typedef LEIntType<uint32_t, 4> LEUINT32;	/* 32-bit unsigned integer. */
-typedef LEIntType<int32_t,  4> LEINT32;		/* 32-bit signed integer. */
-typedef LEIntType<uint32_t, 3> LEUINT24;	/* 24-bit unsigned integer. */
-
-
-struct LE_FONTINFO16
-{
-  inline bool sanitize (OT::hb_sanitize_context_t *c, unsigned int length) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) && c->check_range (this, length)));
-  }
-
-  // https://msdn.microsoft.com/en-us/library/cc194829.aspx
-  enum charset_t
-  {
-    // dfCharSet possible values and the codepage they are indicating to
-    ANSI	= 0x00,	// 1252
-    DEFAULT	= 0x01,	//
-    SYMBOL	= 0x02,	//
-    SHIFTJIS	= 0x80,	// 932
-    HANGUL	= 0x81,	// 949
-    GB2312	= 0x86,	// 936
-    CHINESEBIG5	= 0x88,	// 950
-    GREEK	= 0xA1,	// 1253
-    TURKISH	= 0xA2,	// 1254
-    HEBREW	= 0xB1,	// 1255
-    ARABIC	= 0xB2,	// 1256
-    BALTIC	= 0xBA,	// 1257
-    RUSSIAN	= 0xCC,	// 1251
-    THAI	= 0xDE,	// 874
-    EE		= 0xEE,	// 1250
-    OEM		= 0xFF	//
-  };
-
-  inline const char* get_charset() const
-  {
-    switch (dfCharSet) {
-    case ANSI: return "ISO8859";
-    case DEFAULT: return "WinDefault";
-    case SYMBOL: return "Symbol";
-    case SHIFTJIS: return "JISX0208.1983";
-    case HANGUL: return "MSHangul";
-    case GB2312: return "GB2312.1980";
-    case CHINESEBIG5: return "Big5";
-    case GREEK: return "CP1253";
-    case TURKISH: return "CP1254";
-    case HEBREW: return "CP1255";
-    case ARABIC: return "CP1256";
-    case BALTIC: return "CP1257";
-    case RUSSIAN: return "CP1251";
-    case THAI: return "CP874";
-    case EE: return "CP1250";
-    case OEM: return "OEM";
-    default: return "Unknown";
-    }
-  }
-
-  inline unsigned int get_version () const
-  {
-    return dfVersion;
-  }
-
-  inline unsigned int get_weight () const
-  {
-    return dfWeight;
-  }
-
-  enum weight_t {
-    DONTCARE	= 0,
-    THIN	= 100,
-    EXTRALIGHT	= 200,
-    ULTRALIGHT	= 200,
-    LIGHT	= 300,
-    NORMAL	= 400,
-    REGULAR	= 400,
-    MEDIUM	= 500,
-    SEMIBOLD	= 600,
-    DEMIBOLD	= 600,
-    BOLD	= 700,
-    EXTRABOLD	= 800,
-    ULTRABOLD	= 800,
-    HEAVY	= 900,
-    BLACK	= 900
-  };
-
-  inline void dump () const
-  {
-    // With https://github.com/juanitogan/mkwinfont/blob/master/python/dewinfont.py help
-    // Should be implemented differently eventually, but for now
-    unsigned int ctstart;
-    unsigned int ctsize;
-    if (dfVersion == 0x200)
-    {
-      ctstart = 0x76;
-      ctsize = 4;
-    }
-    else
-    {
-      return; // must of ".fon"s are version 2 and even dewinfont V1 implmentation doesn't seem correct
-      ctstart = 0x94;
-      ctsize = 6;
-    }
-    // unsigned int maxwidth = 0;
-    for (unsigned int i = dfFirstChar; i < dfLastChar; ++i)
-    {
-      unsigned int entry = ctstart + ctsize * (i-dfFirstChar);
-      unsigned int w = (uint16_t) OT::StructAtOffset<LEUINT16> (this, entry);
-
-      unsigned int off;
-      if (ctsize == 4)
-        off = (uint16_t) OT::StructAtOffset<LEUINT16> (this, entry+2);
-      else
-        off = (uint32_t) OT::StructAtOffset<LEUINT32> (this, entry+2);
-
-      unsigned int widthbytes = (w + 7) / 8;
-      for (unsigned int j = 0; j < dfPixHeight; ++j)
-      {
-        for (unsigned int k = 0; k < widthbytes; ++k)
-	{
-	  unsigned int bytepos = off + k * dfPixHeight + j;
-	  const uint8_t b = (uint8_t) OT::StructAtOffset<LEINT8> (this, bytepos);
-	  for (unsigned int a = 128; a > 0; a >>= 1)
-	    printf (b & a ? "x" : ".");
-	}
-	printf ("\n");
-      }
-      printf ("\n\n");
-    }
-  }
-
-  protected:
-  LEUINT16	dfVersion;
-  LEUINT32	dfSize;
-  LEUINT8	dfCopyright[60];
-  LEUINT16	dfType;
-  LEUINT16	dfPoints;
-  LEUINT16	dfVertRes;
-  LEUINT16	dfHorizRes;
-  LEUINT16	dfAscent;
-  LEUINT16	dfInternalLeading;
-  LEUINT16	dfExternalLeading;
-  LEUINT8	dfItalic;
-  LEUINT8	dfUnderline;
-  LEUINT8	dfStrikeOut;
-  LEUINT16	dfWeight; // see weight_t
-  LEUINT8	dfCharSet;  // see charset_t
-  LEUINT16	dfPixWidth;
-  LEUINT16	dfPixHeight;
-  LEUINT8	dfPitchAndFamily;
-  LEUINT16	dfAvgWidth;
-  LEUINT16	dfMaxWidth;
-  LEUINT8	dfFirstChar;
-  LEUINT8	dfLastChar;
-  LEUINT8	dfDefaultChar;
-  LEUINT8	dfBreakChar;
-  LEUINT16	dfWidthBytes;
-  LEUINT32	dfDevice;
-  LEUINT32	dfFace;
-  LEUINT32	dfBitsPointer;
-  LEUINT32	dfBitsOffset;
-  LEUINT8	dfReserved;
-//   LEUINT32	dfFlags;
-//   LEUINT16	dfAspace;
-//   LEUINT16	dfBspace;
-//   LEUINT16	dfCspace;
-//   LEUINT32	dfColorPointer;
-//   LEUINT32	dfReserved1[4];
-  OT::UnsizedArrayOf<LEUINT8>
-		dataZ;
-  public:
-  DEFINE_SIZE_ARRAY (118, dataZ);
-};
-
-struct NE_NAMEINFO
-{
-  friend struct NE_TYPEINFO;
-
-  inline bool sanitize (OT::hb_sanitize_context_t *c, const void *base, unsigned int shift) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) &&
-			  get_font (base, shift).sanitize (c, length << shift)));
-  }
-
-  inline const LE_FONTINFO16& get_font (const void *base, int shift) const
-  {
-    return OT::StructAtOffset<LE_FONTINFO16> (base, offset << shift);
-  }
-
-  enum resource_type_flag_t {
-    NONE     = 0x0000,
-    MOVEABLE = 0x0010,
-    PURE     = 0x0020,
-    PRELOAD  = 0x0040
-  };
-
-  protected:
-  LEUINT16	offset;	// Should be shifted with alignmentShiftCount before use
-  LEUINT16	length;	// Should be shifted with alignmentShiftCount before use
-  LEUINT16	flags;	// resource_type_flag_t
-  LEUINT16	id;
-  LEUINT16	handle;
-  LEUINT16	usage;
-  public:
-  DEFINE_SIZE_STATIC (12);
-};
-
-struct NE_TYPEINFO
-{
-  inline bool sanitize (OT::hb_sanitize_context_t *c, const void *base, unsigned int shift) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) && resources.sanitize (c, count, base, shift));
-  }
-
-  inline unsigned int get_size (void) const
-  { return 8 + count * NE_NAMEINFO::static_size; }
-
-  inline const NE_TYPEINFO& next () const
-  {
-    const NE_TYPEINFO& next = OT::StructAfter<NE_TYPEINFO> (*this);
-    if (type_id == 0)
-      return Null(NE_TYPEINFO);
-    return next;
-  }
-
-  inline const LE_FONTINFO16& get_font (unsigned int idx, const void *base, int shift) const
-  {
-    if (idx < count)
-      return resources[idx].get_font (base, shift);
-    return Null(LE_FONTINFO16);
-  }
-
-  inline unsigned int get_count () const
-  {
-    return count;
-  }
-
-  inline unsigned int get_type_id () const
-  {
-    return type_id;
-  }
-
-  enum type_id_t {
-    CURSOR		= 0x8001,
-    BITMAP		= 0x8002,
-    ICON		= 0x8003,
-    MENU		= 0x8004,
-    DIALOG		= 0x8005,
-    STRING		= 0x8006,
-    FONT_DIRECTORY	= 0x8007,
-    FONT		= 0x8008,
-    ACCELERATOR_TABLE	= 0x8009,
-    RESOURCE_DATA	= 0x800a,
-    GROUP_CURSOR	= 0x800c,
-    GROUP_ICON		= 0x800e,
-    VERSION		= 0x8010
-  };
-
-  protected:
-  LEUINT16	type_id; // see type_id_t
-  LEUINT16	count;
-  LEUINT32	resloader;
-  OT::UnsizedArrayOf<NE_NAMEINFO>
-		resources;
-  public:
-  DEFINE_SIZE_ARRAY (8, resources);
-};
-
-struct NE_RESOURCE_TABLE
-{
-  inline bool sanitize (OT::hb_sanitize_context_t *c, const void *base) const
-  {
-    TRACE_SANITIZE (this);
-
-    if (!c->check_struct (this))
-      return_trace (false);
-
-    const NE_TYPEINFO* n = &chain;
-    while (n != &Null(NE_TYPEINFO) && c->check_struct (n) && n->get_type_id () != 0)
-    {
-      if (n->get_type_id () == NE_TYPEINFO::FONT)
-	return_trace (n->sanitize (c, base, alignmentShiftCount));
-      n = &n->next();
-    }
-    return_trace (false);
-  }
-
-  inline unsigned int get_shift_value () const
-  {
-    return alignmentShiftCount;
-  }
-
-  inline const NE_TYPEINFO& get_fonts_entry () const
-  {
-    const NE_TYPEINFO* n = &chain;
-    while (n != &Null(NE_TYPEINFO) && n->get_type_id () != 0)
-    {
-      if (n->get_type_id () == NE_TYPEINFO::FONT)
-	return *n;
-      n = &n->next();
-    }
-    return Null(NE_TYPEINFO);
-  }
-
-  protected:
-  LEUINT16	alignmentShiftCount;
-  NE_TYPEINFO	chain;
-  // It is followed by an array of OT::ArrayOf<LEUINT8, LEUINT8> chars;
-  public:
-  DEFINE_SIZE_MIN (2);
-};
-
-// https://github.com/wine-mirror/wine/blob/master/include/winnt.h#L2467
-struct LE_IMAGE_OS2_HEADER
-{
-  inline bool sanitize (OT::hb_sanitize_context_t *c, const void *base) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) && (this+rsrctab).sanitize (c, base)));
-  }
-
-  inline const NE_RESOURCE_TABLE& get_resource_table () const
-  {
-    if (magic != 0x454E) // Only NE containers are support for now, NE == 0x454E
-      return Null(NE_RESOURCE_TABLE);
-    return this+rsrctab;
-  }
-
-  protected:
-  LEUINT16	magic;		/* 00 NE signature 'NE' */
-  LEUINT8	ver;		/* 02 Linker version number */
-  LEUINT8	rev;		/* 03 Linker revision number */
-  LEUINT16	enttab;		/* 04 Offset to entry table relative to NE */
-  LEUINT16	cbenttab;	/* 06 Length of entry table in bytes */
-  LEUINT32	crc;		/* 08 Checksum */
-  LEUINT16	flags;		/* 0c Flags about segments in this file */
-  LEUINT16	autodata;	/* 0e Automatic data segment number */
-  LEUINT16	heap;		/* 10 Initial size of local heap */
-  LEUINT16	stack;		/* 12 Initial size of stack */
-  LEUINT32	csip;		/* 14 Initial CS:IP */
-  LEUINT32	sssp;		/* 18 Initial SS:SP */
-  LEUINT16	cseg;		/* 1c # of entries in segment table */
-  LEUINT16	cmod;		/* 1e # of entries in module reference tab. */
-  LEUINT16	cbnrestab;	/* 20 Length of nonresident-name table     */
-  LEUINT16	segtab;		/* 22 Offset to segment table */
-  OT::OffsetTo<NE_RESOURCE_TABLE, LEUINT16>
-		rsrctab;	/* 24 Offset to resource table */
-  LEUINT16	restab;		/* 26 Offset to resident-name table */
-  LEUINT16	modtab;		/* 28 Offset to module reference table */
-  LEUINT16	imptab;		/* 2a Offset to imported name table */
-  LEUINT32	nrestab;	/* 2c Offset to nonresident-name table */
-  LEUINT16	cmovent;	/* 30 # of movable entry points */
-  LEUINT16	align;		/* 32 Logical sector alignment shift count */
-  LEUINT16	cres;		/* 34 # of resource segments */
-  LEUINT8	exetyp;		/* 36 Flags indicating target OS */
-  LEUINT8	flagsothers;	/* 37 Additional information flags */
-  LEUINT16	pretthunks;	/* 38 Offset to return thunks */
-  LEUINT16	psegrefbytes;	/* 3a Offset to segment ref. bytes */
-  LEUINT16	swaparea;	/* 3c Reserved by Microsoft */
-  LEUINT16	expver;		/* 3e Expected Windows version number */
-  public:
-  DEFINE_SIZE_STATIC (64);
-};
-
-struct LE_IMAGE_DOS_HEADER {
-  inline bool sanitize (OT::hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) &&
-			  get_os2_header ().sanitize (c, this)));
-  }
-
-  inline const LE_IMAGE_OS2_HEADER& get_os2_header () const
-  {
-    return this+e_lfanew;
-  }
-
-  protected:
-  LEUINT16	e_magic;	// Magic number
-  LEUINT16	e_cblp;		// Bytes on last page of file
-  LEUINT16	e_cp;		// Pages in file
-  LEUINT16	e_crlc;		// Relocations
-  LEUINT16	e_cparhdr;	// Size of header in paragraphs
-  LEUINT16	e_minalloc;	// Minimum extra paragraphs needed
-  LEUINT16	e_maxalloc;	// Maximum extra paragraphs needed
-  LEUINT16	e_ss;		// Initial (relative) SS value
-  LEUINT16	e_sp;		// Initial SP value
-  LEUINT16	e_csum;		// Checksum
-  LEUINT16	e_ip;		// Initial IP value
-  LEUINT16	e_cs;		// Initial (relative) CS value
-  LEUINT16	e_lfarlc;	// File address of relocation table
-  LEUINT16	e_ovno;		// Overlay number
-  LEUINT16	e_res_0;	// Reserved words
-  LEUINT16	e_res_1;	// Reserved words
-  LEUINT16	e_res_2;	// Reserved words
-  LEUINT16	e_res_3;	// Reserved words
-  LEUINT16	e_oemid;	// OEM identifier (for e_oeminfo)
-  LEUINT16	e_oeminfo;	// OEM information; e_oemid specific
-  LEUINT16	e_res2_0;	// Reserved words
-  LEUINT16	e_res2_1;	// Reserved words
-  LEUINT16	e_res2_2;	// Reserved words
-  LEUINT16	e_res2_3;	// Reserved words
-  LEUINT16	e_res2_4;	// Reserved words
-  LEUINT16	e_res2_5;	// Reserved words
-  LEUINT16	e_res2_6;	// Reserved words
-  LEUINT16	e_res2_7;	// Reserved words
-  LEUINT16	e_res2_8;	// Reserved words
-  LEUINT16	e_res2_9;	// Reserved words
-  OT::OffsetTo<LE_IMAGE_OS2_HEADER, LEUINT32>
-		e_lfanew;	// File address of new exe header
-  public:
-  DEFINE_SIZE_STATIC (64);
-};
-
-int main (int argc, char** argv) {
-  hb_blob_t *blob = hb_blob_create_from_file (argv[1]);
-
-  OT::Sanitizer<LE_IMAGE_DOS_HEADER> sanitizer;
-  hb_blob_t *font_blob = sanitizer.sanitize (blob);
-  const LE_IMAGE_DOS_HEADER* dos_header = font_blob->as<LE_IMAGE_DOS_HEADER> ();
-
-  const NE_RESOURCE_TABLE &rtable = dos_header->get_os2_header ().get_resource_table ();
-  int shift = rtable.get_shift_value ();
-  const NE_TYPEINFO& entry = rtable.get_fonts_entry ();
-  for (unsigned int i = 0; i < entry.get_count (); ++i)
-  {
-    const LE_FONTINFO16& font = entry.get_font (i, dos_header, shift);
-    printf ("version: %x, weight: %d, charset: %s\n", font.get_version (),
-	    font.get_weight (), font.get_charset ());
-    // font.dump ();
-  }
-  return 0;
-}
diff --git a/src/gen-use-table.py b/src/gen-use-table.py
index e7c621c..c742eba 100755
--- a/src/gen-use-table.py
+++ b/src/gen-use-table.py
@@ -328,6 +328,9 @@
 		# TODO: https://github.com/harfbuzz/harfbuzz/pull/626
 		if U == 0xA8B4: UISC = Consonant_Medial
 
+		# TODO: https://github.com/harfbuzz/harfbuzz/issues/1105
+		if U == 0x11134: UISC = Gemination_Mark
+
 		values = [k for k,v in items if v(U,UISC,UGC)]
 		assert len(values) == 1, "%s %s %s %s" % (hex(U), UISC, UGC, values)
 		USE = values[0]
@@ -356,6 +359,13 @@
 		# https://github.com/roozbehp/unicode-data/issues/8
 		if U == 0x0A51: UIPC = Bottom
 
+		# TODO: https://github.com/harfbuzz/harfbuzz/pull/982
+		if UBlock == 'Chakma' and is_VOWEL (U, UISC, UGC):
+			if UIPC == Top:
+				UIPC = Bottom
+			elif UIPC == Bottom:
+				UIPC = Top
+
 		assert (UIPC in [Not_Applicable, Visual_Order_Left] or
 			USE in use_positions), "%s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UGC)
 
diff --git a/src/hb-aat-fmtx-table.hh b/src/hb-aat-fmtx-table.hh
deleted file mode 100644
index aa82c88..0000000
--- a/src/hb-aat-fmtx-table.hh
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright © 2018  Ebrahim Byagowi
- *
- *  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.
- */
-
-#ifndef HB_AAT_FMTX_TABLE_HH
-#define HB_AAT_FMTX_TABLE_HH
-
-#include "hb-aat-layout-common-private.hh"
-
-/*
- * fmtx -- Font Metrics
- * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6fmtx.html
- */
-#define HB_AAT_TAG_fmtx HB_TAG('f','m','t','x')
-
-
-namespace AAT {
-
-
-struct fmtx
-{
-  static const hb_tag_t tableTag = HB_AAT_TAG_fmtx;
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
-  }
-
-  FixedVersion<>version;		/* Version (set to 0x00020000). */
-  HBUINT32	glyphIndex;		/* The glyph whose points represent the metrics. */
-  HBUINT8	horizontalBefore;	/* Point number for the horizontal ascent. */
-  HBUINT8	horizontalAfter;	/* Point number for the horizontal descent. */
-  HBUINT8	horizontalCaretHead;	/* Point number for the horizontal caret head. */
-  HBUINT8	horizontalCaretBase;	/* Point number for the horizontal caret base. */
-  HBUINT8	verticalBefore;		/* Point number for the vertical ascent. */
-  HBUINT8	verticalAfter;		/* Point number for the vertical descent. */
-  HBUINT8	verticalCaretHead;	/* Point number for the vertical caret head. */
-  HBUINT8	verticalCaretBase;	/* Point number for the vertical caret base. */
-  public:
-  DEFINE_SIZE_STATIC (16);
-};
-
-} /* namespace AAT */
-
-
-#endif /* HB_AAT_FMTX_TABLE_HH */
diff --git a/src/hb-aat-gcid-table.hh b/src/hb-aat-gcid-table.hh
deleted file mode 100644
index b48a279..0000000
--- a/src/hb-aat-gcid-table.hh
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright © 2018  Ebrahim Byagowi
- *
- *  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.
- */
-
-#ifndef HB_AAT_GCID_TABLE_HH
-#define HB_AAT_GCID_TABLE_HH
-
-#include "hb-aat-layout-common-private.hh"
-
-/*
- * gcid -- Glyphs CIDs
- * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6gcid.html
- */
-#define HB_AAT_TAG_gcid HB_TAG('g','c','i','d')
-
-
-namespace AAT {
-
-
-struct gcid
-{
-  static const hb_tag_t tableTag = HB_AAT_TAG_gcid;
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) && CIDs.sanitize (c)));
-  }
-
-  protected:
-  HBUINT16	version;	/* Version number (set to 0) */
-  HBUINT16	format;		/* Data format (set to 0) */
-  HBUINT32	size;		/* Size of the table, including header */
-  HBUINT16	registry;	/* The registry ID */
-  HBUINT8	registryName[64];
-				/* The registry name in ASCII */
-  HBUINT16	order;		/* The order ID */
-  HBUINT8	orderName[64];	/* The order name in ASCII */
-  HBUINT16	supplementVersion;
-				/* The supplement version */
-  ArrayOf<HBUINT16>
-		CIDs;		/* The CIDs for the glyphs in the font,
-				 * starting with glyph 0. If a glyph does not correspond
-				 * to a CID in the identified collection, 0xFFFF is used.
-				 * This should not exceed the number of glyphs in the font. */
-  public:
-  DEFINE_SIZE_ARRAY (144, CIDs);
-};
-
-} /* namespace AAT */
-
-
-#endif /* HB_AAT_GCID_TABLE_HH */
diff --git a/src/hb-aat-layout-common-private.hh b/src/hb-aat-layout-common-private.hh
index 2825b18..d7f3505 100644
--- a/src/hb-aat-layout-common-private.hh
+++ b/src/hb-aat-layout-common-private.hh
@@ -161,7 +161,7 @@
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (arrayZ.sanitize (c, c->num_glyphs));
+    return_trace (arrayZ.sanitize (c, c->get_num_glyphs ()));
   }
 
   protected:
@@ -625,7 +625,7 @@
 		sanitizer (), lookup_index (0), debug_depth (0)
   {
     sanitizer.init (table);
-    sanitizer.num_glyphs = face->get_num_glyphs ();
+    sanitizer.set_num_glyphs (face->get_num_glyphs ());
     sanitizer.start_processing ();
   }
 
diff --git a/src/hb-aat-layout.cc b/src/hb-aat-layout.cc
index 7784fae..36d4037 100644
--- a/src/hb-aat-layout.cc
+++ b/src/hb-aat-layout.cc
@@ -36,48 +36,12 @@
 #include "hb-aat-layout-kerx-table.hh"
 #include "hb-aat-layout-morx-table.hh"
 #include "hb-aat-layout-trak-table.hh"
-#include "hb-aat-fmtx-table.hh" // Just so we compile it; unused otherwise.
-#include "hb-aat-gcid-table.hh" // Just so we compile it; unused otherwise.
 #include "hb-aat-ltag-table.hh" // Just so we compile it; unused otherwise.
 
 /*
- * morx/kerx/trak
+ * morx/kerx/trak/ankr
  */
 
-#if 0
-static inline const AAT::ankr&
-_get_ankr (hb_face_t *face, hb_blob_t **blob = nullptr)
-{
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face)))
-  {
-    if (blob)
-      *blob = hb_blob_get_empty ();
-    return Null(AAT::ankr);
-  }
-  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-  const AAT::ankr& ankr = *(layout->ankr.get ());
-  if (blob)
-    *blob = layout->ankr.blob;
-  return ankr;
-}
-
-static inline const AAT::kerx&
-_get_kerx (hb_face_t *face, hb_blob_t **blob = nullptr)
-{
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face)))
-  {
-    if (blob)
-      *blob = hb_blob_get_empty ();
-    return Null(AAT::kerx);
-  }
-  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-  /* XXX this doesn't call set_num_glyphs on sanitizer. */
-  const AAT::kerx& kerx = *(layout->kerx.get ());
-  if (blob)
-    *blob = layout->kerx.blob;
-  return kerx;
-}
-
 static inline const AAT::morx&
 _get_morx (hb_face_t *face, hb_blob_t **blob = nullptr)
 {
@@ -88,36 +52,16 @@
     return Null(AAT::morx);
   }
   hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-  /* XXX this doesn't call set_num_glyphs on sanitizer. */
-  const AAT::morx& morx = *(layout->morx.get ());
+  const AAT::morx& morx = *(layout->table.morx.get ());
   if (blob)
-    *blob = layout->morx.blob;
+    *blob = layout->table.morx.get_blob ();
   return morx;
 }
 
-static inline const AAT::trak&
-_get_trak (hb_face_t *face, hb_blob_t **blob = nullptr)
-{
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face)))
-  {
-    if (blob)
-      *blob = hb_blob_get_empty ();
-    return Null(AAT::trak);
-  }
-  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-  const AAT::trak& trak = *(layout->trak.get ());
-  if (blob)
-    *blob = layout->trak.blob;
-  return trak;
-}
-#endif
-
 // static inline void
 // _hb_aat_layout_create (hb_face_t *face)
 // {
-//   OT::Sanitizer<AAT::morx> sanitizer;
-//   sanitizer.set_num_glyphs (face->get_num_glyphs ());
-//   hb_blob_t *morx_blob = sanitizer.sanitize (face->reference_table (HB_AAT_TAG_morx));
+//   hb_blob_t *morx_blob = hb_sanitize_context_t ().reference_table<AAT::morx> (face);
 //   morx_blob->as<AAT::morx> ();
 
 //   if (0)
@@ -129,13 +73,11 @@
 void
 hb_aat_layout_substitute (hb_font_t *font, hb_buffer_t *buffer)
 {
-#if 0
   hb_blob_t *blob;
   const AAT::morx& morx = _get_morx (font->face, &blob);
 
   AAT::hb_aat_apply_context_t c (font, buffer, blob);
   morx.apply (&c);
-#endif
 }
 
 void
diff --git a/src/hb-atomic-private.hh b/src/hb-atomic-private.hh
index 12caaca..297c646 100644
--- a/src/hb-atomic-private.hh
+++ b/src/hb-atomic-private.hh
@@ -35,7 +35,10 @@
 #include "hb-private.hh"
 
 
-/* atomic_int */
+/*
+ * Atomic integers and pointers.
+ */
+
 
 /* We need external help for these */
 
@@ -43,38 +46,77 @@
  && defined(hb_atomic_ptr_impl_get) \
  && defined(hb_atomic_ptr_impl_cmpexch)
 
-/* Defined externally, i.e. in config.h; must have typedef'ed hb_atomic_int_impl_t as well. */
+/* Defined externally, i.e. in config.h. */
+
+
+#elif !defined(HB_NO_MT) && defined(__ATOMIC_CONSUME)
+
+/* C++11-style GCC primitives. */
+
+#define hb_atomic_int_impl_add(AI, V)		__atomic_fetch_add ((AI), (V), __ATOMIC_ACQ_REL)
+#define hb_atomic_int_impl_set_relaxed(AI, V)	__atomic_store_n ((AI), (V), __ATOMIC_RELAXED)
+#define hb_atomic_int_impl_get_relaxed(AI)	__atomic_load_n ((AI), __ATOMIC_RELAXED)
+
+#define hb_atomic_ptr_impl_set_relaxed(P, V)	__atomic_store_n ((P), (V), __ATOMIC_RELAXED)
+#define hb_atomic_ptr_impl_get_relaxed(P)	__atomic_load_n ((P), __ATOMIC_RELAXED)
+#define hb_atomic_ptr_impl_get(P)		__atomic_load_n ((P), __ATOMIC_CONSUME)
+static inline bool
+_hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
+{
+  const void *O = O_; // Need lvalue
+  return __atomic_compare_exchange_n ((void **) P, (void **) &O, (void *) N, true, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED);
+}
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)	_hb_atomic_ptr_impl_cmplexch ((const void **) (P), (O), (N))
+
+#elif !defined(HB_NO_MT) && __cplusplus >= 201103L
+
+/* C++11 atomics. */
+
+#include <atomic>
+
+#define hb_atomic_int_impl_add(AI, V)		(reinterpret_cast<std::atomic<int> *> (AI)->fetch_add ((V), std::memory_order_acq_rel))
+#define hb_atomic_int_impl_set_relaxed(AI, V)	(reinterpret_cast<std::atomic<int> *> (AI)->store ((V), std::memory_order_relaxed))
+#define hb_atomic_int_impl_get_relaxed(AI)	(reinterpret_cast<std::atomic<int> *> (AI)->load (std::memory_order_relaxed))
+
+#define hb_atomic_ptr_impl_set_relaxed(P, V)	(reinterpret_cast<std::atomic<void*> *> (P)->store ((V), std::memory_order_relaxed))
+#define hb_atomic_ptr_impl_get_relaxed(P)	(reinterpret_cast<std::atomic<void*> *> (P)->load (std::memory_order_relaxed))
+#define hb_atomic_ptr_impl_get(P)		(reinterpret_cast<std::atomic<void*> *> (P)->load (std::memory_order_consume))
+static inline bool
+_hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
+{
+  const void *O = O_; // Need lvalue
+  return reinterpret_cast<std::atomic<const void*> *> (P)->compare_exchange_weak (O, N, std::memory_order_acq_rel, std::memory_order_relaxed);
+}
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)	_hb_atomic_ptr_impl_cmplexch ((const void **) (P), (O), (N))
 
 
 #elif !defined(HB_NO_MT) && (defined(_WIN32) || defined(__CYGWIN__))
 
 #include <windows.h>
 
-/* MinGW has a convoluted history of supporting MemoryBarrier
- * properly.  As such, define a function to wrap the whole
- * thing. */
-static inline void _HBMemoryBarrier (void) {
+static inline void _hb_memory_barrier (void)
+{
 #if !defined(MemoryBarrier)
+  /* MinGW has a convoluted history of supporting MemoryBarrier. */
   long dummy = 0;
   InterlockedExchange (&dummy, 1);
 #else
   MemoryBarrier ();
 #endif
 }
+#define _hb_memory_barrier()			_hb_memory_barrier ()
 
-typedef LONG hb_atomic_int_impl_t;
-#define hb_atomic_int_impl_add(AI, V)		InterlockedExchangeAdd (&(AI), (V))
+#define hb_atomic_int_impl_add(AI, V)		InterlockedExchangeAdd ((unsigned *) (AI), (V))
 
-#define hb_atomic_ptr_impl_get(P)		(_HBMemoryBarrier (), (void *) *(P))
 #define hb_atomic_ptr_impl_cmpexch(P,O,N)	(InterlockedCompareExchangePointer ((void **) (P), (void *) (N), (void *) (O)) == (void *) (O))
 
 
 #elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES)
 
-typedef int hb_atomic_int_impl_t;
-#define hb_atomic_int_impl_add(AI, V)		__sync_fetch_and_add (&(AI), (V))
+#define _hb_memory_barrier()			__sync_synchronize ()
 
-#define hb_atomic_ptr_impl_get(P)		(void *) (__sync_synchronize (), *(P))
+#define hb_atomic_int_impl_add(AI, V)		__sync_fetch_and_add ((AI), (V))
+
 #define hb_atomic_ptr_impl_cmpexch(P,O,N)	__sync_bool_compare_and_swap ((P), (O), (N))
 
 
@@ -83,11 +125,28 @@
 #include <atomic.h>
 #include <mbarrier.h>
 
-typedef unsigned int hb_atomic_int_impl_t;
-#define hb_atomic_int_impl_add(AI, V)		( ({__machine_rw_barrier ();}), atomic_add_int_nv (&(AI), (V)) - (V))
+#define _hb_memory_r_barrier()			__machine_r_barrier ()
+#define _hb_memory_w_barrier()			__machine_w_barrier ()
+#define _hb_memory_barrier()			__machine_rw_barrier ()
 
-#define hb_atomic_ptr_impl_get(P)		( ({__machine_rw_barrier ();}), (void *) *(P))
-#define hb_atomic_ptr_impl_cmpexch(P,O,N)	( ({__machine_rw_barrier ();}), atomic_cas_ptr ((void **) (P), (void *) (O), (void *) (N)) == (void *) (O) ? true : false)
+static inline int _hb_fetch_and_add (int *AI, int V)
+{
+  _hb_memory_w_barrier ();
+  int result = atomic_add_int_nv ((uint_t *) AI, V);
+  _hb_memory_r_barrier ();
+  return result;
+}
+static inline bool _hb_compare_and_swap_ptr (const void **P, const void *O, const void *N)
+{
+  _hb_memory_w_barrier ();
+  int result = atomic_cas_ptr ((void **) P, (void *) O, (void *) N) == (void *) O;
+  _hb_memory_r_barrier ();
+  return result;
+}
+
+#define hb_atomic_int_impl_add(AI, V)           _hb_fetch_and_add ((AI), (V))
+
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)       _hb_compare_and_swap_ptr ((const void **) (P), (O), (N))
 
 
 #elif !defined(HB_NO_MT) && defined(__APPLE__)
@@ -99,11 +158,10 @@
 #include <Availability.h>
 #endif
 
+#define _hb_memory_barrier()			OSMemoryBarrier ()
 
-typedef int32_t hb_atomic_int_impl_t;
-#define hb_atomic_int_impl_add(AI, V)		(OSAtomicAdd32Barrier ((V), &(AI)) - (V))
+#define hb_atomic_int_impl_add(AI, V)		(OSAtomicAdd32Barrier ((V), (AI)) - (V))
 
-#define hb_atomic_ptr_impl_get(P)		(OSMemoryBarrier (), (void *) *(P))
 #if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 || __IPHONE_VERSION_MIN_REQUIRED >= 20100)
 #define hb_atomic_ptr_impl_cmpexch(P,O,N)	OSAtomicCompareAndSwapPtrBarrier ((void *) (O), (void *) (N), (void **) (P))
 #else
@@ -119,64 +177,105 @@
 
 #include <builtins.h>
 
+#define _hb_memory_barrier()			__lwsync ()
 
-static inline int _hb_fetch_and_add(volatile int* AI, unsigned int V) {
-  __lwsync();
-  int result = __fetch_and_add(AI, V);
-  __isync();
+static inline int _hb_fetch_and_add (int *AI, int V)
+{
+  _hb_memory_barrier ();
+  int result = __fetch_and_add (AI, V);
+  _hb_memory_barrier ();
   return result;
 }
-static inline int _hb_compare_and_swaplp(volatile long* P, long O, long N) {
-  __sync();
-  int result = __compare_and_swaplp (P, &O, N);
-  __sync();
+static inline bool _hb_compare_and_swaplp (long *P, long O, long N)
+{
+  _hb_memory_barrier ();
+  bool result = __compare_and_swaplp (P, &O, N);
+  _hb_memory_barrier ();
   return result;
 }
 
-typedef int hb_atomic_int_impl_t;
-#define hb_atomic_int_impl_add(AI, V)           _hb_fetch_and_add (&(AI), (V))
+#define hb_atomic_int_impl_add(AI, V)           _hb_fetch_and_add ((AI), (V))
 
-#define hb_atomic_ptr_impl_get(P)               (__sync(), (void *) *(P))
-#define hb_atomic_ptr_impl_cmpexch(P,O,N)       _hb_compare_and_swaplp ((long*)(P), (long)(O), (long)(N))
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)       _hb_compare_and_swaplp ((long *) (P), (long) (O), (long) (N))
+static_assert ((sizeof (long) == sizeof (void *)), "");
+
 
 #elif !defined(HB_NO_MT)
 
 #define HB_ATOMIC_INT_NIL 1 /* Warn that fallback implementation is in use. */
 
-typedef volatile int hb_atomic_int_impl_t;
-#define hb_atomic_int_impl_add(AI, V)		(((AI) += (V)) - (V))
+#define _hb_memory_barrier()
 
-#define hb_atomic_ptr_impl_get(P)		((void *) *(P))
-#define hb_atomic_ptr_impl_cmpexch(P,O,N)	(* (void * volatile *) (P) == (void *) (O) ? (* (void * volatile *) (P) = (void *) (N), true) : false)
+#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 */
 
-typedef int hb_atomic_int_impl_t;
-#define hb_atomic_int_impl_add(AI, V)		(((AI) += (V)) - (V))
+#define hb_atomic_int_impl_add(AI, V)		((*(AI) += (V)) - (V))
 
-#define hb_atomic_ptr_impl_get(P)		((void *) *(P))
+#define _hb_memory_barrier()
+
 #define hb_atomic_ptr_impl_cmpexch(P,O,N)	(* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
 
 
 #endif
 
 
-#define HB_ATOMIC_INT_INIT(V)          {V}
+#ifndef _hb_memory_r_barrier
+#define _hb_memory_r_barrier()			_hb_memory_barrier ()
+#endif
+#ifndef _hb_memory_w_barrier
+#define _hb_memory_w_barrier()			_hb_memory_barrier ()
+#endif
+#ifndef hb_atomic_int_impl_set_relaxed
+#define hb_atomic_int_impl_set_relaxed(AI, V)	(*(AI) = (V))
+#endif
+#ifndef hb_atomic_int_impl_get_relaxed
+#define hb_atomic_int_impl_get_relaxed(AI)	(*(AI))
+#endif
 
+#ifndef hb_atomic_ptr_impl_set_relaxed
+#define hb_atomic_ptr_impl_set_relaxed(P, V)	(*(P) = (V))
+#endif
+#ifndef hb_atomic_ptr_impl_get_relaxed
+#define hb_atomic_ptr_impl_get_relaxed(P)	(*(P))
+#endif
+#ifndef hb_atomic_ptr_impl_get
+inline void *hb_atomic_ptr_impl_get (void **P)	{ void *v = *P; _hb_memory_r_barrier (); return v; }
+#endif
+
+
+#define HB_ATOMIC_INT_INIT(V)          {V}
 struct hb_atomic_int_t
 {
-  hb_atomic_int_impl_t v;
+  inline void set_relaxed (int v_) const { hb_atomic_int_impl_set_relaxed (&v, v_); }
+  inline int get_relaxed (void) const { return hb_atomic_int_impl_get_relaxed (&v); }
+  inline int inc (void) { return hb_atomic_int_impl_add (&v,  1); }
+  inline int dec (void) { return hb_atomic_int_impl_add (&v, -1); }
 
-  inline void set_unsafe (int v_) { v = v_; }
-  inline int get_unsafe (void) const { return v; }
-  inline int inc (void) { return hb_atomic_int_impl_add (const_cast<hb_atomic_int_impl_t &> (v),  1); }
-  inline int dec (void) { return hb_atomic_int_impl_add (const_cast<hb_atomic_int_impl_t &> (v), -1); }
+  mutable int v;
 };
 
 
-#define hb_atomic_ptr_get(P) hb_atomic_ptr_impl_get(P)
-#define hb_atomic_ptr_cmpexch(P,O,N) hb_atomic_ptr_impl_cmpexch((P),(O),(N))
+template <typename T> struct hb_remove_ptr_t { typedef T value; };
+template <typename T> struct hb_remove_ptr_t<T *> { typedef T value; };
+
+#define HB_ATOMIC_PTR_INIT(V)          {V}
+template <typename P>
+struct hb_atomic_ptr_t
+{
+  typedef typename hb_remove_ptr_t<P>::value T;
+
+  inline void init (T* v_ = nullptr) { set_relaxed (v_); }
+  inline void set_relaxed (T* v_) const { hb_atomic_ptr_impl_set_relaxed (&v, v_); }
+  inline T *get_relaxed (void) const { return hb_atomic_ptr_impl_get_relaxed (&v); }
+  inline T *get (void) const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); }
+  inline bool cmpexch (const T *old, T *new_) const{ return hb_atomic_ptr_impl_cmpexch (&v, old, new_); }
+
+  mutable T *v;
+};
 
 
 #endif /* HB_ATOMIC_PRIVATE_HH */
diff --git a/src/hb-blob-private.hh b/src/hb-blob-private.hh
index 56a7b66..49ad68e 100644
--- a/src/hb-blob-private.hh
+++ b/src/hb-blob-private.hh
@@ -57,11 +57,6 @@
   HB_INTERNAL bool try_make_writable_inplace (void);
   HB_INTERNAL bool try_make_writable_inplace_unix (void);
 
-  inline void lock (void)
-  {
-    hb_blob_make_immutable (this);
-  }
-
   template <typename Type>
   inline const Type* as (void) const
   {
@@ -81,6 +76,7 @@
   void *user_data;
   hb_destroy_func_t destroy;
 };
+DECLARE_NULL_INSTANCE (hb_blob_t);
 
 
 #endif /* HB_BLOB_PRIVATE_HH */
diff --git a/src/hb-blob.cc b/src/hb-blob.cc
index 5bab1ab..25c3e05 100644
--- a/src/hb-blob.cc
+++ b/src/hb-blob.cc
@@ -45,6 +45,20 @@
 #include <stdlib.h>
 
 
+DEFINE_NULL_INSTANCE (hb_blob_t) =
+{
+  HB_OBJECT_HEADER_STATIC,
+
+  true, /* immutable */
+
+  nullptr, /* data */
+  0, /* length */
+  HB_MEMORY_MODE_READONLY, /* mode */
+
+  nullptr, /* user_data */
+  nullptr  /* destroy */
+};
+
 /**
  * hb_blob_create: (skip)
  * @data: Pointer to blob data.
@@ -182,20 +196,7 @@
 hb_blob_t *
 hb_blob_get_empty (void)
 {
-  static const hb_blob_t _hb_blob_nil = {
-    HB_OBJECT_HEADER_STATIC,
-
-    true, /* immutable */
-
-    nullptr, /* data */
-    0, /* length */
-    HB_MEMORY_MODE_READONLY, /* mode */
-
-    nullptr, /* user_data */
-    nullptr  /* destroy */
-  };
-
-  return const_cast<hb_blob_t *> (&_hb_blob_nil);
+  return const_cast<hb_blob_t *> (&Null(hb_blob_t));
 }
 
 /**
diff --git a/src/hb-buffer-private.hh b/src/hb-buffer-private.hh
index f458140..a6c4b69 100644
--- a/src/hb-buffer-private.hh
+++ b/src/hb-buffer-private.hh
@@ -83,7 +83,8 @@
  * hb_buffer_t
  */
 
-struct hb_buffer_t {
+struct hb_buffer_t
+{
   hb_object_header_t header;
   ASSERT_POD ();
 
@@ -352,6 +353,7 @@
       info[i].mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
   }
 };
+DECLARE_NULL_INSTANCE (hb_buffer_t);
 
 
 /* Loop over clusters. Duplicated in foreach_syllable(). */
diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
index d04e1e8..eb7b01c 100644
--- a/src/hb-buffer.cc
+++ b/src/hb-buffer.cc
@@ -216,7 +216,7 @@
     return;
 
   hb_unicode_funcs_destroy (unicode);
-  unicode = hb_unicode_funcs_get_default ();
+  unicode = hb_unicode_funcs_reference (hb_unicode_funcs_get_default ());
   flags = HB_BUFFER_FLAG_DEFAULT;
   replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT;
 
@@ -701,6 +701,28 @@
 
 /* Public API */
 
+DEFINE_NULL_INSTANCE (hb_buffer_t) =
+{
+  HB_OBJECT_HEADER_STATIC,
+
+  const_cast<hb_unicode_funcs_t *> (&_hb_Null_hb_unicode_funcs_t),
+  HB_BUFFER_FLAG_DEFAULT,
+  HB_BUFFER_CLUSTER_LEVEL_DEFAULT,
+  HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT,
+  HB_BUFFER_SCRATCH_FLAG_DEFAULT,
+  HB_BUFFER_MAX_LEN_DEFAULT,
+  HB_BUFFER_MAX_OPS_DEFAULT,
+
+  HB_BUFFER_CONTENT_TYPE_INVALID,
+  HB_SEGMENT_PROPERTIES_DEFAULT,
+  false, /* successful */
+  true, /* have_output */
+  true  /* have_positions */
+
+  /* Zero is good enough for everything else. */
+};
+
+
 /**
  * hb_buffer_create: (Xconstructor)
  *
@@ -743,27 +765,7 @@
 hb_buffer_t *
 hb_buffer_get_empty (void)
 {
-  static const hb_buffer_t _hb_buffer_nil = {
-    HB_OBJECT_HEADER_STATIC,
-
-    const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil),
-    HB_BUFFER_FLAG_DEFAULT,
-    HB_BUFFER_CLUSTER_LEVEL_DEFAULT,
-    HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT,
-    HB_BUFFER_SCRATCH_FLAG_DEFAULT,
-    HB_BUFFER_MAX_LEN_DEFAULT,
-    HB_BUFFER_MAX_OPS_DEFAULT,
-
-    HB_BUFFER_CONTENT_TYPE_INVALID,
-    HB_SEGMENT_PROPERTIES_DEFAULT,
-    false, /* successful */
-    true, /* have_output */
-    true  /* have_positions */
-
-    /* Zero is good enough for everything else. */
-  };
-
-  return const_cast<hb_buffer_t *> (&_hb_buffer_nil);
+  return const_cast<hb_buffer_t *> (&Null(hb_buffer_t));
 }
 
 /**
@@ -906,7 +908,6 @@
   if (!unicode_funcs)
     unicode_funcs = hb_unicode_funcs_get_default ();
 
-
   hb_unicode_funcs_reference (unicode_funcs);
   hb_unicode_funcs_destroy (buffer->unicode);
   buffer->unicode = unicode_funcs;
diff --git a/src/hb-common.cc b/src/hb-common.cc
index 243c216..22dd52f 100644
--- a/src/hb-common.cc
+++ b/src/hb-common.cc
@@ -28,6 +28,7 @@
 
 #include "hb-private.hh"
 
+#include "hb-machinery-private.hh"
 
 #include <locale.h>
 #ifdef HAVE_XLOCALE_H
@@ -37,7 +38,7 @@
 
 /* hb_options_t */
 
-hb_options_union_t _hb_options;
+hb_atomic_int_t _hb_options;
 
 void
 _hb_options_init (void)
@@ -50,7 +51,7 @@
   u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
 
   /* This is idempotent and threadsafe. */
-  _hb_options = u;
+  _hb_options.set_relaxed (u.i);
 }
 
 
@@ -244,15 +245,15 @@
 
 /* Thread-safe lock-free language list */
 
-static hb_language_item_t *langs;
+static hb_atomic_ptr_t <hb_language_item_t> langs;
 
 #ifdef HB_USE_ATEXIT
 static void
 free_langs (void)
 {
 retry:
-  hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
-  if (!hb_atomic_ptr_cmpexch (&langs, first_lang, nullptr))
+  hb_language_item_t *first_lang = langs.get ();
+  if (unlikely (!langs.cmpexch (first_lang, nullptr)))
     goto retry;
 
   while (first_lang) {
@@ -268,7 +269,7 @@
 lang_find_or_insert (const char *key)
 {
 retry:
-  hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
+  hb_language_item_t *first_lang = langs.get ();
 
   for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
     if (*lang == key)
@@ -286,7 +287,8 @@
     return nullptr;
   }
 
-  if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
+  if (unlikely (!langs.cmpexch (first_lang, lang)))
+  {
     lang->fini ();
     free (lang);
     goto retry;
@@ -368,15 +370,16 @@
 hb_language_t
 hb_language_get_default (void)
 {
-  static hb_language_t default_language = HB_LANGUAGE_INVALID;
+  static hb_atomic_ptr_t <hb_language_t> default_language;
 
-  hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
-  if (unlikely (language == HB_LANGUAGE_INVALID)) {
+  hb_language_t language = default_language.get ();
+  if (unlikely (language == HB_LANGUAGE_INVALID))
+  {
     language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1);
-    (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
+    (void) default_language.cmpexch (HB_LANGUAGE_INVALID, language);
   }
 
-  return default_language;
+  return language;
 }
 
 
@@ -728,47 +731,46 @@
 
 #ifdef USE_XLOCALE
 
-static HB_LOCALE_T C_locale;
+
+static void free_static_C_locale (void);
+
+static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_ptr_t<HB_LOCALE_T>::value,
+							  hb_C_locale_lazy_loader_t>
+{
+  static inline HB_LOCALE_T create (void)
+  {
+    HB_LOCALE_T C_locale = HB_CREATE_LOCALE ("C");
 
 #ifdef HB_USE_ATEXIT
-static void
-free_C_locale (void)
+    atexit (free_static_C_locale);
+#endif
+
+    return C_locale;
+  }
+  static inline void destroy (HB_LOCALE_T p)
+  {
+    HB_FREE_LOCALE (p);
+  }
+  static inline HB_LOCALE_T get_null (void)
+  {
+    return nullptr;
+  }
+} static_C_locale;
+
+#ifdef HB_USE_ATEXIT
+static
+void free_static_C_locale (void)
 {
-retry:
-  HB_LOCALE_T locale = (HB_LOCALE_T) hb_atomic_ptr_get (&C_locale);
-
-  if (!hb_atomic_ptr_cmpexch (&C_locale, locale, nullptr))
-    goto retry;
-
-  if (locale)
-    HB_FREE_LOCALE (locale);
+  static_C_locale.free_instance ();
 }
 #endif
 
 static HB_LOCALE_T
 get_C_locale (void)
 {
-retry:
-  HB_LOCALE_T C = (HB_LOCALE_T) hb_atomic_ptr_get (&C_locale);
-
-  if (unlikely (!C))
-  {
-    C = HB_CREATE_LOCALE ("C");
-
-    if (!hb_atomic_ptr_cmpexch (&C_locale, nullptr, C))
-    {
-      HB_FREE_LOCALE (C_locale);
-      goto retry;
-    }
-
-#ifdef HB_USE_ATEXIT
-    atexit (free_C_locale); /* First person registers atexit() callback. */
-#endif
-  }
-
-  return C;
+  return static_C_locale.get_unconst ();
 }
-#endif
+#endif /* USE_XLOCALE */
 
 static bool
 parse_float (const char **pp, const char *end, float *pv)
@@ -845,7 +847,7 @@
   }
 
   const char *p = *pp;
-  while (*pp < end && ISALNUM(**pp))
+  while (*pp < end && (ISALNUM(**pp) || **pp == '_'))
     (*pp)++;
 
   if (p == *pp || *pp - p > 4)
@@ -1069,3 +1071,12 @@
   memcpy (buf, s, len);
   buf[len] = '\0';
 }
+
+/* If there is no visibility control, then hb-static.cc will NOT
+ * define anything.  Instead, we get it to define one set in here
+ * only, so only libharfbuzz.so defines them, not other libs. */
+#ifdef HB_NO_VISIBILITY
+#undef HB_NO_VISIBILITY
+#include "hb-static.cc"
+#define HB_NO_VISIBILITY 1
+#endif
diff --git a/src/hb-coretext.cc b/src/hb-coretext.cc
index 3bdc3f7..1b916a5 100644
--- a/src/hb-coretext.cc
+++ b/src/hb-coretext.cc
@@ -269,7 +269,7 @@
   return ct_font;
 }
 
-hb_coretext_shaper_face_data_t *
+hb_coretext_face_data_t *
 _hb_coretext_shaper_face_data_create (hb_face_t *face)
 {
   CGFontRef cg_font = create_cg_font (face);
@@ -280,11 +280,11 @@
     return nullptr;
   }
 
-  return (hb_coretext_shaper_face_data_t *) cg_font;
+  return (hb_coretext_face_data_t *) cg_font;
 }
 
 void
-_hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data)
+_hb_coretext_shaper_face_data_destroy (hb_coretext_face_data_t *data)
 {
   CFRelease ((CGFontRef) data);
 }
@@ -306,7 +306,7 @@
 }
 
 
-hb_coretext_shaper_font_data_t *
+hb_coretext_font_data_t *
 _hb_coretext_shaper_font_data_create (hb_font_t *font)
 {
   hb_face_t *face = font->face;
@@ -321,11 +321,11 @@
     return nullptr;
   }
 
-  return (hb_coretext_shaper_font_data_t *) ct_font;
+  return (hb_coretext_font_data_t *) ct_font;
 }
 
 void
-_hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t *data)
+_hb_coretext_shaper_font_data_destroy (hb_coretext_font_data_t *data)
 {
   CFRelease ((CTFontRef) data);
 }
@@ -348,7 +348,7 @@
   hb_font_set_ptem (font, coretext_font_size_to_ptem (CTFontGetSize(ct_font)));
 
   /* Let there be dragons here... */
-  HB_SHAPER_DATA_GET (font) = (hb_coretext_shaper_font_data_t *) CFRetain (ct_font);
+  HB_SHAPER_DATA (HB_SHAPER, font).set_relaxed ((hb_coretext_font_data_t *) CFRetain (ct_font));
 
   return font;
 }
@@ -366,20 +366,20 @@
  * shaper shape_plan data
  */
 
-struct hb_coretext_shaper_shape_plan_data_t {};
+struct hb_coretext_shape_plan_data_t {};
 
-hb_coretext_shaper_shape_plan_data_t *
+hb_coretext_shape_plan_data_t *
 _hb_coretext_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
 					     const hb_feature_t *user_features HB_UNUSED,
 					     unsigned int        num_user_features HB_UNUSED,
 					     const int          *coords HB_UNUSED,
 					     unsigned int        num_coords HB_UNUSED)
 {
-  return (hb_coretext_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_coretext_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shaper_shape_plan_data_t *data HB_UNUSED)
+_hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shape_plan_data_t *data HB_UNUSED)
 {
 }
 
@@ -1329,9 +1329,9 @@
  * shaper face data
  */
 
-struct hb_coretext_aat_shaper_face_data_t {};
+struct hb_coretext_aat_face_data_t {};
 
-hb_coretext_aat_shaper_face_data_t *
+hb_coretext_aat_face_data_t *
 _hb_coretext_aat_shaper_face_data_create (hb_face_t *face)
 {
   static const hb_tag_t tags[] = {HB_CORETEXT_TAG_MORX, HB_CORETEXT_TAG_MORT, HB_CORETEXT_TAG_KERX};
@@ -1342,7 +1342,7 @@
     if (hb_blob_get_length (blob))
     {
       hb_blob_destroy (blob);
-      return hb_coretext_shaper_face_data_ensure (face) ? (hb_coretext_aat_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr;
+      return hb_coretext_shaper_face_data_ensure (face) ? (hb_coretext_aat_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr;
     }
     hb_blob_destroy (blob);
   }
@@ -1351,7 +1351,7 @@
 }
 
 void
-_hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_shaper_face_data_t *data HB_UNUSED)
+_hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_face_data_t *data HB_UNUSED)
 {
 }
 
@@ -1360,16 +1360,16 @@
  * shaper font data
  */
 
-struct hb_coretext_aat_shaper_font_data_t {};
+struct hb_coretext_aat_font_data_t {};
 
-hb_coretext_aat_shaper_font_data_t *
+hb_coretext_aat_font_data_t *
 _hb_coretext_aat_shaper_font_data_create (hb_font_t *font)
 {
-  return hb_coretext_shaper_font_data_ensure (font) ? (hb_coretext_aat_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr;
+  return hb_coretext_shaper_font_data_ensure (font) ? (hb_coretext_aat_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr;
 }
 
 void
-_hb_coretext_aat_shaper_font_data_destroy (hb_coretext_aat_shaper_font_data_t *data HB_UNUSED)
+_hb_coretext_aat_shaper_font_data_destroy (hb_coretext_aat_font_data_t *data HB_UNUSED)
 {
 }
 
@@ -1378,20 +1378,20 @@
  * shaper shape_plan data
  */
 
-struct hb_coretext_aat_shaper_shape_plan_data_t {};
+struct hb_coretext_aat_shape_plan_data_t {};
 
-hb_coretext_aat_shaper_shape_plan_data_t *
+hb_coretext_aat_shape_plan_data_t *
 _hb_coretext_aat_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
 					     const hb_feature_t *user_features HB_UNUSED,
 					     unsigned int        num_user_features HB_UNUSED,
 					     const int          *coords HB_UNUSED,
 					     unsigned int        num_coords HB_UNUSED)
 {
-  return (hb_coretext_aat_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_coretext_aat_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_coretext_aat_shaper_shape_plan_data_destroy (hb_coretext_aat_shaper_shape_plan_data_t *data HB_UNUSED)
+_hb_coretext_aat_shaper_shape_plan_data_destroy (hb_coretext_aat_shape_plan_data_t *data HB_UNUSED)
 {
 }
 
diff --git a/src/hb-debug.hh b/src/hb-debug.hh
index ae0b677..9056ffc 100644
--- a/src/hb-debug.hh
+++ b/src/hb-debug.hh
@@ -28,6 +28,7 @@
 #define HB_DEBUG_HH
 
 #include "hb-private.hh"
+#include "hb-atomic-private.hh"
 #include "hb-dsalgs.hh"
 
 
@@ -35,6 +36,47 @@
 #define HB_DEBUG 0
 #endif
 
+
+/*
+ * Global runtime options.
+ */
+
+struct hb_options_t
+{
+  unsigned int unused : 1; /* In-case sign bit is here. */
+  unsigned int initialized : 1;
+  unsigned int uniscribe_bug_compatible : 1;
+};
+
+union hb_options_union_t {
+  int i;
+  hb_options_t opts;
+};
+static_assert ((sizeof (hb_atomic_int_t) >= sizeof (hb_options_union_t)), "");
+
+HB_INTERNAL void
+_hb_options_init (void);
+
+extern HB_INTERNAL hb_atomic_int_t _hb_options;
+
+static inline hb_options_t
+hb_options (void)
+{
+  /* Make a local copy, so we can access bitfield threadsafely. */
+  hb_options_union_t u;
+  u.i = _hb_options.get_relaxed ();
+
+  if (unlikely (!u.i))
+    _hb_options_init ();
+
+  return u.opts;
+}
+
+
+/*
+ * Debug output (needs enabling at compile time.)
+ */
+
 static inline bool
 _hb_debug (unsigned int level,
 	   unsigned int max_level)
diff --git a/src/hb-directwrite.cc b/src/hb-directwrite.cc
index 7c68849..baad818 100644
--- a/src/hb-directwrite.cc
+++ b/src/hb-directwrite.cc
@@ -133,7 +133,7 @@
 * shaper face data
 */
 
-struct hb_directwrite_shaper_face_data_t
+struct hb_directwrite_face_data_t
 {
   IDWriteFactory *dwriteFactory;
   IDWriteFontFile *fontFile;
@@ -143,10 +143,10 @@
   hb_blob_t *faceBlob;
 };
 
-hb_directwrite_shaper_face_data_t *
+hb_directwrite_face_data_t *
 _hb_directwrite_shaper_face_data_create (hb_face_t *face)
 {
-  hb_directwrite_shaper_face_data_t *data = new hb_directwrite_shaper_face_data_t;
+  hb_directwrite_face_data_t *data = new hb_directwrite_face_data_t;
   if (unlikely (!data))
     return nullptr;
 
@@ -206,7 +206,7 @@
 }
 
 void
-_hb_directwrite_shaper_face_data_destroy (hb_directwrite_shaper_face_data_t *data)
+_hb_directwrite_shaper_face_data_destroy (hb_directwrite_face_data_t *data)
 {
   if (data->fontFace)
     data->fontFace->Release ();
@@ -233,16 +233,16 @@
  * shaper font data
  */
 
-struct hb_directwrite_shaper_font_data_t
+struct hb_directwrite_font_data_t
 {
 };
 
-hb_directwrite_shaper_font_data_t *
+hb_directwrite_font_data_t *
 _hb_directwrite_shaper_font_data_create (hb_font_t *font)
 {
   if (unlikely (!hb_directwrite_shaper_face_data_ensure (font->face))) return nullptr;
 
-  hb_directwrite_shaper_font_data_t *data = new hb_directwrite_shaper_font_data_t;
+  hb_directwrite_font_data_t *data = new hb_directwrite_font_data_t;
   if (unlikely (!data))
     return nullptr;
 
@@ -250,7 +250,7 @@
 }
 
 void
-_hb_directwrite_shaper_font_data_destroy (hb_directwrite_shaper_font_data_t *data)
+_hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data)
 {
   delete data;
 }
@@ -260,20 +260,20 @@
  * shaper shape_plan data
  */
 
-struct hb_directwrite_shaper_shape_plan_data_t {};
+struct hb_directwrite_shape_plan_data_t {};
 
-hb_directwrite_shaper_shape_plan_data_t *
+hb_directwrite_shape_plan_data_t *
 _hb_directwrite_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
 					       const hb_feature_t *user_features HB_UNUSED,
 					       unsigned int        num_user_features HB_UNUSED,
 					       const int          *coords HB_UNUSED,
 					       unsigned int        num_coords HB_UNUSED)
 {
-  return (hb_directwrite_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_directwrite_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_directwrite_shaper_shape_plan_data_destroy (hb_directwrite_shaper_shape_plan_data_t *data HB_UNUSED)
+_hb_directwrite_shaper_shape_plan_data_destroy (hb_directwrite_shape_plan_data_t *data HB_UNUSED)
 {
 }
 
@@ -555,8 +555,8 @@
   float               lineWidth)
 {
   hb_face_t *face = font->face;
-  hb_directwrite_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
-  hb_directwrite_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
+  hb_directwrite_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
+  hb_directwrite_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
   IDWriteFactory *dwriteFactory = face_data->dwriteFactory;
   IDWriteFontFace *fontFace = face_data->fontFace;
 
diff --git a/src/hb-dsalgs.hh b/src/hb-dsalgs.hh
index 6a8ddaa..8cbe658 100644
--- a/src/hb-dsalgs.hh
+++ b/src/hb-dsalgs.hh
@@ -232,6 +232,18 @@
  * Tiny stuff.
  */
 
+/* ASCII tag/character handling */
+static inline bool ISALPHA (unsigned char c)
+{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
+static inline bool ISALNUM (unsigned char c)
+{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); }
+static inline bool ISSPACE (unsigned char c)
+{ return c == ' ' || c =='\f'|| c =='\n'|| c =='\r'|| c =='\t'|| c =='\v'; }
+static inline unsigned char TOUPPER (unsigned char c)
+{ return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; }
+static inline unsigned char TOLOWER (unsigned char c)
+{ return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; }
+
 #undef MIN
 template <typename Type>
 static inline Type MIN (const Type &a, const Type &b) { return a < b ? a : b; }
@@ -262,6 +274,37 @@
   return ((v - 1) | 3) + 1;
 }
 
+template <typename T> class hb_assert_unsigned_t;
+template <> class hb_assert_unsigned_t<unsigned char> {};
+template <> class hb_assert_unsigned_t<unsigned short> {};
+template <> class hb_assert_unsigned_t<unsigned int> {};
+template <> class hb_assert_unsigned_t<unsigned long> {};
+
+template <typename T> static inline bool
+hb_in_range (T u, T lo, T hi)
+{
+  /* The sizeof() is here to force template instantiation.
+   * I'm sure there are better ways to do this but can't think of
+   * one right now.  Declaring a variable won't work as HB_UNUSED
+   * is unusable on some platforms and unused types are less likely
+   * to generate a warning than unused variables. */
+  static_assert ((sizeof (hb_assert_unsigned_t<T>) >= 0), "");
+
+  /* The casts below are important as if T is smaller than int,
+   * the subtract results will become a signed int! */
+  return (T)(u - lo) <= (T)(hi - lo);
+}
+template <typename T> static inline bool
+hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2)
+{
+  return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2);
+}
+template <typename T> static inline bool
+hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3)
+{
+  return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3);
+}
+
 
 /*
  * Sort and search.
@@ -445,305 +488,6 @@
 }
 
 
-#define HB_VECTOR_INIT {0, 0, false, nullptr}
-template <typename Type, unsigned int StaticSize=8>
-struct hb_vector_t
-{
-  unsigned int len;
-  unsigned int allocated;
-  bool successful;
-  Type *arrayZ;
-  Type static_array[StaticSize];
-
-  void init (void)
-  {
-    len = 0;
-    allocated = ARRAY_LENGTH (static_array);
-    successful = true;
-    arrayZ = static_array;
-  }
-
-  inline Type& operator [] (unsigned int i)
-  {
-    if (unlikely (i >= len))
-      return Crap (Type);
-    return arrayZ[i];
-  }
-  inline const Type& operator [] (unsigned int i) const
-  {
-    if (unlikely (i >= len))
-      return Null(Type);
-    return arrayZ[i];
-  }
-
-  inline Type *push (void)
-  {
-    if (unlikely (!resize (len + 1)))
-      return &Crap(Type);
-    return &arrayZ[len - 1];
-  }
-  inline Type *push (const Type& v)
-  {
-    Type *p = push ();
-    *p = v;
-    return p;
-  }
-
-  /* Allocate for size but don't adjust len. */
-  inline bool alloc (unsigned int size)
-  {
-    if (unlikely (!successful))
-      return false;
-
-    if (likely (size <= allocated))
-      return true;
-
-    /* Reallocate */
-
-    unsigned int new_allocated = allocated;
-    while (size >= new_allocated)
-      new_allocated += (new_allocated >> 1) + 8;
-
-    Type *new_array = nullptr;
-
-    if (arrayZ == static_array)
-    {
-      new_array = (Type *) calloc (new_allocated, sizeof (Type));
-      if (new_array)
-        memcpy (new_array, arrayZ, len * sizeof (Type));
-    }
-    else
-    {
-      bool overflows = (new_allocated < allocated) || hb_unsigned_mul_overflows (new_allocated, sizeof (Type));
-      if (likely (!overflows))
-        new_array = (Type *) realloc (arrayZ, new_allocated * sizeof (Type));
-    }
-
-    if (unlikely (!new_array))
-    {
-      successful = false;
-      return false;
-    }
-
-    arrayZ = new_array;
-    allocated = new_allocated;
-
-    return true;
-  }
-
-  inline bool resize (int size_)
-  {
-    unsigned int size = size_ < 0 ? 0u : (unsigned int) size_;
-    if (!alloc (size))
-      return false;
-
-    if (size > len)
-      memset (arrayZ + len, 0, (size - len) * sizeof (*arrayZ));
-
-    len = size;
-    return true;
-  }
-
-  inline void pop (void)
-  {
-    if (!len) return;
-    len--;
-  }
-
-  inline void remove (unsigned int i)
-  {
-     if (unlikely (i >= len))
-       return;
-     memmove (static_cast<void *> (&arrayZ[i]),
-	      static_cast<void *> (&arrayZ[i + 1]),
-	      (len - i - 1) * sizeof (Type));
-     len--;
-  }
-
-  inline void shrink (int size_)
-  {
-    unsigned int size = size_ < 0 ? 0u : (unsigned int) size_;
-     if (size < len)
-       len = size;
-  }
-
-  template <typename T>
-  inline Type *find (T v) {
-    for (unsigned int i = 0; i < len; i++)
-      if (arrayZ[i] == v)
-	return &arrayZ[i];
-    return nullptr;
-  }
-  template <typename T>
-  inline const Type *find (T v) const {
-    for (unsigned int i = 0; i < len; i++)
-      if (arrayZ[i] == v)
-	return &arrayZ[i];
-    return nullptr;
-  }
-
-  inline void qsort (int (*cmp)(const void*, const void*))
-  {
-    ::qsort (arrayZ, len, sizeof (Type), cmp);
-  }
-
-  inline void qsort (void)
-  {
-    ::qsort (arrayZ, len, sizeof (Type), Type::cmp);
-  }
-
-  inline void qsort (unsigned int start, unsigned int end)
-  {
-    ::qsort (arrayZ + start, end - start, sizeof (Type), Type::cmp);
-  }
-
-  template <typename T>
-  inline Type *lsearch (const T &x)
-  {
-    for (unsigned int i = 0; i < len; i++)
-      if (0 == this->arrayZ[i].cmp (&x))
-	return &arrayZ[i];
-    return nullptr;
-  }
-
-  template <typename T>
-  inline Type *bsearch (const T &x)
-  {
-    unsigned int i;
-    return bfind (x, &i) ? &arrayZ[i] : nullptr;
-  }
-  template <typename T>
-  inline const Type *bsearch (const T &x) const
-  {
-    unsigned int i;
-    return bfind (x, &i) ? &arrayZ[i] : nullptr;
-  }
-  template <typename T>
-  inline bool bfind (const T &x, unsigned int *i) const
-  {
-    int min = 0, max = (int) this->len - 1;
-    while (min <= max)
-    {
-      int mid = (min + max) / 2;
-      int c = this->arrayZ[mid].cmp (&x);
-      if (c < 0)
-        max = mid - 1;
-      else if (c > 0)
-        min = mid + 1;
-      else
-      {
-        *i = mid;
-	return true;
-      }
-    }
-    if (max < 0 || (max < (int) this->len && this->arrayZ[max].cmp (&x) > 0))
-      max++;
-    *i = max;
-    return false;
-  }
-
-  inline void fini (void)
-  {
-    if (arrayZ != static_array)
-      free (arrayZ);
-    arrayZ = nullptr;
-    allocated = len = 0;
-  }
-};
-
-
-#define HB_LOCKABLE_SET_INIT {HB_VECTOR_INIT}
-template <typename item_t, typename lock_t>
-struct hb_lockable_set_t
-{
-  hb_vector_t <item_t, 1> items;
-
-  inline void init (void) { items.init (); }
-
-  template <typename T>
-  inline item_t *replace_or_insert (T v, lock_t &l, bool replace)
-  {
-    l.lock ();
-    item_t *item = items.find (v);
-    if (item) {
-      if (replace) {
-	item_t old = *item;
-	*item = v;
-	l.unlock ();
-	old.fini ();
-      }
-      else {
-        item = nullptr;
-	l.unlock ();
-      }
-    } else {
-      item = items.push (v);
-      l.unlock ();
-    }
-    return item;
-  }
-
-  template <typename T>
-  inline void remove (T v, lock_t &l)
-  {
-    l.lock ();
-    item_t *item = items.find (v);
-    if (item) {
-      item_t old = *item;
-      *item = items[items.len - 1];
-      items.pop ();
-      l.unlock ();
-      old.fini ();
-    } else {
-      l.unlock ();
-    }
-  }
-
-  template <typename T>
-  inline bool find (T v, item_t *i, lock_t &l)
-  {
-    l.lock ();
-    item_t *item = items.find (v);
-    if (item)
-      *i = *item;
-    l.unlock ();
-    return !!item;
-  }
-
-  template <typename T>
-  inline item_t *find_or_insert (T v, lock_t &l)
-  {
-    l.lock ();
-    item_t *item = items.find (v);
-    if (!item) {
-      item = items.push (v);
-    }
-    l.unlock ();
-    return item;
-  }
-
-  inline void fini (lock_t &l)
-  {
-    if (!items.len) {
-      /* No need for locking. */
-      items.fini ();
-      return;
-    }
-    l.lock ();
-    while (items.len) {
-      item_t old = items[items.len - 1];
-	items.pop ();
-	l.unlock ();
-	old.fini ();
-	l.lock ();
-    }
-    items.fini ();
-    l.unlock ();
-  }
-
-};
-
-
 template <typename Type>
 struct hb_auto_t : Type
 {
@@ -854,8 +598,7 @@
   union {
     elt_t v[byte_size / sizeof (elt_t)];
 #if HB_VECTOR_SIZE
-    typedef unsigned long vec_t __attribute__((vector_size (HB_VECTOR_SIZE / 8)));
-    vec_t vec[byte_size / sizeof (vec_t)];
+    hb_vector_size_impl_t vec[byte_size / sizeof (hb_vector_size_impl_t)];
 #endif
   } u;
 };
diff --git a/src/hb-face-private.hh b/src/hb-face-private.hh
index fe3b8b2..086ce6e 100644
--- a/src/hb-face-private.hh
+++ b/src/hb-face-private.hh
@@ -39,7 +39,8 @@
  * hb_face_t
  */
 
-struct hb_face_t {
+struct hb_face_t
+{
   hb_object_header_t header;
   ASSERT_POD ();
 
@@ -55,15 +56,13 @@
 
   struct hb_shaper_data_t shaper_data;	/* Various shaper data. */
 
-  /* Various non-shaping data. */
-  /* ... */
-
   /* Cache */
-  struct plan_node_t {
+  struct plan_node_t
+  {
     hb_shape_plan_t *shape_plan;
     plan_node_t *next;
-  } *shape_plans;
-
+  };
+  hb_atomic_ptr_t<plan_node_t> shape_plans;
 
   inline hb_blob_t *reference_table (hb_tag_t tag) const
   {
@@ -97,8 +96,7 @@
   HB_INTERNAL void load_upem (void) const;
   HB_INTERNAL void load_num_glyphs (void) const;
 };
-
-extern HB_INTERNAL const hb_face_t _hb_face_nil;
+DECLARE_NULL_INSTANCE (hb_face_t);
 
 #define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, face);
diff --git a/src/hb-face.cc b/src/hb-face.cc
index 2fef09d..49f29d3 100644
--- a/src/hb-face.cc
+++ b/src/hb-face.cc
@@ -31,9 +31,6 @@
 #include "hb-face-private.hh"
 #include "hb-blob-private.hh"
 #include "hb-open-file-private.hh"
-#include "hb-ot-head-table.hh"
-#include "hb-ot-maxp-table.hh"
-
 
 
 /**
@@ -52,17 +49,22 @@
   if (unlikely (!blob))
     return 0;
 
-  hb_blob_t *sanitized = OT::Sanitizer<OT::OpenTypeFontFile> ().sanitize (blob);
+  /* TODO We shouldn't be sanitizing blob.  Port to run sanitizer and return if not sane. */
+  /* Make API signature const after. */
+  hb_blob_t *sanitized = hb_sanitize_context_t ().sanitize_blob<OT::OpenTypeFontFile> (hb_blob_reference (blob));
   const OT::OpenTypeFontFile& ot = *sanitized->as<OT::OpenTypeFontFile> ();
+  unsigned int ret = ot.get_face_count ();
+  hb_blob_destroy (sanitized);
 
-  return ot.get_face_count ();
+  return ret;
 }
 
 /*
  * hb_face_t
  */
 
-const hb_face_t _hb_face_nil = {
+DEFINE_NULL_INSTANCE (hb_face_t) =
+{
   HB_OBJECT_HEADER_STATIC,
 
   true, /* immutable */
@@ -76,12 +78,12 @@
   0,    /* num_glyphs */
 
   {
-#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
+#define HB_SHAPER_IMPLEMENT(shaper) HB_ATOMIC_PTR_INIT (HB_SHAPER_DATA_INVALID),
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
   },
 
-  nullptr, /* shape_plans */
+  HB_ATOMIC_PTR_INIT (nullptr), /* shape_plans */
 };
 
 
@@ -188,7 +190,7 @@
   if (unlikely (!blob))
     blob = hb_blob_get_empty ();
 
-  hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (OT::Sanitizer<OT::OpenTypeFontFile>().sanitize (hb_blob_reference (blob)), index);
+  hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (hb_sanitize_context_t ().sanitize_blob<OT::OpenTypeFontFile> (hb_blob_reference (blob)), index);
 
   if (unlikely (!closure))
     return hb_face_get_empty ();
@@ -214,7 +216,7 @@
 hb_face_t *
 hb_face_get_empty (void)
 {
-  return const_cast<hb_face_t *> (&_hb_face_nil);
+  return const_cast<hb_face_t *> (&Null(hb_face_t));
 }
 
 
@@ -247,7 +249,7 @@
 {
   if (!hb_object_destroy (face)) return;
 
-  for (hb_face_t::plan_node_t *node = face->shape_plans; node; )
+  for (hb_face_t::plan_node_t *node = face->shape_plans.get (); node; )
   {
     hb_face_t::plan_node_t *next = node->next;
     hb_shape_plan_destroy (node->shape_plan);
@@ -301,7 +303,7 @@
  * Since: 0.9.2
  **/
 void *
-hb_face_get_user_data (hb_face_t          *face,
+hb_face_get_user_data (const hb_face_t    *face,
 		       hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (face, key);
@@ -335,7 +337,7 @@
  * Since: 0.9.2
  **/
 hb_bool_t
-hb_face_is_immutable (hb_face_t *face)
+hb_face_is_immutable (const hb_face_t *face)
 {
   return face->immutable;
 }
@@ -353,8 +355,8 @@
  * Since: 0.9.2
  **/
 hb_blob_t *
-hb_face_reference_table (hb_face_t *face,
-			 hb_tag_t   tag)
+hb_face_reference_table (const hb_face_t *face,
+			 hb_tag_t tag)
 {
   return face->reference_table (tag);
 }
@@ -405,7 +407,7 @@
  * Since: 0.9.2
  **/
 unsigned int
-hb_face_get_index (hb_face_t    *face)
+hb_face_get_index (const hb_face_t *face)
 {
   return face->index;
 }
@@ -440,20 +442,11 @@
  * Since: 0.9.2
  **/
 unsigned int
-hb_face_get_upem (hb_face_t *face)
+hb_face_get_upem (const hb_face_t *face)
 {
   return face->get_upem ();
 }
 
-void
-hb_face_t::load_upem (void) const
-{
-  hb_blob_t *head_blob = OT::Sanitizer<OT::head>().sanitize (reference_table (HB_OT_TAG_head));
-  const OT::head *head_table = head_blob->as<OT::head> ();
-  upem = head_table->get_upem ();
-  hb_blob_destroy (head_blob);
-}
-
 /**
  * hb_face_set_glyph_count:
  * @face: a face.
@@ -484,20 +477,11 @@
  * Since: 0.9.7
  **/
 unsigned int
-hb_face_get_glyph_count (hb_face_t *face)
+hb_face_get_glyph_count (const hb_face_t *face)
 {
   return face->get_num_glyphs ();
 }
 
-void
-hb_face_t::load_num_glyphs (void) const
-{
-  hb_blob_t *maxp_blob = OT::Sanitizer<OT::maxp>().sanitize (reference_table (HB_OT_TAG_maxp));
-  const OT::maxp *maxp_table = maxp_blob->as<OT::maxp> ();
-  num_glyphs = maxp_table->get_num_glyphs ();
-  hb_blob_destroy (maxp_blob);
-}
-
 /**
  * hb_face_get_table_tags:
  * @face: a face.
@@ -509,7 +493,7 @@
  * Since: 1.6.0
  **/
 unsigned int
-hb_face_get_table_tags (hb_face_t    *face,
+hb_face_get_table_tags (const hb_face_t *face,
 			unsigned int  start_offset,
 			unsigned int *table_count, /* IN/OUT */
 			hb_tag_t     *table_tags /* OUT */)
diff --git a/src/hb-face.h b/src/hb-face.h
index 983ee56..208092e 100644
--- a/src/hb-face.h
+++ b/src/hb-face.h
@@ -76,19 +76,19 @@
 		       hb_bool_t           replace);
 
 HB_EXTERN void *
-hb_face_get_user_data (hb_face_t          *face,
+hb_face_get_user_data (const hb_face_t    *face,
 		       hb_user_data_key_t *key);
 
 HB_EXTERN void
 hb_face_make_immutable (hb_face_t *face);
 
 HB_EXTERN hb_bool_t
-hb_face_is_immutable (hb_face_t *face);
+hb_face_is_immutable (const hb_face_t *face);
 
 
 HB_EXTERN hb_blob_t *
-hb_face_reference_table (hb_face_t *face,
-			 hb_tag_t   tag);
+hb_face_reference_table (const hb_face_t *face,
+			 hb_tag_t tag);
 
 HB_EXTERN hb_blob_t *
 hb_face_reference_blob (hb_face_t *face);
@@ -98,24 +98,24 @@
 		   unsigned int  index);
 
 HB_EXTERN unsigned int
-hb_face_get_index (hb_face_t    *face);
+hb_face_get_index (const hb_face_t *face);
 
 HB_EXTERN void
 hb_face_set_upem (hb_face_t    *face,
 		  unsigned int  upem);
 
 HB_EXTERN unsigned int
-hb_face_get_upem (hb_face_t *face);
+hb_face_get_upem (const hb_face_t *face);
 
 HB_EXTERN void
 hb_face_set_glyph_count (hb_face_t    *face,
 			 unsigned int  glyph_count);
 
 HB_EXTERN unsigned int
-hb_face_get_glyph_count (hb_face_t *face);
+hb_face_get_glyph_count (const hb_face_t *face);
 
 HB_EXTERN unsigned int
-hb_face_get_table_tags (hb_face_t    *face,
+hb_face_get_table_tags (const hb_face_t *face,
 			unsigned int  start_offset,
 			unsigned int *table_count, /* IN/OUT */
 			hb_tag_t     *table_tags /* OUT */);
diff --git a/src/hb-fallback-shape.cc b/src/hb-fallback-shape.cc
index 3f09c3f..eff20f7 100644
--- a/src/hb-fallback-shape.cc
+++ b/src/hb-fallback-shape.cc
@@ -36,16 +36,16 @@
  * shaper face data
  */
 
-struct hb_fallback_shaper_face_data_t {};
+struct hb_fallback_face_data_t {};
 
-hb_fallback_shaper_face_data_t *
+hb_fallback_face_data_t *
 _hb_fallback_shaper_face_data_create (hb_face_t *face HB_UNUSED)
 {
-  return (hb_fallback_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_fallback_face_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_fallback_shaper_face_data_destroy (hb_fallback_shaper_face_data_t *data HB_UNUSED)
+_hb_fallback_shaper_face_data_destroy (hb_fallback_face_data_t *data HB_UNUSED)
 {
 }
 
@@ -54,16 +54,16 @@
  * shaper font data
  */
 
-struct hb_fallback_shaper_font_data_t {};
+struct hb_fallback_font_data_t {};
 
-hb_fallback_shaper_font_data_t *
+hb_fallback_font_data_t *
 _hb_fallback_shaper_font_data_create (hb_font_t *font HB_UNUSED)
 {
-  return (hb_fallback_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_fallback_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_fallback_shaper_font_data_destroy (hb_fallback_shaper_font_data_t *data HB_UNUSED)
+_hb_fallback_shaper_font_data_destroy (hb_fallback_font_data_t *data HB_UNUSED)
 {
 }
 
@@ -72,20 +72,20 @@
  * shaper shape_plan data
  */
 
-struct hb_fallback_shaper_shape_plan_data_t {};
+struct hb_fallback_shape_plan_data_t {};
 
-hb_fallback_shaper_shape_plan_data_t *
+hb_fallback_shape_plan_data_t *
 _hb_fallback_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
 					    const hb_feature_t *user_features HB_UNUSED,
 					    unsigned int        num_user_features HB_UNUSED,
 					    const int          *coords HB_UNUSED,
 					    unsigned int        num_coords HB_UNUSED)
 {
-  return (hb_fallback_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_fallback_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_fallback_shaper_shape_plan_data_destroy (hb_fallback_shaper_shape_plan_data_t *data HB_UNUSED)
+_hb_fallback_shaper_shape_plan_data_destroy (hb_fallback_shape_plan_data_t *data HB_UNUSED)
 {
 }
 
diff --git a/src/hb-font-private.hh b/src/hb-font-private.hh
index 9f657db..d3a4138 100644
--- a/src/hb-font-private.hh
+++ b/src/hb-font-private.hh
@@ -35,7 +35,6 @@
 #include "hb-shaper-private.hh"
 
 
-
 /*
  * hb_font_funcs_t
  */
@@ -47,6 +46,8 @@
   HB_FONT_FUNC_IMPLEMENT (variation_glyph) \
   HB_FONT_FUNC_IMPLEMENT (glyph_h_advance) \
   HB_FONT_FUNC_IMPLEMENT (glyph_v_advance) \
+  HB_FONT_FUNC_IMPLEMENT (glyph_h_advances) \
+  HB_FONT_FUNC_IMPLEMENT (glyph_v_advances) \
   HB_FONT_FUNC_IMPLEMENT (glyph_h_origin) \
   HB_FONT_FUNC_IMPLEMENT (glyph_v_origin) \
   HB_FONT_FUNC_IMPLEMENT (glyph_h_kerning) \
@@ -57,7 +58,8 @@
   HB_FONT_FUNC_IMPLEMENT (glyph_from_name) \
   /* ^--- Add new callbacks here */
 
-struct hb_font_funcs_t {
+struct hb_font_funcs_t
+{
   hb_object_header_t header;
   ASSERT_POD ();
 
@@ -89,14 +91,15 @@
 		]) (void);
   } get;
 };
-
+DECLARE_NULL_INSTANCE (hb_font_funcs_t);
 
 
 /*
  * hb_font_t
  */
 
-struct hb_font_t {
+struct hb_font_t
+{
   hb_object_header_t header;
   ASSERT_POD ();
 
@@ -233,6 +236,32 @@
 					 klass->user_data.glyph_v_advance);
   }
 
+  inline void get_glyph_h_advances (unsigned int count,
+				    hb_codepoint_t *first_glyph,
+				    unsigned int glyph_stride,
+				    hb_position_t *first_advance,
+				    unsigned int advance_stride)
+  {
+    return klass->get.f.glyph_h_advances (this, user_data,
+					  count,
+					  first_glyph, glyph_stride,
+					  first_advance, advance_stride,
+					  klass->user_data.glyph_h_advances);
+  }
+
+  inline void get_glyph_v_advances (unsigned int count,
+				    hb_codepoint_t *first_glyph,
+				    unsigned int glyph_stride,
+				    hb_position_t *first_advance,
+				    unsigned int advance_stride)
+  {
+    return klass->get.f.glyph_v_advances (this, user_data,
+					  count,
+					  first_glyph, glyph_stride,
+					  first_advance, advance_stride,
+					  klass->user_data.glyph_v_advances);
+  }
+
   inline hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph,
 				       hb_position_t *x, hb_position_t *y)
   {
@@ -341,13 +370,23 @@
 					       hb_direction_t direction,
 					       hb_position_t *x, hb_position_t *y)
   {
-    if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) {
+    *x = *y = 0;
+    if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
       *x = get_glyph_h_advance (glyph);
-      *y = 0;
-    } else {
-      *x = 0;
+    else
       *y = get_glyph_v_advance (glyph);
-    }
+  }
+  inline void get_glyph_advances_for_direction (hb_direction_t direction,
+						unsigned count,
+						hb_codepoint_t *first_glyph,
+						unsigned glyph_stride,
+						hb_position_t *first_advance,
+						unsigned advance_stride)
+  {
+    if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
+      get_glyph_h_advances (count, first_glyph, glyph_stride, first_advance, advance_stride);
+    else
+      get_glyph_v_advances (count, first_glyph, glyph_stride, first_advance, advance_stride);
   }
 
   inline void guess_v_origin_minus_h_origin (hb_codepoint_t glyph,
@@ -553,6 +592,7 @@
     return (float) v * scale / face->get_upem ();
   }
 };
+DECLARE_NULL_INSTANCE (hb_font_t);
 
 #define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, font);
diff --git a/src/hb-font.cc b/src/hb-font.cc
index 4d62b9e..857b8f5 100644
--- a/src/hb-font.cc
+++ b/src/hb-font.cc
@@ -29,6 +29,7 @@
 #include "hb-private.hh"
 
 #include "hb-font-private.hh"
+#include "hb-machinery-private.hh"
 
 
 /*
@@ -45,10 +46,10 @@
   return false;
 }
 static hb_bool_t
-hb_font_get_font_h_extents_parent (hb_font_t *font,
-				   void *font_data HB_UNUSED,
-				   hb_font_extents_t *metrics,
-				   void *user_data HB_UNUSED)
+hb_font_get_font_h_extents_default (hb_font_t *font,
+				    void *font_data HB_UNUSED,
+				    hb_font_extents_t *metrics,
+				    void *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_font_h_extents (metrics);
   if (ret) {
@@ -69,10 +70,10 @@
   return false;
 }
 static hb_bool_t
-hb_font_get_font_v_extents_parent (hb_font_t *font,
-				   void *font_data HB_UNUSED,
-				   hb_font_extents_t *metrics,
-				   void *user_data HB_UNUSED)
+hb_font_get_font_v_extents_default (hb_font_t *font,
+				    void *font_data HB_UNUSED,
+				    hb_font_extents_t *metrics,
+				    void *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_font_v_extents (metrics);
   if (ret) {
@@ -94,11 +95,11 @@
   return false;
 }
 static hb_bool_t
-hb_font_get_nominal_glyph_parent (hb_font_t *font,
-				  void *font_data HB_UNUSED,
-				  hb_codepoint_t unicode,
-				  hb_codepoint_t *glyph,
-				  void *user_data HB_UNUSED)
+hb_font_get_nominal_glyph_default (hb_font_t *font,
+				   void *font_data HB_UNUSED,
+				   hb_codepoint_t unicode,
+				   hb_codepoint_t *glyph,
+				   void *user_data HB_UNUSED)
 {
   return font->parent->get_nominal_glyph (unicode, glyph);
 }
@@ -115,12 +116,12 @@
   return false;
 }
 static hb_bool_t
-hb_font_get_variation_glyph_parent (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    hb_codepoint_t unicode,
-				    hb_codepoint_t variation_selector,
-				    hb_codepoint_t *glyph,
-				    void *user_data HB_UNUSED)
+hb_font_get_variation_glyph_default (hb_font_t *font,
+				     void *font_data HB_UNUSED,
+				     hb_codepoint_t unicode,
+				     hb_codepoint_t variation_selector,
+				     hb_codepoint_t *glyph,
+				     void *user_data HB_UNUSED)
 {
   return font->parent->get_variation_glyph (unicode, variation_selector, glyph);
 }
@@ -129,16 +130,16 @@
 static hb_position_t
 hb_font_get_glyph_h_advance_nil (hb_font_t *font,
 				 void *font_data HB_UNUSED,
-				 hb_codepoint_t glyph,
+				 hb_codepoint_t glyph HB_UNUSED,
 				 void *user_data HB_UNUSED)
 {
   return font->x_scale;
 }
 static hb_position_t
-hb_font_get_glyph_h_advance_parent (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    hb_codepoint_t glyph,
-				    void *user_data HB_UNUSED)
+hb_font_get_glyph_h_advance_default (hb_font_t *font,
+				     void *font_data HB_UNUSED,
+				     hb_codepoint_t glyph,
+				     void *user_data HB_UNUSED)
 {
   return font->parent_scale_x_distance (font->parent->get_glyph_h_advance (glyph));
 }
@@ -146,21 +147,85 @@
 static hb_position_t
 hb_font_get_glyph_v_advance_nil (hb_font_t *font,
 				 void *font_data HB_UNUSED,
-				 hb_codepoint_t glyph,
+				 hb_codepoint_t glyph HB_UNUSED,
 				 void *user_data HB_UNUSED)
 {
   /* TODO use font_extents.ascender+descender */
   return font->y_scale;
 }
 static hb_position_t
-hb_font_get_glyph_v_advance_parent (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    hb_codepoint_t glyph,
-				    void *user_data HB_UNUSED)
+hb_font_get_glyph_v_advance_default (hb_font_t *font,
+				     void *font_data HB_UNUSED,
+				     hb_codepoint_t glyph,
+				     void *user_data HB_UNUSED)
 {
   return font->parent_scale_y_distance (font->parent->get_glyph_v_advance (glyph));
 }
 
+#define hb_font_get_glyph_h_advances_nil hb_font_get_glyph_h_advances_default
+static void
+hb_font_get_glyph_h_advances_default (hb_font_t* font,
+				      void* font_data HB_UNUSED,
+				      unsigned int count,
+				      hb_codepoint_t *first_glyph,
+				      unsigned int glyph_stride,
+				      hb_position_t *first_advance,
+				      unsigned int advance_stride,
+				      void *user_data HB_UNUSED)
+{
+  if (font->has_glyph_h_advance_func ())
+  {
+    for (unsigned int i = 0; i < count; i++)
+    {
+      *first_advance = font->get_glyph_h_advance (*first_glyph);
+      first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
+      first_advance = &StructAtOffset<hb_position_t> (first_advance, advance_stride);
+    }
+    return;
+  }
+
+  font->parent->get_glyph_h_advances (count,
+				      first_glyph, glyph_stride,
+				      first_advance, advance_stride);
+  for (unsigned int i = 0; i < count; i++)
+  {
+    *first_advance = font->parent_scale_x_distance (*first_advance);
+    first_advance = &StructAtOffset<hb_position_t> (first_advance, advance_stride);
+  }
+}
+
+#define hb_font_get_glyph_v_advances_nil hb_font_get_glyph_v_advances_default
+static void
+hb_font_get_glyph_v_advances_default (hb_font_t* font,
+				      void* font_data HB_UNUSED,
+				      unsigned int count,
+				      hb_codepoint_t *first_glyph,
+				      unsigned int glyph_stride,
+				      hb_position_t *first_advance,
+				      unsigned int advance_stride,
+				      void *user_data HB_UNUSED)
+{
+  if (font->has_glyph_v_advance_func ())
+  {
+    for (unsigned int i = 0; i < count; i++)
+    {
+      *first_advance = font->get_glyph_v_advance (*first_glyph);
+      first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
+      first_advance = &StructAtOffset<hb_position_t> (first_advance, advance_stride);
+    }
+    return;
+  }
+
+  font->parent->get_glyph_v_advances (count,
+				      first_glyph, glyph_stride,
+				      first_advance, advance_stride);
+  for (unsigned int i = 0; i < count; i++)
+  {
+    *first_advance = font->parent_scale_y_distance (*first_advance);
+    first_advance = &StructAtOffset<hb_position_t> (first_advance, advance_stride);
+  }
+}
+
 static hb_bool_t
 hb_font_get_glyph_h_origin_nil (hb_font_t *font HB_UNUSED,
 				void *font_data HB_UNUSED,
@@ -173,12 +238,12 @@
   return true;
 }
 static hb_bool_t
-hb_font_get_glyph_h_origin_parent (hb_font_t *font,
-				   void *font_data HB_UNUSED,
-				   hb_codepoint_t glyph,
-				   hb_position_t *x,
-				   hb_position_t *y,
-				   void *user_data HB_UNUSED)
+hb_font_get_glyph_h_origin_default (hb_font_t *font,
+				    void *font_data HB_UNUSED,
+				    hb_codepoint_t glyph,
+				    hb_position_t *x,
+				    hb_position_t *y,
+				    void *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_glyph_h_origin (glyph, x, y);
   if (ret)
@@ -198,12 +263,12 @@
   return false;
 }
 static hb_bool_t
-hb_font_get_glyph_v_origin_parent (hb_font_t *font,
-				   void *font_data HB_UNUSED,
-				   hb_codepoint_t glyph,
-				   hb_position_t *x,
-				   hb_position_t *y,
-				   void *user_data HB_UNUSED)
+hb_font_get_glyph_v_origin_default (hb_font_t *font,
+				    void *font_data HB_UNUSED,
+				    hb_codepoint_t glyph,
+				    hb_position_t *x,
+				    hb_position_t *y,
+				    void *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_glyph_v_origin (glyph, x, y);
   if (ret)
@@ -221,11 +286,11 @@
   return 0;
 }
 static hb_position_t
-hb_font_get_glyph_h_kerning_parent (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    hb_codepoint_t left_glyph,
-				    hb_codepoint_t right_glyph,
-				    void *user_data HB_UNUSED)
+hb_font_get_glyph_h_kerning_default (hb_font_t *font,
+				     void *font_data HB_UNUSED,
+				     hb_codepoint_t left_glyph,
+				     hb_codepoint_t right_glyph,
+				     void *user_data HB_UNUSED)
 {
   return font->parent_scale_x_distance (font->parent->get_glyph_h_kerning (left_glyph, right_glyph));
 }
@@ -240,11 +305,11 @@
   return 0;
 }
 static hb_position_t
-hb_font_get_glyph_v_kerning_parent (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    hb_codepoint_t top_glyph,
-				    hb_codepoint_t bottom_glyph,
-				    void *user_data HB_UNUSED)
+hb_font_get_glyph_v_kerning_default (hb_font_t *font,
+				     void *font_data HB_UNUSED,
+				     hb_codepoint_t top_glyph,
+				     hb_codepoint_t bottom_glyph,
+				     void *user_data HB_UNUSED)
 {
   return font->parent_scale_y_distance (font->parent->get_glyph_v_kerning (top_glyph, bottom_glyph));
 }
@@ -260,11 +325,11 @@
   return false;
 }
 static hb_bool_t
-hb_font_get_glyph_extents_parent (hb_font_t *font,
-				  void *font_data HB_UNUSED,
-				  hb_codepoint_t glyph,
-				  hb_glyph_extents_t *extents,
-				  void *user_data HB_UNUSED)
+hb_font_get_glyph_extents_default (hb_font_t *font,
+				   void *font_data HB_UNUSED,
+				   hb_codepoint_t glyph,
+				   hb_glyph_extents_t *extents,
+				   void *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_glyph_extents (glyph, extents);
   if (ret) {
@@ -287,13 +352,13 @@
   return false;
 }
 static hb_bool_t
-hb_font_get_glyph_contour_point_parent (hb_font_t *font,
-					void *font_data HB_UNUSED,
-					hb_codepoint_t glyph,
-					unsigned int point_index,
-					hb_position_t *x,
-					hb_position_t *y,
-					void *user_data HB_UNUSED)
+hb_font_get_glyph_contour_point_default (hb_font_t *font,
+					 void *font_data HB_UNUSED,
+					 hb_codepoint_t glyph,
+					 unsigned int point_index,
+					 hb_position_t *x,
+					 hb_position_t *y,
+					 void *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_glyph_contour_point (glyph, point_index, x, y);
   if (ret)
@@ -312,11 +377,11 @@
   return false;
 }
 static hb_bool_t
-hb_font_get_glyph_name_parent (hb_font_t *font,
-			       void *font_data HB_UNUSED,
-			       hb_codepoint_t glyph,
-			       char *name, unsigned int size,
-			       void *user_data HB_UNUSED)
+hb_font_get_glyph_name_default (hb_font_t *font,
+				void *font_data HB_UNUSED,
+				hb_codepoint_t glyph,
+				char *name, unsigned int size,
+				void *user_data HB_UNUSED)
 {
   return font->parent->get_glyph_name (glyph, name, size);
 }
@@ -332,16 +397,17 @@
   return false;
 }
 static hb_bool_t
-hb_font_get_glyph_from_name_parent (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    const char *name, int len, /* -1 means nul-terminated */
-				    hb_codepoint_t *glyph,
-				    void *user_data HB_UNUSED)
+hb_font_get_glyph_from_name_default (hb_font_t *font,
+				     void *font_data HB_UNUSED,
+				     const char *name, int len, /* -1 means nul-terminated */
+				     hb_codepoint_t *glyph,
+				     void *user_data HB_UNUSED)
 {
   return font->parent->get_glyph_from_name (name, len, glyph);
 }
 
-static const hb_font_funcs_t _hb_font_funcs_nil = {
+DEFINE_NULL_INSTANCE (hb_font_funcs_t) =
+{
   HB_OBJECT_HEADER_STATIC,
 
   true, /* immutable */
@@ -364,7 +430,8 @@
     }
   }
 };
-static const hb_font_funcs_t _hb_font_funcs_parent = {
+
+static const hb_font_funcs_t _hb_font_funcs_default = {
   HB_OBJECT_HEADER_STATIC,
 
   true, /* immutable */
@@ -381,7 +448,7 @@
   },
   {
     {
-#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_parent,
+#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_default,
       HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
     }
@@ -406,7 +473,7 @@
   if (!(ffuncs = hb_object_create<hb_font_funcs_t> ()))
     return hb_font_funcs_get_empty ();
 
-  ffuncs->get = _hb_font_funcs_parent.get;
+  ffuncs->get = _hb_font_funcs_default.get;
 
   return ffuncs;
 }
@@ -423,7 +490,7 @@
 hb_font_funcs_t *
 hb_font_funcs_get_empty (void)
 {
-  return const_cast<hb_font_funcs_t *> (&_hb_font_funcs_parent);
+  return const_cast<hb_font_funcs_t *> (&_hb_font_funcs_default);
 }
 
 /**
@@ -562,9 +629,9 @@
     ffuncs->user_data.name = user_data;                                  \
     ffuncs->destroy.name = destroy;                                      \
   } else {                                                               \
-    ffuncs->get.f.name = hb_font_get_##name##_parent;                    \
-    ffuncs->user_data.name = nullptr;                                       \
-    ffuncs->destroy.name = nullptr;                                         \
+    ffuncs->get.f.name = hb_font_get_##name##_default;                   \
+    ffuncs->user_data.name = nullptr;                                    \
+    ffuncs->destroy.name = nullptr;                                      \
   }                                                                      \
 }
 
@@ -574,9 +641,8 @@
 bool
 hb_font_t::has_func (unsigned int i)
 {
-  if (parent && parent != hb_font_get_empty () && parent->has_func (i))
-    return true;
-  return this->klass->get.array[i] != _hb_font_funcs_parent.get.array[i];
+  return (this->klass->get.array[i] != _hb_font_funcs_default.get.array[i]) ||
+	 (parent && parent != &_hb_Null_hb_font_t && parent->has_func (i));
 }
 
 /* Public getters */
@@ -718,6 +784,43 @@
 }
 
 /**
+ * hb_font_get_glyph_h_advances:
+ * @font: a font.
+ *
+ * 
+ *
+ * Since: 1.8.6
+ **/
+void
+hb_font_get_glyph_h_advances (hb_font_t* font,
+			      unsigned count,
+			      hb_codepoint_t *first_glyph,
+			      unsigned glyph_stride,
+			      hb_position_t *first_advance,
+			      unsigned advance_stride)
+{
+  font->get_glyph_h_advances (count, first_glyph, glyph_stride, first_advance, advance_stride);
+}
+/**
+ * hb_font_get_glyph_v_advances:
+ * @font: a font.
+ *
+ * 
+ *
+ * Since: 1.8.6
+ **/
+void
+hb_font_get_glyph_v_advances (hb_font_t* font,
+			      unsigned count,
+			      hb_codepoint_t *first_glyph,
+			      unsigned glyph_stride,
+			      hb_position_t *first_advance,
+			      unsigned advance_stride)
+{
+  font->get_glyph_v_advances (count, first_glyph, glyph_stride, first_advance, advance_stride);
+}
+
+/**
  * hb_font_get_glyph_h_origin:
  * @font: a font.
  * @glyph: 
@@ -921,6 +1024,26 @@
 {
   return font->get_glyph_advance_for_direction (glyph, direction, x, y);
 }
+/**
+ * hb_font_get_glyph_advances_for_direction:
+ * @font: a font.
+ * @direction: 
+ *
+ * 
+ *
+ * Since: 1.8.6
+ **/
+HB_EXTERN void
+hb_font_get_glyph_advances_for_direction (hb_font_t* font,
+					  hb_direction_t direction,
+					  unsigned count,
+					  hb_codepoint_t *first_glyph,
+					  unsigned glyph_stride,
+					  hb_position_t *first_advance,
+					  unsigned advance_stride)
+{
+  font->get_glyph_advances_for_direction (direction, count, first_glyph, glyph_stride, first_advance, advance_stride);
+}
 
 /**
  * hb_font_get_glyph_origin_for_direction:
@@ -1100,6 +1223,37 @@
  * hb_font_t
  */
 
+DEFINE_NULL_INSTANCE (hb_font_t) =
+{
+  HB_OBJECT_HEADER_STATIC,
+
+  true, /* immutable */
+
+  nullptr, /* parent */
+  const_cast<hb_face_t *> (&_hb_Null_hb_face_t),
+
+  1000, /* x_scale */
+  1000, /* y_scale */
+
+  0, /* x_ppem */
+  0, /* y_ppem */
+  0, /* ptem */
+
+  0, /* num_coords */
+  nullptr, /* coords */
+
+  const_cast<hb_font_funcs_t *> (&_hb_Null_hb_font_funcs_t), /* klass */
+  nullptr, /* user_data */
+  nullptr, /* destroy */
+
+  {
+#define HB_SHAPER_IMPLEMENT(shaper) HB_ATOMIC_PTR_INIT (HB_SHAPER_DATA_INVALID),
+#include "hb-shaper-list.hh"
+#undef HB_SHAPER_IMPLEMENT
+  }
+};
+
+
 /**
  * hb_font_create: (Xconstructor)
  * @face: a face.
@@ -1187,36 +1341,7 @@
 hb_font_t *
 hb_font_get_empty (void)
 {
-  static const hb_font_t _hb_font_nil = {
-    HB_OBJECT_HEADER_STATIC,
-
-    true, /* immutable */
-
-    nullptr, /* parent */
-    const_cast<hb_face_t *> (&_hb_face_nil),
-
-    1000, /* x_scale */
-    1000, /* y_scale */
-
-    0, /* x_ppem */
-    0, /* y_ppem */
-    0, /* ptem */
-
-    0, /* num_coords */
-    nullptr, /* coords */
-
-    const_cast<hb_font_funcs_t *> (&_hb_font_funcs_nil), /* klass */
-    nullptr, /* user_data */
-    nullptr, /* destroy */
-
-    {
-#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
-#include "hb-shaper-list.hh"
-#undef HB_SHAPER_IMPLEMENT
-    }
-  };
-
-  return const_cast<hb_font_t *> (&_hb_font_nil);
+  return const_cast<hb_font_t *> (&Null(hb_font_t));
 }
 
 /**
diff --git a/src/hb-font.h b/src/hb-font.h
index c95b61d..6cd4869 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -132,6 +132,16 @@
 typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_h_advance_func_t;
 typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_v_advance_func_t;
 
+typedef void (*hb_font_get_glyph_advances_func_t) (hb_font_t* font, void* font_data,
+						   unsigned count,
+						   hb_codepoint_t *first_glyph,
+						   unsigned glyph_stride,
+						   hb_position_t *first_advance,
+						   unsigned advance_stride,
+						   void *user_data);
+typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_h_advances_func_t;
+typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_v_advances_func_t;
+
 typedef hb_bool_t (*hb_font_get_glyph_origin_func_t) (hb_font_t *font, void *font_data,
 						      hb_codepoint_t glyph,
 						      hb_position_t *x, hb_position_t *y,
@@ -265,6 +275,38 @@
 					void *user_data, hb_destroy_func_t destroy);
 
 /**
+ * hb_font_funcs_set_glyph_h_advances_func:
+ * @ffuncs: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ * 
+ *
+ * Since: 1.8.6
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_h_advances_func (hb_font_funcs_t *ffuncs,
+					hb_font_get_glyph_h_advances_func_t func,
+					void *user_data, hb_destroy_func_t destroy);
+
+/**
+ * hb_font_funcs_set_glyph_v_advances_func:
+ * @ffuncs: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ * 
+ *
+ * Since: 1.8.6
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_v_advances_func (hb_font_funcs_t *ffuncs,
+					hb_font_get_glyph_v_advances_func_t func,
+					void *user_data, hb_destroy_func_t destroy);
+
+/**
  * hb_font_funcs_set_glyph_h_origin_func:
  * @ffuncs: font functions.
  * @func: (closure user_data) (destroy destroy) (scope notified):
@@ -417,6 +459,21 @@
 hb_font_get_glyph_v_advance (hb_font_t *font,
 			     hb_codepoint_t glyph);
 
+HB_EXTERN void
+hb_font_get_glyph_h_advances (hb_font_t* font,
+			      unsigned count,
+			      hb_codepoint_t *first_glyph,
+			      unsigned glyph_stride,
+			      hb_position_t *first_advance,
+			      unsigned advance_stride);
+HB_EXTERN void
+hb_font_get_glyph_v_advances (hb_font_t* font,
+			      unsigned count,
+			      hb_codepoint_t *first_glyph,
+			      unsigned glyph_stride,
+			      hb_position_t *first_advance,
+			      unsigned advance_stride);
+
 HB_EXTERN hb_bool_t
 hb_font_get_glyph_h_origin (hb_font_t *font,
 			    hb_codepoint_t glyph,
@@ -472,6 +529,14 @@
 					 hb_direction_t direction,
 					 hb_position_t *x, hb_position_t *y);
 HB_EXTERN void
+hb_font_get_glyph_advances_for_direction (hb_font_t* font,
+					  hb_direction_t direction,
+					  unsigned count,
+					  hb_codepoint_t *first_glyph,
+					  unsigned glyph_stride,
+					  hb_position_t *first_advance,
+					  unsigned advance_stride);
+HB_EXTERN void
 hb_font_get_glyph_origin_for_direction (hb_font_t *font,
 					hb_codepoint_t glyph,
 					hb_direction_t direction,
diff --git a/src/hb-ft.cc b/src/hb-ft.cc
index 6670d9a..01341b6 100644
--- a/src/hb-ft.cc
+++ b/src/hb-ft.cc
@@ -32,6 +32,7 @@
 #include "hb-ft.h"
 
 #include "hb-font-private.hh"
+#include "hb-machinery-private.hh"
 
 #include FT_ADVANCES_H
 #include FT_MULTIPLE_MASTERS_H
@@ -416,30 +417,13 @@
   return true;
 }
 
-static hb_font_funcs_t *static_ft_funcs = nullptr;
+static void free_static_ft_funcs (void);
 
-#ifdef HB_USE_ATEXIT
-static
-void free_static_ft_funcs (void)
+static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t<hb_ft_font_funcs_lazy_loader_t>
 {
-retry:
-  hb_font_funcs_t *ft_funcs = (hb_font_funcs_t *) hb_atomic_ptr_get (&static_ft_funcs);
-  if (!hb_atomic_ptr_cmpexch (&static_ft_funcs, ft_funcs, nullptr))
-    goto retry;
-
-  hb_font_funcs_destroy (ft_funcs);
-}
-#endif
-
-static void
-_hb_ft_font_set_funcs (hb_font_t *font, FT_Face ft_face, bool unref)
-{
-retry:
-  hb_font_funcs_t *funcs = (hb_font_funcs_t *) hb_atomic_ptr_get (&static_ft_funcs);
-
-  if (unlikely (!funcs))
+  static inline hb_font_funcs_t *create (void)
   {
-    funcs = hb_font_funcs_create ();
+    hb_font_funcs_t *funcs = hb_font_funcs_create ();
 
     hb_font_funcs_set_font_h_extents_func (funcs, hb_ft_get_font_h_extents, nullptr, nullptr);
     //hb_font_funcs_set_font_v_extents_func (funcs, hb_ft_get_font_v_extents, nullptr, nullptr);
@@ -458,20 +442,35 @@
 
     hb_font_funcs_make_immutable (funcs);
 
-    if (!hb_atomic_ptr_cmpexch (&static_ft_funcs, nullptr, funcs)) {
-      hb_font_funcs_destroy (funcs);
-      goto retry;
-    }
+#ifdef HB_USE_ATEXIT
+    atexit (free_static_ft_funcs);
+#endif
+
+    return funcs;
+  }
+} static_ft_funcs;
 
 #ifdef HB_USE_ATEXIT
-    atexit (free_static_ft_funcs); /* First person registers atexit() callback. */
+static
+void free_static_ft_funcs (void)
+{
+  static_ft_funcs.free_instance ();
+}
 #endif
-  };
 
+static hb_font_funcs_t *
+_hb_ft_get_font_funcs (void)
+{
+  return static_ft_funcs.get_unconst ();
+}
+
+static void
+_hb_ft_font_set_funcs (hb_font_t *font, FT_Face ft_face, bool unref)
+{
   bool symbol = ft_face->charmap && ft_face->charmap->encoding == FT_ENCODING_MS_SYMBOL;
 
   hb_font_set_funcs (font,
-		     funcs,
+		     _hb_ft_get_font_funcs (),
 		     _hb_ft_font_create (ft_face, symbol, unref),
 		     _hb_ft_font_destroy);
 }
@@ -497,7 +496,10 @@
 
   error = FT_Load_Sfnt_Table (ft_face, tag, 0, buffer, &length);
   if (error)
+  {
+    free (buffer);
     return nullptr;
+  }
 
   return hb_blob_create ((const char *) buffer, length,
 			 HB_MEMORY_MODE_WRITABLE,
@@ -681,46 +683,45 @@
 }
 
 
-/* Thread-safe, lock-free, FT_Library */
+static void free_static_ft_library (void);
 
-static FT_Library ft_library;
+static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_t<hb_remove_ptr_t<FT_Library>::value,
+							     hb_ft_library_lazy_loader_t>
+{
+  static inline FT_Library create (void)
+  {
+    FT_Library l;
+    if (FT_Init_FreeType (&l))
+      return nullptr;
+
+#ifdef HB_USE_ATEXIT
+    atexit (free_static_ft_library);
+#endif
+
+    return l;
+  }
+  static inline void destroy (FT_Library l)
+  {
+    FT_Done_FreeType (l);
+  }
+  static inline FT_Library get_null (void)
+  {
+    return nullptr;
+  }
+} static_ft_library;
 
 #ifdef HB_USE_ATEXIT
 static
-void free_ft_library (void)
+void free_static_ft_library (void)
 {
-retry:
-  FT_Library library = (FT_Library) hb_atomic_ptr_get (&ft_library);
-  if (!hb_atomic_ptr_cmpexch (&ft_library, library, nullptr))
-    goto retry;
-
-  FT_Done_FreeType (library);
+  static_ft_library.free_instance ();
 }
 #endif
 
 static FT_Library
 get_ft_library (void)
 {
-retry:
-  FT_Library library = (FT_Library) hb_atomic_ptr_get (&ft_library);
-
-  if (unlikely (!library))
-  {
-    /* Not found; allocate one. */
-    if (FT_Init_FreeType (&library))
-      return nullptr;
-
-    if (!hb_atomic_ptr_cmpexch (&ft_library, nullptr, library)) {
-      FT_Done_FreeType (library);
-      goto retry;
-    }
-
-#ifdef HB_USE_ATEXIT
-    atexit (free_ft_library); /* First person registers atexit() callback. */
-#endif
-  }
-
-  return library;
+  return static_ft_library.get_unconst ();
 }
 
 static void
diff --git a/src/hb-glib.cc b/src/hb-glib.cc
index 246380a..809b22f 100644
--- a/src/hb-glib.cc
+++ b/src/hb-glib.cc
@@ -31,6 +31,7 @@
 #include "hb-glib.h"
 
 #include "hb-unicode-private.hh"
+#include "hb-machinery-private.hh"
 
 
 #if !GLIB_CHECK_VERSION(2,29,14)
@@ -364,30 +365,15 @@
   return utf8_decomposed_len;
 }
 
-static hb_unicode_funcs_t *static_glib_funcs = nullptr;
 
-#ifdef HB_USE_ATEXIT
-static
-void free_static_glib_funcs (void)
+
+static void free_static_glib_funcs (void);
+
+static struct hb_glib_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t<hb_glib_unicode_funcs_lazy_loader_t>
 {
-retry:
-  hb_unicode_funcs_t *glib_funcs = (hb_unicode_funcs_t *) hb_atomic_ptr_get (&static_glib_funcs);
-  if (!hb_atomic_ptr_cmpexch (&static_glib_funcs, glib_funcs, nullptr))
-    goto retry;
-
-  hb_unicode_funcs_destroy (glib_funcs);
-}
-#endif
-
-hb_unicode_funcs_t *
-hb_glib_get_unicode_funcs (void)
-{
-retry:
-  hb_unicode_funcs_t *funcs = (hb_unicode_funcs_t *) hb_atomic_ptr_get (&static_glib_funcs);
-
-  if (unlikely (!funcs))
+  static inline hb_unicode_funcs_t *create (void)
   {
-    funcs = hb_unicode_funcs_create (nullptr);
+    hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr);
 
 #define HB_UNICODE_FUNC_IMPLEMENT(name) \
     hb_unicode_funcs_set_##name##_func (funcs, hb_glib_unicode_##name, nullptr, nullptr);
@@ -396,18 +382,29 @@
 
     hb_unicode_funcs_make_immutable (funcs);
 
-    if (!hb_atomic_ptr_cmpexch (&static_glib_funcs, nullptr, funcs)) {
-      hb_unicode_funcs_destroy (funcs);
-      goto retry;
-    }
+#ifdef HB_USE_ATEXIT
+    atexit (free_static_glib_funcs);
+#endif
+
+    return funcs;
+  }
+} static_glib_funcs;
 
 #ifdef HB_USE_ATEXIT
-    atexit (free_static_glib_funcs); /* First person registers atexit() callback. */
-#endif
-  };
-
-  return hb_unicode_funcs_reference (funcs);
+static
+void free_static_glib_funcs (void)
+{
+  static_glib_funcs.free_instance ();
 }
+#endif
+
+hb_unicode_funcs_t *
+hb_glib_get_unicode_funcs (void)
+{
+  return static_glib_funcs.get_unconst ();
+}
+
+
 
 #if GLIB_CHECK_VERSION(2,31,10)
 
diff --git a/src/hb-gobject-structs.cc b/src/hb-gobject-structs.cc
index a96c358..0c8e557 100644
--- a/src/hb-gobject-structs.cc
+++ b/src/hb-gobject-structs.cc
@@ -71,6 +71,7 @@
 HB_DEFINE_OBJECT_TYPE (font)
 HB_DEFINE_OBJECT_TYPE (font_funcs)
 HB_DEFINE_OBJECT_TYPE (set)
+HB_DEFINE_OBJECT_TYPE (map)
 HB_DEFINE_OBJECT_TYPE (shape_plan)
 HB_DEFINE_OBJECT_TYPE (unicode_funcs)
 HB_DEFINE_VALUE_TYPE (feature)
diff --git a/src/hb-gobject-structs.h b/src/hb-gobject-structs.h
index 302dc95..800beed 100644
--- a/src/hb-gobject-structs.h
+++ b/src/hb-gobject-structs.h
@@ -90,6 +90,10 @@
 #define HB_GOBJECT_TYPE_SET (hb_gobject_set_get_type ())
 
 HB_EXTERN GType
+hb_gobject_map_get_type (void);
+#define HB_GOBJECT_TYPE_MAP (hb_gobject_map_get_type ())
+
+HB_EXTERN GType
 hb_gobject_shape_plan_get_type (void);
 #define HB_GOBJECT_TYPE_SHAPE_PLAN (hb_gobject_shape_plan_get_type ())
 
diff --git a/src/hb-graphite2.cc b/src/hb-graphite2.cc
index c20f6be..1f42a36 100644
--- a/src/hb-graphite2.cc
+++ b/src/hb-graphite2.cc
@@ -42,22 +42,24 @@
  * shaper face data
  */
 
-typedef struct hb_graphite2_tablelist_t {
+typedef struct hb_graphite2_tablelist_t
+{
   struct hb_graphite2_tablelist_t *next;
   hb_blob_t *blob;
   unsigned int tag;
 } hb_graphite2_tablelist_t;
 
-struct hb_graphite2_shaper_face_data_t {
+struct hb_graphite2_face_data_t
+{
   hb_face_t *face;
   gr_face   *grface;
-  hb_graphite2_tablelist_t *tlist;
+  hb_atomic_ptr_t<hb_graphite2_tablelist_t> tlist;
 };
 
 static const void *hb_graphite2_get_table (const void *data, unsigned int tag, size_t *len)
 {
-  hb_graphite2_shaper_face_data_t *face_data = (hb_graphite2_shaper_face_data_t *) data;
-  hb_graphite2_tablelist_t *tlist = face_data->tlist;
+  hb_graphite2_face_data_t *face_data = (hb_graphite2_face_data_t *) data;
+  hb_graphite2_tablelist_t *tlist = face_data->tlist.get ();
 
   hb_blob_t *blob = nullptr;
 
@@ -80,10 +82,10 @@
     p->tag = tag;
 
 retry:
-    hb_graphite2_tablelist_t *tlist = (hb_graphite2_tablelist_t *) hb_atomic_ptr_get (&face_data->tlist);
+    hb_graphite2_tablelist_t *tlist = face_data->tlist.get ();
     p->next = tlist;
 
-    if (!hb_atomic_ptr_cmpexch (&face_data->tlist, tlist, p))
+    if (unlikely (!face_data->tlist.cmpexch (tlist, p)))
       goto retry;
   }
 
@@ -93,7 +95,7 @@
   return d;
 }
 
-hb_graphite2_shaper_face_data_t *
+hb_graphite2_face_data_t *
 _hb_graphite2_shaper_face_data_create (hb_face_t *face)
 {
   hb_blob_t *silf_blob = face->reference_table (HB_GRAPHITE2_TAG_SILF);
@@ -106,7 +108,7 @@
   }
   hb_blob_destroy (silf_blob);
 
-  hb_graphite2_shaper_face_data_t *data = (hb_graphite2_shaper_face_data_t *) calloc (1, sizeof (hb_graphite2_shaper_face_data_t));
+  hb_graphite2_face_data_t *data = (hb_graphite2_face_data_t *) calloc (1, sizeof (hb_graphite2_face_data_t));
   if (unlikely (!data))
     return nullptr;
 
@@ -122,9 +124,9 @@
 }
 
 void
-_hb_graphite2_shaper_face_data_destroy (hb_graphite2_shaper_face_data_t *data)
+_hb_graphite2_shaper_face_data_destroy (hb_graphite2_face_data_t *data)
 {
-  hb_graphite2_tablelist_t *tlist = data->tlist;
+  hb_graphite2_tablelist_t *tlist = data->tlist.get ();
 
   while (tlist)
   {
@@ -154,16 +156,16 @@
  * shaper font data
  */
 
-struct hb_graphite2_shaper_font_data_t {};
+struct hb_graphite2_font_data_t {};
 
-hb_graphite2_shaper_font_data_t *
+hb_graphite2_font_data_t *
 _hb_graphite2_shaper_font_data_create (hb_font_t *font HB_UNUSED)
 {
-  return (hb_graphite2_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_graphite2_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_graphite2_shaper_font_data_destroy (hb_graphite2_shaper_font_data_t *data HB_UNUSED)
+_hb_graphite2_shaper_font_data_destroy (hb_graphite2_font_data_t *data HB_UNUSED)
 {
 }
 
@@ -181,20 +183,20 @@
  * shaper shape_plan data
  */
 
-struct hb_graphite2_shaper_shape_plan_data_t {};
+struct hb_graphite2_shape_plan_data_t {};
 
-hb_graphite2_shaper_shape_plan_data_t *
+hb_graphite2_shape_plan_data_t *
 _hb_graphite2_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
 					     const hb_feature_t *user_features HB_UNUSED,
 					     unsigned int        num_user_features HB_UNUSED,
 					     const int          *coords HB_UNUSED,
 					     unsigned int        num_coords HB_UNUSED)
 {
-  return (hb_graphite2_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_graphite2_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_graphite2_shaper_shape_plan_data_destroy (hb_graphite2_shaper_shape_plan_data_t *data HB_UNUSED)
+_hb_graphite2_shaper_shape_plan_data_destroy (hb_graphite2_shape_plan_data_t *data HB_UNUSED)
 {
 }
 
diff --git a/src/hb-icu.cc b/src/hb-icu.cc
index c52e165..2e3ad36 100644
--- a/src/hb-icu.cc
+++ b/src/hb-icu.cc
@@ -32,6 +32,7 @@
 #include "hb-icu.h"
 
 #include "hb-unicode-private.hh"
+#include "hb-machinery-private.hh"
 
 #include <unicode/uchar.h>
 #include <unicode/unorm2.h>
@@ -164,10 +165,6 @@
   return hb_icu_script_to_script (scriptCode);
 }
 
-#if U_ICU_VERSION_MAJOR_NUM >= 49
-static const UNormalizer2 *normalizer;
-#endif
-
 static hb_bool_t
 hb_icu_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 			hb_codepoint_t      a,
@@ -177,6 +174,7 @@
 {
 #if U_ICU_VERSION_MAJOR_NUM >= 49
   {
+    const UNormalizer2 *normalizer = (const UNormalizer2 *) user_data;
     UChar32 ret = unorm2_composePair (normalizer, a, b);
     if (ret < 0) return false;
     *ab = ret;
@@ -222,6 +220,7 @@
 {
 #if U_ICU_VERSION_MAJOR_NUM >= 49
   {
+    const UNormalizer2 *normalizer = (const UNormalizer2 *) user_data;
     UChar decomposed[4];
     int len;
     UErrorCode icu_err = U_ZERO_ERROR;
@@ -345,55 +344,46 @@
 }
 
 
-static hb_unicode_funcs_t *static_icu_funcs = nullptr;
+static void free_static_icu_funcs (void);
+
+static struct hb_icu_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t<hb_icu_unicode_funcs_lazy_loader_t>
+{
+  static inline hb_unicode_funcs_t *create (void)
+  {
+    void *user_data = nullptr;
+#if U_ICU_VERSION_MAJOR_NUM >= 49
+    UErrorCode icu_err = U_ZERO_ERROR;
+    user_data = (void *) unorm2_getNFCInstance (&icu_err);
+    assert (user_data);
+#endif
+
+    hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr);
+
+#define HB_UNICODE_FUNC_IMPLEMENT(name) \
+    hb_unicode_funcs_set_##name##_func (funcs, hb_icu_unicode_##name, user_data, nullptr);
+      HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_UNICODE_FUNC_IMPLEMENT
+
+    hb_unicode_funcs_make_immutable (funcs);
+
+#ifdef HB_USE_ATEXIT
+    atexit (free_static_icu_funcs);
+#endif
+
+    return funcs;
+  }
+} static_icu_funcs;
 
 #ifdef HB_USE_ATEXIT
 static
 void free_static_icu_funcs (void)
 {
-retry:
-  hb_unicode_funcs_t *icu_funcs = (hb_unicode_funcs_t *) hb_atomic_ptr_get (&static_icu_funcs);
-  if (!hb_atomic_ptr_cmpexch (&static_icu_funcs, icu_funcs, nullptr))
-    goto retry;
-
-  hb_unicode_funcs_destroy (icu_funcs);
+  static_icu_funcs.free_instance ();
 }
 #endif
 
 hb_unicode_funcs_t *
 hb_icu_get_unicode_funcs (void)
 {
-retry:
-  hb_unicode_funcs_t *funcs = (hb_unicode_funcs_t *) hb_atomic_ptr_get (&static_icu_funcs);
-
-  if (unlikely (!funcs))
-  {
-#if U_ICU_VERSION_MAJOR_NUM >= 49
-    if (!hb_atomic_ptr_get (&normalizer)) {
-      UErrorCode icu_err = U_ZERO_ERROR;
-      /* We ignore failure in getNFCInstace(). */
-      (void) hb_atomic_ptr_cmpexch (&normalizer, nullptr, unorm2_getNFCInstance (&icu_err));
-    }
-#endif
-
-    funcs = hb_unicode_funcs_create (nullptr);
-
-#define HB_UNICODE_FUNC_IMPLEMENT(name) \
-    hb_unicode_funcs_set_##name##_func (funcs, hb_icu_unicode_##name, nullptr, nullptr);
-      HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
-#undef HB_UNICODE_FUNC_IMPLEMENT
-
-    hb_unicode_funcs_make_immutable (funcs);
-
-    if (!hb_atomic_ptr_cmpexch (&static_icu_funcs, nullptr, funcs)) {
-      hb_unicode_funcs_destroy (funcs);
-      goto retry;
-    }
-
-#ifdef HB_USE_ATEXIT
-    atexit (free_static_icu_funcs); /* First person registers atexit() callback. */
-#endif
-  };
-
-  return hb_unicode_funcs_reference (funcs);
+  return static_icu_funcs.get_unconst ();
 }
diff --git a/src/hb-iter-private.hh b/src/hb-iter-private.hh
new file mode 100644
index 0000000..314133a
--- /dev/null
+++ b/src/hb-iter-private.hh
@@ -0,0 +1,149 @@
+/*
+ * Copyright © 2018  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
+ */
+
+#ifndef HB_ITER_PRIVATE_HH
+#define HB_ITER_PRIVATE_HH
+
+#include "hb-private.hh"
+
+
+/* Unified iterator object.
+ *
+ * The goal of this template is to make the same iterator interface
+ * available to all types, and make it very easy and compact to use.
+ * Iterator objects are small, light-weight, objects that can be
+ * copied by value.  If the collection / object being iterated on
+ * is writable, then the iterator points to lvalues, otherwise it
+ * returns rvalues.
+ *
+ * The way to declare, initialize, and use iterators, eg.:
+ *
+ *   Iter<const int *> s (src);
+ *   Iter<int *> t (dst);
+ *   for (; s && t; s++, t++)
+ *     *s = *t;
+ */
+
+template <typename T>
+struct Iter;
+
+#if 0
+template <typename T>
+struct Iter
+{
+  explicit inline Iter (const T &c);
+};
+#endif
+
+template <typename T>
+struct Iter<T *>
+{
+  /* Type of items. */
+  typedef T Value;
+
+  /* Constructors. */
+  inline Iter (T *array_, int length_) :
+    array (array_), length (MAX (length_, 0)) {}
+  template <unsigned int length_>
+  explicit inline Iter (T (&array_)[length_]) :
+    array (array_), length (length_) {}
+
+  /* Emptiness. */
+  explicit_operator inline operator bool (void) const { return bool (length); }
+
+  /* Current item. */
+  inline T &operator * (void)
+  {
+    if (unlikely (!length)) return CrapOrNull(T);
+    return *array;
+  }
+  inline T &operator -> (void)
+  {
+    return (operator *);
+  }
+
+  /* Next. */
+  inline Iter<T *> & operator ++ (void)
+  {
+    if (unlikely (!length)) return *this;
+    array++;
+    length--;
+    return *this;
+  }
+  /* Might return void, or a copy of pre-increment iterator. */
+  inline void operator ++ (int)
+  {
+    if (unlikely (!length)) return;
+    array++;
+    length--;
+  }
+
+  /* Some iterators might implement len(). */
+  inline unsigned int len (void) const { return length; }
+
+  /* Some iterators might implement fast-forward.
+   * Only implement it if it's constant-time. */
+  inline void operator += (unsigned int n)
+  {
+    n = MIN (n, length);
+    array += n;
+    length -= n;
+  }
+
+  /* Some iterators might implement random-access.
+   * Only implement it if it's constant-time. */
+  inline Iter<T *> & operator [] (unsigned int i)
+  {
+    if (unlikely (i >= length)) return CrapOrNull(T);
+    return array[i];
+  }
+
+  private:
+  T *array;
+  unsigned int length;
+};
+
+/* XXX Remove
+ * Just to test these compile. */
+static inline void
+m (void)
+{
+  const int src[10] = {};
+  int dst[20];
+
+  Iter<const int *> s (src);
+  Iter<const int *> s2 (src, 5);
+  Iter<int *> t (dst);
+
+  s2 = s;
+
+  for (; s && t; ++s, ++t)
+   {
+    *t = *s;
+   }
+}
+
+#endif /* HB_ITER_PRIVATE_HH */
diff --git a/src/hb-machinery-private.hh b/src/hb-machinery-private.hh
new file mode 100644
index 0000000..99ef485
--- /dev/null
+++ b/src/hb-machinery-private.hh
@@ -0,0 +1,792 @@
+/*
+ * Copyright © 2007,2008,2009,2010  Red Hat, Inc.
+ * Copyright © 2012,2018  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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_MACHINERY_PRIVATE_HH
+#define HB_MACHINERY_PRIVATE_HH
+
+#include "hb-private.hh"
+#include "hb-blob-private.hh"
+
+#include "hb-iter-private.hh"
+
+
+/*
+ * Casts
+ */
+
+/* Cast to struct T, reference to reference */
+template<typename Type, typename TObject>
+static inline const Type& CastR(const TObject &X)
+{ return reinterpret_cast<const Type&> (X); }
+template<typename Type, typename TObject>
+static inline Type& CastR(TObject &X)
+{ return reinterpret_cast<Type&> (X); }
+
+/* Cast to struct T, pointer to pointer */
+template<typename Type, typename TObject>
+static inline const Type* CastP(const TObject *X)
+{ return reinterpret_cast<const Type*> (X); }
+template<typename Type, typename TObject>
+static inline Type* CastP(TObject *X)
+{ return reinterpret_cast<Type*> (X); }
+
+/* StructAtOffset<T>(P,Ofs) returns the struct T& that is placed at memory
+ * location pointed to by P plus Ofs bytes. */
+template<typename Type>
+static inline const Type& StructAtOffset(const void *P, unsigned int offset)
+{ return * reinterpret_cast<const Type*> ((const char *) P + offset); }
+template<typename Type>
+static inline Type& StructAtOffset(void *P, unsigned int offset)
+{ return * reinterpret_cast<Type*> ((char *) P + offset); }
+
+/* StructAfter<T>(X) returns the struct T& that is placed after X.
+ * Works with X of variable size also.  X must implement get_size() */
+template<typename Type, typename TObject>
+static inline const Type& StructAfter(const TObject &X)
+{ return StructAtOffset<Type>(&X, X.get_size()); }
+template<typename Type, typename TObject>
+static inline Type& StructAfter(TObject &X)
+{ return StructAtOffset<Type>(&X, X.get_size()); }
+
+
+/*
+ * Size checking
+ */
+
+/* Check _assertion in a method environment */
+#define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \
+  inline void _instance_assertion_on_line_##_line (void) const \
+  { \
+    static_assert ((_assertion), ""); \
+    ASSERT_INSTANCE_POD (*this); /* Make sure it's POD. */ \
+  }
+# define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion)
+# define DEFINE_INSTANCE_ASSERTION(_assertion) _DEFINE_INSTANCE_ASSERTION0 (__LINE__, _assertion)
+
+/* Check that _code compiles in a method environment */
+#define _DEFINE_COMPILES_ASSERTION1(_line, _code) \
+  inline void _compiles_assertion_on_line_##_line (void) const \
+  { _code; }
+# define _DEFINE_COMPILES_ASSERTION0(_line, _code) _DEFINE_COMPILES_ASSERTION1 (_line, _code)
+# define DEFINE_COMPILES_ASSERTION(_code) _DEFINE_COMPILES_ASSERTION0 (__LINE__, _code)
+
+
+#define DEFINE_SIZE_STATIC(size) \
+  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)); \
+  static const unsigned int static_size = (size); \
+  static const unsigned int min_size = (size); \
+  inline unsigned int get_size (void) const { return (size); }
+
+#define DEFINE_SIZE_UNION(size, _member) \
+  DEFINE_INSTANCE_ASSERTION (0*sizeof(this->u._member.static_size) + sizeof(this->u._member) == (size)); \
+  static const unsigned int min_size = (size)
+
+#define DEFINE_SIZE_MIN(size) \
+  DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)); \
+  static const unsigned int min_size = (size)
+
+#define DEFINE_SIZE_ARRAY(size, array) \
+  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (array[0])); \
+  DEFINE_COMPILES_ASSERTION ((void) array[0].static_size) \
+  static const unsigned int min_size = (size)
+
+#define DEFINE_SIZE_ARRAY2(size, array1, array2) \
+  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (this->array1[0]) + sizeof (this->array2[0])); \
+  DEFINE_COMPILES_ASSERTION ((void) array1[0].static_size; (void) array2[0].static_size) \
+  static const unsigned int min_size = (size)
+
+
+/*
+ * Dispatch
+ */
+
+template <typename Context, typename Return, unsigned int MaxDebugDepth>
+struct hb_dispatch_context_t
+{
+  static const unsigned int max_debug_depth = MaxDebugDepth;
+  typedef Return return_t;
+  template <typename T, typename F>
+  inline bool may_dispatch (const T *obj, const F *format) { return true; }
+  static return_t no_dispatch_return_value (void) { return Context::default_return_value (); }
+};
+
+
+/*
+ * Sanitize
+ */
+
+/* This limits sanitizing time on really broken fonts. */
+#ifndef HB_SANITIZE_MAX_EDITS
+#define HB_SANITIZE_MAX_EDITS 32
+#endif
+#ifndef HB_SANITIZE_MAX_OPS_FACTOR
+#define HB_SANITIZE_MAX_OPS_FACTOR 8
+#endif
+#ifndef HB_SANITIZE_MAX_OPS_MIN
+#define HB_SANITIZE_MAX_OPS_MIN 16384
+#endif
+
+struct hb_sanitize_context_t :
+       hb_dispatch_context_t<hb_sanitize_context_t, bool, HB_DEBUG_SANITIZE>
+{
+  inline hb_sanitize_context_t (void) :
+	debug_depth (0),
+	start (nullptr), end (nullptr),
+	writable (false), edit_count (0), max_ops (0),
+	blob (nullptr),
+	num_glyphs (65536),
+	num_glyphs_set (false) {}
+
+  inline const char *get_name (void) { return "SANITIZE"; }
+  template <typename T, typename F>
+  inline bool may_dispatch (const T *obj, const F *format)
+  { return format->sanitize (this); }
+  template <typename T>
+  inline return_t dispatch (const T &obj) { return obj.sanitize (this); }
+  static return_t default_return_value (void) { return true; }
+  static return_t no_dispatch_return_value (void) { return false; }
+  bool stop_sublookup_iteration (const return_t r) const { return !r; }
+
+  inline void init (hb_blob_t *b)
+  {
+    this->blob = hb_blob_reference (b);
+    this->writable = false;
+  }
+
+  inline void set_num_glyphs (unsigned int num_glyphs_)
+  {
+    num_glyphs = num_glyphs_;
+    num_glyphs_set = true;
+  }
+  inline unsigned int get_num_glyphs (void) { return num_glyphs; }
+
+  inline void start_processing (void)
+  {
+    this->start = this->blob->data;
+    this->end = this->start + this->blob->length;
+    assert (this->start <= this->end); /* Must not overflow. */
+    this->max_ops = MAX ((unsigned int) (this->end - this->start) * HB_SANITIZE_MAX_OPS_FACTOR,
+			 (unsigned) HB_SANITIZE_MAX_OPS_MIN);
+    this->edit_count = 0;
+    this->debug_depth = 0;
+
+    DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1,
+		     "start [%p..%p] (%lu bytes)",
+		     this->start, this->end,
+		     (unsigned long) (this->end - this->start));
+  }
+
+  inline void end_processing (void)
+  {
+    DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1,
+		     "end [%p..%p] %u edit requests",
+		     this->start, this->end, this->edit_count);
+
+    hb_blob_destroy (this->blob);
+    this->blob = nullptr;
+    this->start = this->end = nullptr;
+  }
+
+  inline bool check_range (const void *base, unsigned int len) const
+  {
+    const char *p = (const char *) base;
+    bool ok = this->max_ops-- > 0 &&
+	      this->start <= p &&
+	      p <= this->end &&
+	      (unsigned int) (this->end - p) >= len;
+
+    DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
+       "check_range [%p..%p] (%d bytes) in [%p..%p] -> %s",
+       p, p + len, len,
+       this->start, this->end,
+       ok ? "OK" : "OUT-OF-RANGE");
+
+    return likely (ok);
+  }
+
+  inline bool check_array (const void *base, unsigned int record_size, unsigned int len) const
+  {
+    const char *p = (const char *) base;
+    bool overflows = hb_unsigned_mul_overflows (len, record_size);
+    unsigned int array_size = record_size * len;
+    bool ok = !overflows && this->check_range (base, array_size);
+
+    DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
+       "check_array [%p..%p] (%d*%d=%d bytes) in [%p..%p] -> %s",
+       p, p + (record_size * len), record_size, len, (unsigned int) array_size,
+       this->start, this->end,
+       overflows ? "OVERFLOWS" : ok ? "OK" : "OUT-OF-RANGE");
+
+    return likely (ok);
+  }
+
+  template <typename Type>
+  inline bool check_struct (const Type *obj) const
+  {
+    return likely (this->check_range (obj, obj->min_size));
+  }
+
+  inline bool may_edit (const void *base, unsigned int len)
+  {
+    if (this->edit_count >= HB_SANITIZE_MAX_EDITS)
+      return false;
+
+    const char *p = (const char *) base;
+    this->edit_count++;
+
+    DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
+       "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s",
+       this->edit_count,
+       p, p + len, len,
+       this->start, this->end,
+       this->writable ? "GRANTED" : "DENIED");
+
+    return this->writable;
+  }
+
+  template <typename Type, typename ValueType>
+  inline bool try_set (const Type *obj, const ValueType &v) {
+    if (this->may_edit (obj, obj->static_size)) {
+      const_cast<Type *> (obj)->set (v);
+      return true;
+    }
+    return false;
+  }
+
+  template <typename Type>
+  inline hb_blob_t *sanitize_blob (hb_blob_t *blob)
+  {
+    bool sane;
+
+    init (blob);
+
+  retry:
+    DEBUG_MSG_FUNC (SANITIZE, start, "start");
+
+    start_processing ();
+
+    if (unlikely (!start))
+    {
+      end_processing ();
+      return blob;
+    }
+
+    Type *t = CastP<Type> (const_cast<char *> (start));
+
+    sane = t->sanitize (this);
+    if (sane)
+    {
+      if (edit_count)
+      {
+	DEBUG_MSG_FUNC (SANITIZE, start, "passed first round with %d edits; going for second round", edit_count);
+
+        /* sanitize again to ensure no toe-stepping */
+        edit_count = 0;
+	sane = t->sanitize (this);
+	if (edit_count) {
+	  DEBUG_MSG_FUNC (SANITIZE, start, "requested %d edits in second round; FAILLING", edit_count);
+	  sane = false;
+	}
+      }
+    }
+    else
+    {
+      if (edit_count && !writable) {
+        start = hb_blob_get_data_writable (blob, nullptr);
+	end = start + blob->length;
+
+	if (start)
+	{
+	  writable = true;
+	  /* ok, we made it writable by relocating.  try again */
+	  DEBUG_MSG_FUNC (SANITIZE, start, "retry");
+	  goto retry;
+	}
+      }
+    }
+
+    end_processing ();
+
+    DEBUG_MSG_FUNC (SANITIZE, start, sane ? "PASSED" : "FAILED");
+    if (sane)
+    {
+      hb_blob_make_immutable (blob);
+      return blob;
+    }
+    else
+    {
+      hb_blob_destroy (blob);
+      return hb_blob_get_empty ();
+    }
+  }
+
+  template <typename Type>
+  inline hb_blob_t *reference_table (const hb_face_t *face, hb_tag_t tableTag = Type::tableTag)
+  {
+    if (!num_glyphs_set)
+      set_num_glyphs (hb_face_get_glyph_count (face));
+    return sanitize_blob<Type> (hb_face_reference_table (face, tableTag));
+  }
+
+  mutable unsigned int debug_depth;
+  const char *start, *end;
+  private:
+  bool writable;
+  unsigned int edit_count;
+  mutable int max_ops;
+  hb_blob_t *blob;
+  unsigned int num_glyphs;
+  bool  num_glyphs_set;
+};
+
+
+/*
+ * Serialize
+ */
+
+struct hb_serialize_context_t
+{
+  inline hb_serialize_context_t (void *start_, unsigned int size)
+  {
+    this->start = (char *) start_;
+    this->end = this->start + size;
+
+    this->ran_out_of_room = false;
+    this->head = this->start;
+    this->debug_depth = 0;
+  }
+
+  template <typename Type>
+  inline Type *start_serialize (void)
+  {
+    DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1,
+		     "start [%p..%p] (%lu bytes)",
+		     this->start, this->end,
+		     (unsigned long) (this->end - this->start));
+
+    return start_embed<Type> ();
+  }
+
+  inline void end_serialize (void)
+  {
+    DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1,
+		     "end [%p..%p] serialized %d bytes; %s",
+		     this->start, this->end,
+		     (int) (this->head - this->start),
+		     this->ran_out_of_room ? "RAN OUT OF ROOM" : "did not ran out of room");
+  }
+
+  template <typename Type>
+  inline Type *copy (void)
+  {
+    assert (!this->ran_out_of_room);
+    unsigned int len = this->head - this->start;
+    void *p = malloc (len);
+    if (p)
+      memcpy (p, this->start, len);
+    return reinterpret_cast<Type *> (p);
+  }
+
+  template <typename Type>
+  inline Type *allocate_size (unsigned int size)
+  {
+    if (unlikely (this->ran_out_of_room || this->end - this->head < ptrdiff_t (size))) {
+      this->ran_out_of_room = true;
+      return nullptr;
+    }
+    memset (this->head, 0, size);
+    char *ret = this->head;
+    this->head += size;
+    return reinterpret_cast<Type *> (ret);
+  }
+
+  template <typename Type>
+  inline Type *allocate_min (void)
+  {
+    return this->allocate_size<Type> (Type::min_size);
+  }
+
+  template <typename Type>
+  inline Type *start_embed (void)
+  {
+    Type *ret = reinterpret_cast<Type *> (this->head);
+    return ret;
+  }
+
+  template <typename Type>
+  inline Type *embed (const Type &obj)
+  {
+    unsigned int size = obj.get_size ();
+    Type *ret = this->allocate_size<Type> (size);
+    if (unlikely (!ret)) return nullptr;
+    memcpy (ret, obj, size);
+    return ret;
+  }
+
+  template <typename Type>
+  inline Type *extend_min (Type &obj)
+  {
+    unsigned int size = obj.min_size;
+    assert (this->start <= (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head);
+    if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return nullptr;
+    return reinterpret_cast<Type *> (&obj);
+  }
+
+  template <typename Type>
+  inline Type *extend (Type &obj)
+  {
+    unsigned int size = obj.get_size ();
+    assert (this->start < (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head);
+    if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return nullptr;
+    return reinterpret_cast<Type *> (&obj);
+  }
+
+  unsigned int debug_depth;
+  char *start, *end, *head;
+  bool ran_out_of_room;
+};
+
+
+/*
+ * Supplier
+ */
+
+template <typename Type>
+struct Supplier
+{
+  inline Supplier (const Type *array, unsigned int len_, unsigned int stride_=sizeof(Type))
+  {
+    head = array;
+    len = len_;
+    stride = stride_;
+  }
+  inline const Type operator [] (unsigned int i) const
+  {
+    if (unlikely (i >= len)) return Type ();
+    return * (const Type *) (const void *) ((const char *) head + stride * i);
+  }
+
+  inline Supplier<Type> & operator += (unsigned int count)
+  {
+    if (unlikely (count > len))
+      count = len;
+    len -= count;
+    head = (const Type *) (const void *) ((const char *) head + stride * count);
+    return *this;
+  }
+
+  private:
+  inline Supplier (const Supplier<Type> &); /* Disallow copy */
+  inline Supplier<Type>& operator= (const Supplier<Type> &); /* Disallow copy */
+
+  unsigned int len;
+  unsigned int stride;
+  const Type *head;
+};
+
+
+/*
+ * Big-endian integers.
+ */
+
+template <typename Type, int Bytes> struct BEInt;
+
+template <typename Type>
+struct BEInt<Type, 1>
+{
+  public:
+  inline void set (Type V)
+  {
+    v = V;
+  }
+  inline operator Type (void) const
+  {
+    return v;
+  }
+  private: uint8_t v;
+};
+template <typename Type>
+struct BEInt<Type, 2>
+{
+  public:
+  inline void set (Type V)
+  {
+    v[0] = (V >>  8) & 0xFF;
+    v[1] = (V      ) & 0xFF;
+  }
+  inline operator Type (void) const
+  {
+    return (v[0] <<  8)
+         + (v[1]      );
+  }
+  private: uint8_t v[2];
+};
+template <typename Type>
+struct BEInt<Type, 3>
+{
+  public:
+  inline void set (Type V)
+  {
+    v[0] = (V >> 16) & 0xFF;
+    v[1] = (V >>  8) & 0xFF;
+    v[2] = (V      ) & 0xFF;
+  }
+  inline operator Type (void) const
+  {
+    return (v[0] << 16)
+         + (v[1] <<  8)
+         + (v[2]      );
+  }
+  private: uint8_t v[3];
+};
+template <typename Type>
+struct BEInt<Type, 4>
+{
+  public:
+  inline void set (Type V)
+  {
+    v[0] = (V >> 24) & 0xFF;
+    v[1] = (V >> 16) & 0xFF;
+    v[2] = (V >>  8) & 0xFF;
+    v[3] = (V      ) & 0xFF;
+  }
+  inline operator Type (void) const
+  {
+    return (v[0] << 24)
+         + (v[1] << 16)
+         + (v[2] <<  8)
+         + (v[3]      );
+  }
+  private: uint8_t v[4];
+};
+
+
+/*
+ * Lazy loaders.
+ */
+
+template <typename Data, unsigned int WheresData>
+struct hb_data_wrapper_t
+{
+  static_assert (WheresData > 0, "");
+
+  inline Data * get_data (void) const
+  {
+    return *(((Data **) (void *) this) - WheresData);
+  }
+
+  template <typename Stored, typename Subclass>
+  inline Stored * call_create (void) const
+  {
+    Data *data = this->get_data ();
+    return likely (data) ? Subclass::create (data) : nullptr;
+  }
+};
+template <>
+struct hb_data_wrapper_t<void, 0>
+{
+  template <typename Stored, typename Funcs>
+  inline Stored * call_create (void) const
+  {
+    return Funcs::create ();
+  }
+};
+
+template <typename T1, typename T2> struct hb_non_void_t { typedef T1 value; };
+template <typename T2> struct hb_non_void_t<void, T2> { typedef T2 value; };
+
+template <typename Returned,
+	  typename Subclass = void,
+	  typename Data = void,
+	  unsigned int WheresData = 0,
+	  typename Stored = Returned>
+struct hb_lazy_loader_t : hb_data_wrapper_t<Data, WheresData>
+{
+  typedef typename hb_non_void_t<Subclass,
+				 hb_lazy_loader_t<Returned,Subclass,Data,WheresData,Stored>
+				>::value Funcs;
+
+  inline void init0 (void) {} /* Init, when memory is already set to 0. No-op for us. */
+  inline void init (void) { instance.set_relaxed (nullptr); }
+  inline void fini (void)
+  {
+    do_destroy (instance.get ());
+  }
+  inline void free_instance (void)
+  {
+  retry:
+    Stored *p = instance.get ();
+    if (unlikely (p && !this->instance.cmpexch (p, nullptr)))
+      goto retry;
+    do_destroy (p);
+  }
+
+  inline Stored * do_create (void) const
+  {
+    Stored *p = this->template call_create<Stored, Funcs> ();
+    if (unlikely (!p))
+      p = const_cast<Stored *> (Funcs::get_null ());
+    return p;
+  }
+  static inline void do_destroy (Stored *p)
+  {
+    if (p && p != Funcs::get_null ())
+      Funcs::destroy (p);
+  }
+
+  inline const Returned * operator -> (void) const { return get (); }
+  inline const Returned & operator * (void) const { return *get (); }
+
+  inline Data * get_data (void) const
+  {
+    return *(((Data **) this) - WheresData);
+  }
+
+  inline Stored * get_stored (void) const
+  {
+  retry:
+    Stored *p = this->instance.get ();
+    if (unlikely (!p))
+    {
+      p = do_create ();
+      if (unlikely (!this->instance.cmpexch (nullptr, p)))
+      {
+        do_destroy (p);
+	goto retry;
+      }
+    }
+    return p;
+  }
+
+  inline void set_stored (Stored *instance_)
+  {
+    /* This *must* be called when there are no other threads accessing.
+     * However, to make TSan, etc, happy, we using cmpexch. */
+  retry:
+    Stored *p = this->instance.get ();
+    if (unlikely (!this->instance.cmpexch (p, instance_)))
+      goto retry;
+    do_destroy (p);
+  }
+
+  inline const Returned * get (void) const { return Funcs::convert (get_stored ()); }
+  inline Returned * get_unconst (void) const { return const_cast<Returned *> (Funcs::convert (get_stored ())); }
+
+  /* To be possibly overloaded by subclasses. */
+  static inline Returned* convert (Stored *p) { return p; }
+
+  /* By default null/init/fini the object. */
+  static inline const Stored* get_null (void) { return &Null(Stored); }
+  static inline Stored *create (Data *data)
+  {
+    Stored *p = (Stored *) calloc (1, sizeof (Stored));
+    if (likely (p))
+      p->init (data);
+    return p;
+  }
+  static inline Stored *create (void)
+  {
+    Stored *p = (Stored *) calloc (1, sizeof (Stored));
+    if (likely (p))
+      p->init ();
+    return p;
+  }
+  static inline void destroy (Stored *p)
+  {
+    p->fini ();
+    free (p);
+  }
+
+  private:
+  /* Must only have one pointer. */
+  hb_atomic_ptr_t<Stored *> instance;
+};
+
+/* Specializations. */
+
+template <unsigned int WheresFace, typename T>
+struct hb_face_lazy_loader_t : hb_lazy_loader_t<T,
+						hb_face_lazy_loader_t<WheresFace, T>,
+						hb_face_t, WheresFace> {};
+
+template <typename T, unsigned int WheresFace>
+struct hb_table_lazy_loader_t : hb_lazy_loader_t<T,
+						 hb_table_lazy_loader_t<T, WheresFace>,
+						 hb_face_t, WheresFace,
+						 hb_blob_t>
+{
+  static inline hb_blob_t *create (hb_face_t *face)
+  {
+    return hb_sanitize_context_t ().reference_table<T> (face);
+  }
+  static inline void destroy (hb_blob_t *p)
+  {
+    hb_blob_destroy (p);
+  }
+  static inline const hb_blob_t *get_null (void)
+  {
+      return hb_blob_get_empty ();
+  }
+  static inline const T* convert (const hb_blob_t *blob)
+  {
+    return blob->as<T> ();
+  }
+
+  inline hb_blob_t* get_blob (void) const
+  {
+    return this->get_stored ();
+  }
+};
+
+template <typename Subclass>
+struct hb_font_funcs_lazy_loader_t : hb_lazy_loader_t<hb_font_funcs_t, Subclass>
+{
+  static inline void destroy (hb_font_funcs_t *p)
+  {
+    hb_font_funcs_destroy (p);
+  }
+  static inline const hb_font_funcs_t *get_null (void)
+  {
+      return hb_font_funcs_get_empty ();
+  }
+};
+template <typename Subclass>
+struct hb_unicode_funcs_lazy_loader_t : hb_lazy_loader_t<hb_unicode_funcs_t, Subclass>
+{
+  static inline void destroy (hb_unicode_funcs_t *p)
+  {
+    hb_unicode_funcs_destroy (p);
+  }
+  static inline const hb_unicode_funcs_t *get_null (void)
+  {
+      return hb_unicode_funcs_get_empty ();
+  }
+};
+
+
+#endif /* HB_MACHINERY_PRIVATE_HH */
diff --git a/src/hb-map.cc b/src/hb-map.cc
index e3ddae4..138a856 100644
--- a/src/hb-map.cc
+++ b/src/hb-map.cc
@@ -157,8 +157,6 @@
  *
  *
  *
- * Return value:
- *
  * Since: 1.7.7
  **/
 void
@@ -188,7 +186,7 @@
 /**
  * hb_map_del:
  * @map: a map.
- * @codepoint:
+ * @key:
  *
  *
  *
@@ -204,7 +202,7 @@
 /**
  * hb_map_has:
  * @map: a map.
- * @codepoint:
+ * @key:
  *
  *
  *
diff --git a/src/hb-null.hh b/src/hb-null.hh
new file mode 100644
index 0000000..91efee6
--- /dev/null
+++ b/src/hb-null.hh
@@ -0,0 +1,106 @@
+/*
+ * Copyright © 2018  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
+ */
+
+#ifndef HB_NULL_HH
+#define HB_NULL_HH
+
+#include "hb-private.hh"
+
+
+/*
+ * Static pools
+ */
+
+/* Global nul-content Null pool.  Enlarge as necessary. */
+
+#define HB_NULL_POOL_SIZE 264
+
+extern HB_INTERNAL
+hb_vector_size_impl_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)];
+
+/* Generic nul-content Null objects. */
+template <typename Type>
+static inline Type const & Null (void) {
+  static_assert (sizeof (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
+  return *reinterpret_cast<Type const *> (_hb_NullPool);
+}
+#define Null(Type) Null<Type>()
+
+/* Specializaitons for arbitrary-content Null objects expressed in bytes. */
+#define DECLARE_NULL_NAMESPACE_BYTES(Namespace, Type) \
+} /* Close namespace. */ \
+extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::min_size]; \
+template <> \
+/*static*/ inline const Namespace::Type& Null<Namespace::Type> (void) { \
+  return *reinterpret_cast<const Namespace::Type *> (_hb_Null_##Namespace##_##Type); \
+} \
+namespace Namespace { \
+static_assert (true, "Just so we take semicolon after.")
+#define DEFINE_NULL_NAMESPACE_BYTES(Namespace, Type) \
+const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::min_size]
+
+/* Specializaitons for arbitrary-content Null objects expressed as struct initializer. */
+#define DECLARE_NULL_INSTANCE(Type) \
+extern HB_INTERNAL const Type _hb_Null_##Type; \
+template <> \
+/*static*/ inline const Type& Null<Type> (void) { \
+  return _hb_Null_##Type; \
+} \
+static_assert (true, "Just so we take semicolon after.")
+#define DEFINE_NULL_INSTANCE(Type) \
+const Type _hb_Null_##Type
+
+/* Global writable pool.  Enlarge as necessary. */
+
+/* To be fully correct, CrapPool must be thread_local. However, we do not rely on CrapPool
+ * for correct operation. It only exist to catch and divert program logic bugs instead of
+ * causing bad memory access. So, races there are not actually introducing incorrectness
+ * in the code. Has ~12kb binary size overhead to have it, also clang build fails with it. */
+extern HB_INTERNAL
+/*thread_local*/ hb_vector_size_impl_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)];
+
+/* CRAP pool: Common Region for Access Protection. */
+template <typename Type>
+static inline Type& Crap (void) {
+  static_assert (sizeof (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
+  Type *obj = reinterpret_cast<Type *> (_hb_CrapPool);
+  *obj = Null(Type);
+  return *obj;
+}
+#define Crap(Type) Crap<Type>()
+
+template <typename Type>
+struct CrapOrNull {
+  static inline Type & get (void) { return Crap(Type); }
+};
+template <typename Type>
+struct CrapOrNull<const Type> {
+  static inline Type const & get (void) { return Null(Type); }
+};
+#define CrapOrNull(Type) CrapOrNull<Type>::get ()
+
+
+#endif /* HB_NULL_HH */
diff --git a/src/hb-object-private.hh b/src/hb-object-private.hh
index fcdc925..4955a68 100644
--- a/src/hb-object-private.hh
+++ b/src/hb-object-private.hh
@@ -35,9 +35,107 @@
 #include "hb-private.hh"
 #include "hb-atomic-private.hh"
 #include "hb-mutex-private.hh"
+#include "hb-vector-private.hh"
 
 
-/* reference_count */
+/*
+ * Lockable set
+ */
+
+template <typename item_t, typename lock_t>
+struct hb_lockable_set_t
+{
+  hb_vector_t <item_t, 1> items;
+
+  inline void init (void) { items.init (); }
+
+  template <typename T>
+  inline item_t *replace_or_insert (T v, lock_t &l, bool replace)
+  {
+    l.lock ();
+    item_t *item = items.find (v);
+    if (item) {
+      if (replace) {
+	item_t old = *item;
+	*item = v;
+	l.unlock ();
+	old.fini ();
+      }
+      else {
+        item = nullptr;
+	l.unlock ();
+      }
+    } else {
+      item = items.push (v);
+      l.unlock ();
+    }
+    return item;
+  }
+
+  template <typename T>
+  inline void remove (T v, lock_t &l)
+  {
+    l.lock ();
+    item_t *item = items.find (v);
+    if (item) {
+      item_t old = *item;
+      *item = items[items.len - 1];
+      items.pop ();
+      l.unlock ();
+      old.fini ();
+    } else {
+      l.unlock ();
+    }
+  }
+
+  template <typename T>
+  inline bool find (T v, item_t *i, lock_t &l)
+  {
+    l.lock ();
+    item_t *item = items.find (v);
+    if (item)
+      *i = *item;
+    l.unlock ();
+    return !!item;
+  }
+
+  template <typename T>
+  inline item_t *find_or_insert (T v, lock_t &l)
+  {
+    l.lock ();
+    item_t *item = items.find (v);
+    if (!item) {
+      item = items.push (v);
+    }
+    l.unlock ();
+    return item;
+  }
+
+  inline void fini (lock_t &l)
+  {
+    if (!items.len) {
+      /* No need for locking. */
+      items.fini ();
+      return;
+    }
+    l.lock ();
+    while (items.len) {
+      item_t old = items[items.len - 1];
+	items.pop ();
+	l.unlock ();
+	old.fini ();
+	l.lock ();
+    }
+    items.fini ();
+    l.unlock ();
+  }
+
+};
+
+
+/*
+ * Reference-count.
+ */
 
 #define HB_REFERENCE_COUNT_INERT_VALUE 0
 #define HB_REFERENCE_COUNT_POISON_VALUE -0x0000DEAD
@@ -45,16 +143,16 @@
 
 struct hb_reference_count_t
 {
-  hb_atomic_int_t ref_count;
+  mutable hb_atomic_int_t ref_count;
 
-  inline void init (int v) { ref_count.set_unsafe (v); }
-  inline int get_unsafe (void) const { return ref_count.get_unsafe (); }
-  inline int inc (void) { return ref_count.inc (); }
-  inline int dec (void) { return ref_count.dec (); }
-  inline void fini (void) { ref_count.set_unsafe (HB_REFERENCE_COUNT_POISON_VALUE); }
+  inline void init (int v = 1) { ref_count.set_relaxed (v); }
+  inline int get_relaxed (void) const { return ref_count.get_relaxed (); }
+  inline int inc (void) const { return ref_count.inc (); }
+  inline int dec (void) const { return ref_count.dec (); }
+  inline void fini (void) { ref_count.set_relaxed (HB_REFERENCE_COUNT_POISON_VALUE); }
 
-  inline bool is_inert (void) const { return ref_count.get_unsafe () == HB_REFERENCE_COUNT_INERT_VALUE; }
-  inline bool is_valid (void) const { return ref_count.get_unsafe () > 0; }
+  inline bool is_inert (void) const { return ref_count.get_relaxed () == HB_REFERENCE_COUNT_INERT_VALUE; }
+  inline bool is_valid (void) const { return ref_count.get_relaxed () > 0; }
 };
 
 
@@ -89,21 +187,25 @@
 };
 
 
-/* object_header */
+/*
+ * Object header
+ */
 
 struct hb_object_header_t
 {
   hb_reference_count_t ref_count;
-  hb_user_data_array_t *user_data;
+  hb_atomic_ptr_t<hb_user_data_array_t> user_data;
 
-#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INIT, nullptr}
+#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INIT, HB_ATOMIC_PTR_INIT (nullptr)}
 
   private:
   ASSERT_POD ();
 };
 
 
-/* object */
+/*
+ * Object
+ */
 
 template <typename Type>
 static inline void hb_object_trace (const Type *obj, const char *function)
@@ -111,7 +213,7 @@
   DEBUG_MSG (OBJECT, (void *) obj,
 	     "%s refcount=%d",
 	     function,
-	     obj ? obj->header.ref_count.get_unsafe () : 0);
+	     obj ? obj->header.ref_count.get_relaxed () : 0);
 }
 
 template <typename Type>
@@ -129,8 +231,8 @@
 template <typename Type>
 static inline void hb_object_init (Type *obj)
 {
-  obj->header.ref_count.init (1);
-  obj->header.user_data = nullptr;
+  obj->header.ref_count.init ();
+  obj->header.user_data.init ();
 }
 template <typename Type>
 static inline bool hb_object_is_inert (const Type *obj)
@@ -169,10 +271,11 @@
 static inline void hb_object_fini (Type *obj)
 {
   obj->header.ref_count.fini (); /* Do this before user_data */
-  if (obj->header.user_data)
+  hb_user_data_array_t *user_data = obj->header.user_data.get ();
+  if (user_data)
   {
-    obj->header.user_data->fini ();
-    free (obj->header.user_data);
+    user_data->fini ();
+    free (user_data);
   }
 }
 template <typename Type>
@@ -187,14 +290,14 @@
   assert (hb_object_is_valid (obj));
 
 retry:
-  hb_user_data_array_t *user_data = (hb_user_data_array_t *) hb_atomic_ptr_get (&obj->header.user_data);
+  hb_user_data_array_t *user_data = obj->header.user_data.get ();
   if (unlikely (!user_data))
   {
     user_data = (hb_user_data_array_t *) calloc (sizeof (hb_user_data_array_t), 1);
     if (unlikely (!user_data))
       return false;
     user_data->init ();
-    if (unlikely (!hb_atomic_ptr_cmpexch (&obj->header.user_data, nullptr, user_data)))
+    if (unlikely (!obj->header.user_data.cmpexch (nullptr, user_data)))
     {
       user_data->fini ();
       free (user_data);
@@ -209,10 +312,13 @@
 static inline void *hb_object_get_user_data (Type               *obj,
 					     hb_user_data_key_t *key)
 {
-  if (unlikely (!obj || hb_object_is_inert (obj) || !obj->header.user_data))
+  if (unlikely (!obj || hb_object_is_inert (obj)))
     return nullptr;
   assert (hb_object_is_valid (obj));
-  return obj->header.user_data->get (key);
+  hb_user_data_array_t *user_data = obj->header.user_data.get ();
+  if (!user_data)
+    return nullptr;
+  return user_data->get (key);
 }
 
 
diff --git a/src/hb-open-file-private.hh b/src/hb-open-file-private.hh
index 226c48f..d47bff6 100644
--- a/src/hb-open-file-private.hh
+++ b/src/hb-open-file-private.hh
@@ -484,8 +484,6 @@
 
 struct OpenTypeFontFile
 {
-  static const hb_tag_t tableTag	= HB_TAG ('_','_','_','_'); /* Sanitizer needs this. */
-
   enum {
     CFFTag		= HB_TAG ('O','T','T','O'), /* OpenType with Postscript outlines */
     TrueTypeTag	= HB_TAG ( 0 , 1 , 0 , 0 ), /* OpenType with TrueType outlines */
diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh
index 207f6e0..5580565 100644
--- a/src/hb-open-type-private.hh
+++ b/src/hb-open-type-private.hh
@@ -32,484 +32,12 @@
 #include "hb-private.hh"
 #include "hb-blob-private.hh"
 #include "hb-face-private.hh"
+#include "hb-machinery-private.hh"
 
 
 namespace OT {
 
 
-
-/*
- * Casts
- */
-
-/* Cast to struct T, reference to reference */
-template<typename Type, typename TObject>
-static inline const Type& CastR(const TObject &X)
-{ return reinterpret_cast<const Type&> (X); }
-template<typename Type, typename TObject>
-static inline Type& CastR(TObject &X)
-{ return reinterpret_cast<Type&> (X); }
-
-/* Cast to struct T, pointer to pointer */
-template<typename Type, typename TObject>
-static inline const Type* CastP(const TObject *X)
-{ return reinterpret_cast<const Type*> (X); }
-template<typename Type, typename TObject>
-static inline Type* CastP(TObject *X)
-{ return reinterpret_cast<Type*> (X); }
-
-/* StructAtOffset<T>(P,Ofs) returns the struct T& that is placed at memory
- * location pointed to by P plus Ofs bytes. */
-template<typename Type>
-static inline const Type& StructAtOffset(const void *P, unsigned int offset)
-{ return * reinterpret_cast<const Type*> ((const char *) P + offset); }
-template<typename Type>
-static inline Type& StructAtOffset(void *P, unsigned int offset)
-{ return * reinterpret_cast<Type*> ((char *) P + offset); }
-
-/* StructAfter<T>(X) returns the struct T& that is placed after X.
- * Works with X of variable size also.  X must implement get_size() */
-template<typename Type, typename TObject>
-static inline const Type& StructAfter(const TObject &X)
-{ return StructAtOffset<Type>(&X, X.get_size()); }
-template<typename Type, typename TObject>
-static inline Type& StructAfter(TObject &X)
-{ return StructAtOffset<Type>(&X, X.get_size()); }
-
-
-
-/*
- * Size checking
- */
-
-/* Check _assertion in a method environment */
-#define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \
-  inline void _instance_assertion_on_line_##_line (void) const \
-  { \
-    static_assert ((_assertion), ""); \
-    ASSERT_INSTANCE_POD (*this); /* Make sure it's POD. */ \
-  }
-# define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion)
-# define DEFINE_INSTANCE_ASSERTION(_assertion) _DEFINE_INSTANCE_ASSERTION0 (__LINE__, _assertion)
-
-/* Check that _code compiles in a method environment */
-#define _DEFINE_COMPILES_ASSERTION1(_line, _code) \
-  inline void _compiles_assertion_on_line_##_line (void) const \
-  { _code; }
-# define _DEFINE_COMPILES_ASSERTION0(_line, _code) _DEFINE_COMPILES_ASSERTION1 (_line, _code)
-# define DEFINE_COMPILES_ASSERTION(_code) _DEFINE_COMPILES_ASSERTION0 (__LINE__, _code)
-
-
-#define DEFINE_SIZE_STATIC(size) \
-  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)); \
-  static const unsigned int static_size = (size); \
-  static const unsigned int min_size = (size); \
-  inline unsigned int get_size (void) const { return (size); }
-
-#define DEFINE_SIZE_UNION(size, _member) \
-  DEFINE_INSTANCE_ASSERTION (0*sizeof(this->u._member.static_size) + sizeof(this->u._member) == (size)); \
-  static const unsigned int min_size = (size)
-
-#define DEFINE_SIZE_MIN(size) \
-  DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)); \
-  static const unsigned int min_size = (size)
-
-#define DEFINE_SIZE_ARRAY(size, array) \
-  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (array[0])); \
-  DEFINE_COMPILES_ASSERTION ((void) array[0].static_size) \
-  static const unsigned int min_size = (size)
-
-#define DEFINE_SIZE_ARRAY2(size, array1, array2) \
-  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (this->array1[0]) + sizeof (this->array2[0])); \
-  DEFINE_COMPILES_ASSERTION ((void) array1[0].static_size; (void) array2[0].static_size) \
-  static const unsigned int min_size = (size)
-
-
-
-/*
- * Dispatch
- */
-
-template <typename Context, typename Return, unsigned int MaxDebugDepth>
-struct hb_dispatch_context_t
-{
-  static const unsigned int max_debug_depth = MaxDebugDepth;
-  typedef Return return_t;
-  template <typename T, typename F>
-  inline bool may_dispatch (const T *obj, const F *format) { return true; }
-  static return_t no_dispatch_return_value (void) { return Context::default_return_value (); }
-};
-
-
-/*
- * Sanitize
- */
-
-/* This limits sanitizing time on really broken fonts. */
-#ifndef HB_SANITIZE_MAX_EDITS
-#define HB_SANITIZE_MAX_EDITS 32
-#endif
-#ifndef HB_SANITIZE_MAX_OPS_FACTOR
-#define HB_SANITIZE_MAX_OPS_FACTOR 8
-#endif
-#ifndef HB_SANITIZE_MAX_OPS_MIN
-#define HB_SANITIZE_MAX_OPS_MIN 16384
-#endif
-
-struct hb_sanitize_context_t :
-       hb_dispatch_context_t<hb_sanitize_context_t, bool, HB_DEBUG_SANITIZE>
-{
-  inline hb_sanitize_context_t (void) :
-	debug_depth (0),
-	start (nullptr), end (nullptr),
-	writable (false), edit_count (0), max_ops (0),
-	blob (nullptr),
-	num_glyphs (0) {}
-
-  inline const char *get_name (void) { return "SANITIZE"; }
-  template <typename T, typename F>
-  inline bool may_dispatch (const T *obj, const F *format)
-  { return format->sanitize (this); }
-  template <typename T>
-  inline return_t dispatch (const T &obj) { return obj.sanitize (this); }
-  static return_t default_return_value (void) { return true; }
-  static return_t no_dispatch_return_value (void) { return false; }
-  bool stop_sublookup_iteration (const return_t r) const { return !r; }
-
-  inline void init (hb_blob_t *b)
-  {
-    this->blob = hb_blob_reference (b);
-    this->writable = false;
-  }
-
-  inline void start_processing (void)
-  {
-    this->start = hb_blob_get_data (this->blob, nullptr);
-    this->end = this->start + this->blob->length;
-    assert (this->start <= this->end); /* Must not overflow. */
-    this->max_ops = MAX ((unsigned int) (this->end - this->start) * HB_SANITIZE_MAX_OPS_FACTOR,
-			 (unsigned) HB_SANITIZE_MAX_OPS_MIN);
-    this->edit_count = 0;
-    this->debug_depth = 0;
-
-    DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1,
-		     "start [%p..%p] (%lu bytes)",
-		     this->start, this->end,
-		     (unsigned long) (this->end - this->start));
-  }
-
-  inline void end_processing (void)
-  {
-    DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1,
-		     "end [%p..%p] %u edit requests",
-		     this->start, this->end, this->edit_count);
-
-    hb_blob_destroy (this->blob);
-    this->blob = nullptr;
-    this->start = this->end = nullptr;
-  }
-
-  inline bool check_range (const void *base, unsigned int len) const
-  {
-    const char *p = (const char *) base;
-    bool ok = this->max_ops-- > 0 &&
-	      this->start <= p &&
-	      p <= this->end &&
-	      (unsigned int) (this->end - p) >= len;
-
-    DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
-       "check_range [%p..%p] (%d bytes) in [%p..%p] -> %s",
-       p, p + len, len,
-       this->start, this->end,
-       ok ? "OK" : "OUT-OF-RANGE");
-
-    return likely (ok);
-  }
-
-  inline bool check_array (const void *base, unsigned int record_size, unsigned int len) const
-  {
-    const char *p = (const char *) base;
-    bool overflows = hb_unsigned_mul_overflows (len, record_size);
-    unsigned int array_size = record_size * len;
-    bool ok = !overflows && this->check_range (base, array_size);
-
-    DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
-       "check_array [%p..%p] (%d*%d=%d bytes) in [%p..%p] -> %s",
-       p, p + (record_size * len), record_size, len, (unsigned int) array_size,
-       this->start, this->end,
-       overflows ? "OVERFLOWS" : ok ? "OK" : "OUT-OF-RANGE");
-
-    return likely (ok);
-  }
-
-  template <typename Type>
-  inline bool check_struct (const Type *obj) const
-  {
-    return likely (this->check_range (obj, obj->min_size));
-  }
-
-  inline bool may_edit (const void *base, unsigned int len)
-  {
-    if (this->edit_count >= HB_SANITIZE_MAX_EDITS)
-      return false;
-
-    const char *p = (const char *) base;
-    this->edit_count++;
-
-    DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
-       "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s",
-       this->edit_count,
-       p, p + len, len,
-       this->start, this->end,
-       this->writable ? "GRANTED" : "DENIED");
-
-    return this->writable;
-  }
-
-  template <typename Type, typename ValueType>
-  inline bool try_set (const Type *obj, const ValueType &v) {
-    if (this->may_edit (obj, obj->static_size)) {
-      const_cast<Type *> (obj)->set (v);
-      return true;
-    }
-    return false;
-  }
-
-  mutable unsigned int debug_depth;
-  const char *start, *end;
-  bool writable;
-  unsigned int edit_count;
-  mutable int max_ops;
-  hb_blob_t *blob;
-  unsigned int num_glyphs;
-};
-
-
-
-/* Template to sanitize an object. */
-template <typename Type>
-struct Sanitizer
-{
-  inline Sanitizer (void) {}
-
-  inline hb_blob_t *sanitize (hb_blob_t *blob) {
-    bool sane;
-
-    /* TODO is_sane() stuff */
-
-    c->init (blob);
-
-  retry:
-    DEBUG_MSG_FUNC (SANITIZE, c->start, "start");
-
-    c->start_processing ();
-
-    if (unlikely (!c->start)) {
-      c->end_processing ();
-      return blob;
-    }
-
-    Type *t = CastP<Type> (const_cast<char *> (c->start));
-
-    sane = t->sanitize (c);
-    if (sane) {
-      if (c->edit_count) {
-	DEBUG_MSG_FUNC (SANITIZE, c->start, "passed first round with %d edits; going for second round", c->edit_count);
-
-        /* sanitize again to ensure no toe-stepping */
-        c->edit_count = 0;
-	sane = t->sanitize (c);
-	if (c->edit_count) {
-	  DEBUG_MSG_FUNC (SANITIZE, c->start, "requested %d edits in second round; FAILLING", c->edit_count);
-	  sane = false;
-	}
-      }
-    } else {
-      unsigned int edit_count = c->edit_count;
-      if (edit_count && !c->writable) {
-        c->start = hb_blob_get_data_writable (blob, nullptr);
-	c->end = c->start + blob->length;
-
-	if (c->start) {
-	  c->writable = true;
-	  /* ok, we made it writable by relocating.  try again */
-	  DEBUG_MSG_FUNC (SANITIZE, c->start, "retry");
-	  goto retry;
-	}
-      }
-    }
-
-    c->end_processing ();
-
-    DEBUG_MSG_FUNC (SANITIZE, c->start, sane ? "PASSED" : "FAILED");
-    if (sane)
-    {
-      blob->lock ();
-      return blob;
-    }
-    else
-    {
-      hb_blob_destroy (blob);
-      return hb_blob_get_empty ();
-    }
-  }
-
-  inline void set_num_glyphs (unsigned int num_glyphs) { c->num_glyphs = num_glyphs; }
-
-  private:
-  hb_sanitize_context_t c[1];
-};
-
-
-
-/*
- * Serialize
- */
-
-
-struct hb_serialize_context_t
-{
-  inline hb_serialize_context_t (void *start_, unsigned int size)
-  {
-    this->start = (char *) start_;
-    this->end = this->start + size;
-
-    this->ran_out_of_room = false;
-    this->head = this->start;
-    this->debug_depth = 0;
-  }
-
-  template <typename Type>
-  inline Type *start_serialize (void)
-  {
-    DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1,
-		     "start [%p..%p] (%lu bytes)",
-		     this->start, this->end,
-		     (unsigned long) (this->end - this->start));
-
-    return start_embed<Type> ();
-  }
-
-  inline void end_serialize (void)
-  {
-    DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1,
-		     "end [%p..%p] serialized %d bytes; %s",
-		     this->start, this->end,
-		     (int) (this->head - this->start),
-		     this->ran_out_of_room ? "RAN OUT OF ROOM" : "did not ran out of room");
-
-  }
-
-  template <typename Type>
-  inline Type *copy (void)
-  {
-    assert (!this->ran_out_of_room);
-    unsigned int len = this->head - this->start;
-    void *p = malloc (len);
-    if (p)
-      memcpy (p, this->start, len);
-    return reinterpret_cast<Type *> (p);
-  }
-
-  template <typename Type>
-  inline Type *allocate_size (unsigned int size)
-  {
-    if (unlikely (this->ran_out_of_room || this->end - this->head < ptrdiff_t (size))) {
-      this->ran_out_of_room = true;
-      return nullptr;
-    }
-    memset (this->head, 0, size);
-    char *ret = this->head;
-    this->head += size;
-    return reinterpret_cast<Type *> (ret);
-  }
-
-  template <typename Type>
-  inline Type *allocate_min (void)
-  {
-    return this->allocate_size<Type> (Type::min_size);
-  }
-
-  template <typename Type>
-  inline Type *start_embed (void)
-  {
-    Type *ret = reinterpret_cast<Type *> (this->head);
-    return ret;
-  }
-
-  template <typename Type>
-  inline Type *embed (const Type &obj)
-  {
-    unsigned int size = obj.get_size ();
-    Type *ret = this->allocate_size<Type> (size);
-    if (unlikely (!ret)) return nullptr;
-    memcpy (ret, obj, size);
-    return ret;
-  }
-
-  template <typename Type>
-  inline Type *extend_min (Type &obj)
-  {
-    unsigned int size = obj.min_size;
-    assert (this->start <= (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head);
-    if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return nullptr;
-    return reinterpret_cast<Type *> (&obj);
-  }
-
-  template <typename Type>
-  inline Type *extend (Type &obj)
-  {
-    unsigned int size = obj.get_size ();
-    assert (this->start < (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head);
-    if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return nullptr;
-    return reinterpret_cast<Type *> (&obj);
-  }
-
-  inline void truncate (void *new_head)
-  {
-    assert (this->start < new_head && new_head <= this->head);
-    this->head = (char *) new_head;
-  }
-
-  unsigned int debug_depth;
-  char *start, *end, *head;
-  bool ran_out_of_room;
-};
-
-template <typename Type>
-struct Supplier
-{
-  inline Supplier (const Type *array, unsigned int len_, unsigned int stride_=sizeof(Type))
-  {
-    head = array;
-    len = len_;
-    stride = stride_;
-  }
-  inline const Type operator [] (unsigned int i) const
-  {
-    if (unlikely (i >= len)) return Type ();
-    return * (const Type *) (const void *) ((const char *) head + stride * i);
-  }
-
-  inline Supplier<Type> & operator += (unsigned int count)
-  {
-    if (unlikely (count > len))
-      count = len;
-    len -= count;
-    head = (const Type *) (const void *) ((const char *) head + stride * count);
-    return *this;
-  }
-
-  private:
-  inline Supplier (const Supplier<Type> &); /* Disallow copy */
-  inline Supplier<Type>& operator= (const Supplier<Type> &); /* Disallow copy */
-
-  unsigned int len;
-  unsigned int stride;
-  const Type *head;
-};
-
-
 /*
  *
  * The OpenType Font File: Data Types
@@ -523,78 +51,6 @@
  * Int types
  */
 
-
-template <typename Type, int Bytes> struct BEInt;
-
-template <typename Type>
-struct BEInt<Type, 1>
-{
-  public:
-  inline void set (Type V)
-  {
-    v = V;
-  }
-  inline operator Type (void) const
-  {
-    return v;
-  }
-  private: uint8_t v;
-};
-template <typename Type>
-struct BEInt<Type, 2>
-{
-  public:
-  inline void set (Type V)
-  {
-    v[0] = (V >>  8) & 0xFF;
-    v[1] = (V      ) & 0xFF;
-  }
-  inline operator Type (void) const
-  {
-    return (v[0] <<  8)
-         + (v[1]      );
-  }
-  private: uint8_t v[2];
-};
-template <typename Type>
-struct BEInt<Type, 3>
-{
-  public:
-  inline void set (Type V)
-  {
-    v[0] = (V >> 16) & 0xFF;
-    v[1] = (V >>  8) & 0xFF;
-    v[2] = (V      ) & 0xFF;
-  }
-  inline operator Type (void) const
-  {
-    return (v[0] << 16)
-         + (v[1] <<  8)
-         + (v[2]      );
-  }
-  private: uint8_t v[3];
-};
-template <typename Type>
-struct BEInt<Type, 4>
-{
-  public:
-  inline void set (Type V)
-  {
-    v[0] = (V >> 24) & 0xFF;
-    v[1] = (V >> 16) & 0xFF;
-    v[2] = (V >>  8) & 0xFF;
-    v[3] = (V      ) & 0xFF;
-  }
-  inline operator Type (void) const
-  {
-    return (v[0] << 24)
-         + (v[1] << 16)
-         + (v[2] <<  8)
-         + (v[3]      );
-  }
-  private: uint8_t v[4];
-};
-
 /* Integer types in big-endian order and no alignment requirement */
 template <typename Type, unsigned int Size>
 struct IntType
@@ -684,7 +140,6 @@
   public:
   DEFINE_SIZE_STATIC (4);
 };
-DEFINE_NULL_DATA (OT, Tag, "    ");
 
 /* Glyph index number, same as uint16 (length = 16 bits) */
 typedef HBUINT16 GlyphID;
@@ -696,7 +151,7 @@
 struct Index : HBUINT16 {
   static const unsigned int NOT_FOUND_INDEX = 0xFFFFu;
 };
-DEFINE_NULL_DATA (OT, Index, "\xff\xff");
+DECLARE_NULL_NAMESPACE_BYTES (OT, Index);
 
 /* Offset, Null offset = 0 */
 template <typename Type>
@@ -765,7 +220,6 @@
 };
 
 
-
 /*
  * Template subclasses of Offset that do the dereferencing.
  * Use: (base+offset)
@@ -831,7 +285,6 @@
  * Array Types
  */
 
-
 /* TODO Use it in ArrayOf, HeadlessArrayOf, and other places around the code base?? */
 template <typename Type>
 struct UnsizedArrayOf
@@ -913,7 +366,6 @@
   }
 };
 
-
 /* An array with a number of elements. */
 template <typename Type, typename LenType=HBUINT16>
 struct ArrayOf
@@ -1066,7 +518,6 @@
   }
 };
 
-
 /* An array starting at second element. */
 template <typename Type, typename LenType=HBUINT16>
 struct HeadlessArrayOf
@@ -1131,10 +582,7 @@
   DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ);
 };
 
-
-/*
- * An array with sorted elements.  Supports binary searching.
- */
+/* An array with sorted elements.  Supports binary searching. */
 template <typename Type, typename LenType=HBUINT16>
 struct SortedArrayOf : ArrayOf<Type, LenType>
 {
@@ -1159,10 +607,7 @@
   }
 };
 
-/*
- * Binary-search arrays
- */
-
+/* Binary-search arrays */
 struct BinSearchHeader
 {
   inline operator uint32_t (void) const { return len; }
@@ -1198,101 +643,6 @@
 struct BinSearchArrayOf : SortedArrayOf<Type, BinSearchHeader> {};
 
 
-/* Lazy struct and blob loaders. */
-
-/* Logic is shared between hb_lazy_loader_t and hb_table_lazy_loader_t */
-template <typename T>
-struct hb_lazy_loader_t
-{
-  inline void init (hb_face_t *face_)
-  {
-    face = face_;
-    instance = nullptr;
-  }
-
-  inline void fini (void)
-  {
-    if (instance && instance != &Null(T))
-    {
-      instance->fini();
-      free (instance);
-    }
-  }
-
-  inline const T* get (void) const
-  {
-  retry:
-    T *p = (T *) hb_atomic_ptr_get (&instance);
-    if (unlikely (!p))
-    {
-      p = (T *) calloc (1, sizeof (T));
-      if (unlikely (!p))
-        p = const_cast<T *> (&Null(T));
-      else
-	p->init (face);
-      if (unlikely (!hb_atomic_ptr_cmpexch (const_cast<T **>(&instance), nullptr, p)))
-      {
-	if (p != &Null(T))
-	  p->fini ();
-	goto retry;
-      }
-    }
-    return p;
-  }
-
-  inline const T* operator-> (void) const
-  {
-    return get ();
-  }
-
-  private:
-  hb_face_t *face;
-  T *instance;
-};
-
-/* Logic is shared between hb_lazy_loader_t and hb_table_lazy_loader_t */
-template <typename T>
-struct hb_table_lazy_loader_t
-{
-  inline void init (hb_face_t *face_)
-  {
-    face = face_;
-    blob = nullptr;
-  }
-
-  inline void fini (void)
-  {
-    hb_blob_destroy (blob);
-  }
-
-  inline const T* get (void) const
-  {
-  retry:
-    hb_blob_t *blob_ = (hb_blob_t *) hb_atomic_ptr_get (&blob);
-    if (unlikely (!blob_))
-    {
-      blob_ = OT::Sanitizer<T>().sanitize (face->reference_table (T::tableTag));
-      if (!hb_atomic_ptr_cmpexch (&blob, nullptr, blob_))
-      {
-	hb_blob_destroy (blob_);
-	goto retry;
-      }
-      blob = blob_;
-    }
-    return blob_->as<T> ();
-  }
-
-  inline const T* operator-> (void) const
-  {
-    return get();
-  }
-
-  private:
-  hb_face_t *face;
-  mutable hb_blob_t *blob;
-};
-
-
 } /* namespace OT */
 
 
diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh
index 0ea3720..67a9c7d 100644
--- a/src/hb-ot-cmap-table.hh
+++ b/src/hb-ot-cmap-table.hh
@@ -37,6 +37,9 @@
  */
 #define HB_OT_TAG_cmap HB_TAG('c','m','a','p')
 
+#ifndef HB_MAX_UNICODE_CODEPOINT_VALUE
+#define HB_MAX_UNICODE_CODEPOINT_VALUE 0x10FFFF
+#endif
 
 namespace OT {
 
@@ -437,8 +440,10 @@
   {
     for (unsigned int i = 0; i < this->groups.len; i++) {
       hb_set_add_range (out,
-			this->groups[i].startCharCode,
-			this->groups[i].endCharCode);
+			MIN ((unsigned int) this->groups[i].startCharCode,
+			     (unsigned int) HB_MAX_UNICODE_CODEPOINT_VALUE),
+			MIN ((unsigned int) this->groups[i].endCharCode,
+			     (unsigned int) HB_MAX_UNICODE_CODEPOINT_VALUE));
     }
   }
 
@@ -800,37 +805,37 @@
   {
     hb_serialize_context_t c (dest, dest_sz);
 
-    OT::cmap *cmap = c.start_serialize<OT::cmap> ();
-    if (unlikely (!c.extend_min (*cmap)))
+    cmap *table = c.start_serialize<cmap> ();
+    if (unlikely (!c.extend_min (*table)))
     {
       return false;
     }
 
-    cmap->version.set (0);
+    table->version.set (0);
 
-    if (unlikely (!cmap->encodingRecord.serialize (&c, /* numTables */ 3)))
+    if (unlikely (!table->encodingRecord.serialize (&c, /* numTables */ 3)))
       return false;
 
     // TODO(grieger): Convert the below to a for loop
 
     // Format 4, Plat 0 Encoding Record
-    EncodingRecord &format4_plat0_rec = cmap->encodingRecord[0];
+    EncodingRecord &format4_plat0_rec = table->encodingRecord[0];
     format4_plat0_rec.platformID.set (0); // Unicode
     format4_plat0_rec.encodingID.set (3);
 
     // Format 4, Plat 3 Encoding Record
-    EncodingRecord &format4_plat3_rec = cmap->encodingRecord[1];
+    EncodingRecord &format4_plat3_rec = table->encodingRecord[1];
     format4_plat3_rec.platformID.set (3); // Windows
     format4_plat3_rec.encodingID.set (1); // Unicode BMP
 
     // Format 12 Encoding Record
-    EncodingRecord &format12_rec = cmap->encodingRecord[2];
+    EncodingRecord &format12_rec = table->encodingRecord[2];
     format12_rec.platformID.set (3); // Windows
     format12_rec.encodingID.set (10); // Unicode UCS-4
 
     // Write out format 4 sub table
     {
-      CmapSubtable &subtable = format4_plat0_rec.subtable.serialize (&c, cmap);
+      CmapSubtable &subtable = format4_plat0_rec.subtable.serialize (&c, table);
       format4_plat3_rec.subtable.set (format4_plat0_rec.subtable);
       subtable.u.format.set (4);
 
@@ -841,7 +846,7 @@
 
     // Write out format 12 sub table.
     {
-      CmapSubtable &subtable = format12_rec.subtable.serialize (&c, cmap);
+      CmapSubtable &subtable = format12_rec.subtable.serialize (&c, table);
       subtable.u.format.set (12);
 
       CmapSubtableFormat12 &format12 = subtable.u.format12;
@@ -894,57 +899,57 @@
   {
     inline void init (hb_face_t *face)
     {
-      this->blob = OT::Sanitizer<OT::cmap>().sanitize (face->reference_table (HB_OT_TAG_cmap));
-      const OT::cmap *cmap = this->blob->as<OT::cmap> ();
-      const OT::CmapSubtable *subtable = nullptr;
-      const OT::CmapSubtableFormat14 *subtable_uvs = nullptr;
+      this->blob = hb_sanitize_context_t().reference_table<cmap> (face);
+      const cmap *table = this->blob->as<cmap> ();
+      const CmapSubtable *subtable = nullptr;
+      const CmapSubtableFormat14 *subtable_uvs = nullptr;
 
       bool symbol = false;
       /* 32-bit subtables. */
-      if (!subtable) subtable = cmap->find_subtable (3, 10);
-      if (!subtable) subtable = cmap->find_subtable (0, 6);
-      if (!subtable) subtable = cmap->find_subtable (0, 4);
+      if (!subtable) subtable = table->find_subtable (3, 10);
+      if (!subtable) subtable = table->find_subtable (0, 6);
+      if (!subtable) subtable = table->find_subtable (0, 4);
       /* 16-bit subtables. */
-      if (!subtable) subtable = cmap->find_subtable (3, 1);
-      if (!subtable) subtable = cmap->find_subtable (0, 3);
-      if (!subtable) subtable = cmap->find_subtable (0, 2);
-      if (!subtable) subtable = cmap->find_subtable (0, 1);
-      if (!subtable) subtable = cmap->find_subtable (0, 0);
+      if (!subtable) subtable = table->find_subtable (3, 1);
+      if (!subtable) subtable = table->find_subtable (0, 3);
+      if (!subtable) subtable = table->find_subtable (0, 2);
+      if (!subtable) subtable = table->find_subtable (0, 1);
+      if (!subtable) subtable = table->find_subtable (0, 0);
       if (!subtable)
       {
-	subtable = cmap->find_subtable (3, 0);
+	subtable = table->find_subtable (3, 0);
 	if (subtable) symbol = true;
       }
       /* Meh. */
-      if (!subtable) subtable = &Null(OT::CmapSubtable);
+      if (!subtable) subtable = &Null(CmapSubtable);
 
       /* UVS subtable. */
       if (!subtable_uvs)
       {
-	const OT::CmapSubtable *st = cmap->find_subtable (0, 5);
+	const CmapSubtable *st = table->find_subtable (0, 5);
 	if (st && st->u.format == 14)
 	  subtable_uvs = &st->u.format14;
       }
       /* Meh. */
-      if (!subtable_uvs) subtable_uvs = &Null(OT::CmapSubtableFormat14);
+      if (!subtable_uvs) subtable_uvs = &Null(CmapSubtableFormat14);
 
       this->uvs_table = subtable_uvs;
 
       this->get_glyph_data = subtable;
       if (unlikely (symbol))
       {
-	this->get_glyph_func = get_glyph_from_symbol<OT::CmapSubtable>;
+	this->get_glyph_func = get_glyph_from_symbol<CmapSubtable>;
 	this->get_all_codepoints_func = null_get_all_codepoints_func;
       } else {
 	switch (subtable->u.format) {
 	/* Accelerate format 4 and format 12. */
 	default:
-	  this->get_glyph_func = get_glyph_from<OT::CmapSubtable>;
+	  this->get_glyph_func = get_glyph_from<CmapSubtable>;
 	  this->get_all_codepoints_func = null_get_all_codepoints_func;
 	  break;
 	case 12:
-	  this->get_glyph_func = get_glyph_from<OT::CmapSubtableFormat12>;
-	  this->get_all_codepoints_func = get_all_codepoints_from<OT::CmapSubtableFormat12>;
+	  this->get_glyph_func = get_glyph_from<CmapSubtableFormat12>;
+	  this->get_all_codepoints_func = get_all_codepoints_from<CmapSubtableFormat12>;
 	  break;
 	case  4:
 	  {
@@ -977,9 +982,9 @@
 						  variation_selector,
 						  glyph))
       {
-	case OT::GLYPH_VARIANT_NOT_FOUND:		return false;
-	case OT::GLYPH_VARIANT_FOUND:		return true;
-	case OT::GLYPH_VARIANT_USE_DEFAULT:	break;
+	case GLYPH_VARIANT_NOT_FOUND:	return false;
+	case GLYPH_VARIANT_FOUND:	return true;
+	case GLYPH_VARIANT_USE_DEFAULT:	break;
       }
 
       return get_nominal_glyph (unicode, glyph);
@@ -1046,9 +1051,9 @@
     const void *get_glyph_data;
     hb_cmap_get_all_codepoints_func_t get_all_codepoints_func;
 
-    OT::CmapSubtableFormat4::accelerator_t format4_accel;
+    CmapSubtableFormat4::accelerator_t format4_accel;
 
-    const OT::CmapSubtableFormat14 *uvs_table;
+    const CmapSubtableFormat14 *uvs_table;
     hb_blob_t *blob;
   };
 
diff --git a/src/hb-ot-color-cbdt-table.hh b/src/hb-ot-color-cbdt-table.hh
index d1dd9de..21c2f47 100644
--- a/src/hb-ot-color-cbdt-table.hh
+++ b/src/hb-ot-color-cbdt-table.hh
@@ -394,8 +394,8 @@
     {
       upem = hb_face_get_upem (face);
 
-      cblc_blob = Sanitizer<CBLC>().sanitize (face->reference_table (HB_OT_TAG_CBLC));
-      cbdt_blob = Sanitizer<CBDT>().sanitize (face->reference_table (HB_OT_TAG_CBDT));
+      cblc_blob = hb_sanitize_context_t().reference_table<CBLC> (face);
+      cbdt_blob = hb_sanitize_context_t().reference_table<CBDT> (face);
       cbdt_len = hb_blob_get_length (cbdt_blob);
 
       if (hb_blob_get_length (cblc_blob) == 0) {
diff --git a/src/hb-ot-color-sbix-table.hh b/src/hb-ot-color-sbix-table.hh
index 09a9517..0461afa 100644
--- a/src/hb-ot-color-sbix-table.hh
+++ b/src/hb-ot-color-sbix-table.hh
@@ -68,7 +68,7 @@
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-		  imageOffsetsZ.sanitize_shallow (c, c->num_glyphs + 1));
+		  imageOffsetsZ.sanitize_shallow (c, c->get_num_glyphs () + 1));
   }
 
   protected:
@@ -96,14 +96,9 @@
   {
     inline void init (hb_face_t *face)
     {
-      num_glyphs = hb_face_get_glyph_count (face);
-
-      OT::Sanitizer<OT::sbix> sanitizer;
-      sanitizer.set_num_glyphs (num_glyphs);
-      sbix_blob = sanitizer.sanitize (face->reference_table (HB_OT_TAG_sbix));
+      sbix_blob = hb_sanitize_context_t().reference_table<sbix> (face);
       sbix_len = hb_blob_get_length (sbix_blob);
-      sbix_table = sbix_blob->as<OT::sbix> ();
-
+      sbix_table = sbix_blob->as<sbix> ();
     }
 
     inline void fini (void)
@@ -134,7 +129,6 @@
 
     unsigned int sbix_len;
     unsigned int num_glyphs;
-
   };
 
   protected:
diff --git a/src/hb-ot-color-svg-table.hh b/src/hb-ot-color-svg-table.hh
index ed6cf97..3976694 100644
--- a/src/hb-ot-color-svg-table.hh
+++ b/src/hb-ot-color-svg-table.hh
@@ -96,11 +96,9 @@
   {
     inline void init (hb_face_t *face)
     {
-      OT::Sanitizer<OT::SVG> sanitizer;
-      svg_blob = sanitizer.sanitize (face->reference_table (HB_OT_TAG_SVG));
+      svg_blob = hb_sanitize_context_t().reference_table<SVG> (face);
       svg_len = hb_blob_get_length (svg_blob);
-      svg = svg_blob->as<OT::SVG> ();
-
+      svg = svg_blob->as<SVG> ();
     }
 
     inline void fini (void)
diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc
index 8310230..9da8fc7 100644
--- a/src/hb-ot-font.cc
+++ b/src/hb-ot-font.cc
@@ -29,6 +29,7 @@
 #include "hb-ot.h"
 
 #include "hb-font-private.hh"
+#include "hb-machinery-private.hh"
 
 #include "hb-ot-cmap-table.hh"
 #include "hb-ot-glyf-table.hh"
@@ -41,13 +42,39 @@
 
 struct hb_ot_font_t
 {
+  inline void init (hb_face_t *face)
+  {
+    cmap.init (face);
+    h_metrics.init (face);
+    v_metrics.init (face, h_metrics.ascender - h_metrics.descender); /* TODO Can we do this lazily? */
+
+    this->face = face;
+    glyf.init ();
+    cbdt.init ();
+    post.init ();
+    kern.init ();
+  }
+  inline void fini (void)
+  {
+    cmap.fini ();
+    h_metrics.fini ();
+    v_metrics.fini ();
+
+    glyf.fini ();
+    cbdt.fini ();
+    post.fini ();
+    kern.fini ();
+  }
+
   OT::cmap::accelerator_t cmap;
   OT::hmtx::accelerator_t h_metrics;
   OT::vmtx::accelerator_t v_metrics;
-  OT::hb_lazy_loader_t<OT::glyf::accelerator_t> glyf;
-  OT::hb_lazy_loader_t<OT::CBDT::accelerator_t> cbdt;
-  OT::hb_lazy_loader_t<OT::post::accelerator_t> post;
-  OT::hb_lazy_loader_t<OT::kern::accelerator_t> kern;
+
+  hb_face_t *face; /* MUST be JUST before the lazy loaders. */
+  hb_face_lazy_loader_t<1, OT::glyf::accelerator_t> glyf;
+  hb_face_lazy_loader_t<2, OT::CBDT::accelerator_t> cbdt;
+  hb_face_lazy_loader_t<3, OT::post::accelerator_t> post;
+  hb_face_lazy_loader_t<4, OT::kern::accelerator_t> kern;
 };
 
 
@@ -59,13 +86,7 @@
   if (unlikely (!ot_font))
     return nullptr;
 
-  ot_font->cmap.init (face);
-  ot_font->h_metrics.init (face);
-  ot_font->v_metrics.init (face, ot_font->h_metrics.ascender - ot_font->h_metrics.descender); /* TODO Can we do this lazily? */
-  ot_font->glyf.init (face);
-  ot_font->cbdt.init (face);
-  ot_font->post.init (face);
-  ot_font->kern.init (face);
+  ot_font->init (face);
 
   return ot_font;
 }
@@ -75,13 +96,7 @@
 {
   hb_ot_font_t *ot_font = (hb_ot_font_t *) data;
 
-  ot_font->cmap.fini ();
-  ot_font->h_metrics.fini ();
-  ot_font->v_metrics.fini ();
-  ot_font->glyf.fini ();
-  ot_font->cbdt.fini ();
-  ot_font->post.fini ();
-  ot_font->kern.fini ();
+  ot_font->fini ();
 
   free (ot_font);
 }
@@ -211,30 +226,14 @@
   return ot_font->v_metrics.has_font_extents;
 }
 
-static hb_font_funcs_t *static_ot_funcs = nullptr;
 
-#ifdef HB_USE_ATEXIT
-static
-void free_static_ot_funcs (void)
+static void free_static_ot_funcs (void);
+
+static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t<hb_ot_font_funcs_lazy_loader_t>
 {
-retry:
-  hb_font_funcs_t *ot_funcs = (hb_font_funcs_t *) hb_atomic_ptr_get (&static_ot_funcs);
-  if (!hb_atomic_ptr_cmpexch (&static_ot_funcs, ot_funcs, nullptr))
-    goto retry;
-
-  hb_font_funcs_destroy (ot_funcs);
-}
-#endif
-
-static hb_font_funcs_t *
-_hb_ot_get_font_funcs (void)
-{
-retry:
-  hb_font_funcs_t *funcs = (hb_font_funcs_t *) hb_atomic_ptr_get (&static_ot_funcs);
-
-  if (unlikely (!funcs))
+  static inline hb_font_funcs_t *create (void)
   {
-    funcs = hb_font_funcs_create ();
+    hb_font_funcs_t *funcs = hb_font_funcs_create ();
 
     hb_font_funcs_set_font_h_extents_func (funcs, hb_ot_get_font_h_extents, nullptr, nullptr);
     hb_font_funcs_set_font_v_extents_func (funcs, hb_ot_get_font_v_extents, nullptr, nullptr);
@@ -253,17 +252,26 @@
 
     hb_font_funcs_make_immutable (funcs);
 
-    if (!hb_atomic_ptr_cmpexch (&static_ot_funcs, nullptr, funcs)) {
-      hb_font_funcs_destroy (funcs);
-      goto retry;
-    }
+#ifdef HB_USE_ATEXIT
+    atexit (free_static_ot_funcs);
+#endif
+
+    return funcs;
+  }
+} static_ot_funcs;
 
 #ifdef HB_USE_ATEXIT
-    atexit (free_static_ot_funcs); /* First person registers atexit() callback. */
+static
+void free_static_ot_funcs (void)
+{
+  static_ot_funcs.free_instance ();
+}
 #endif
-  };
 
-  return funcs;
+static hb_font_funcs_t *
+_hb_ot_get_font_funcs (void)
+{
+  return static_ot_funcs.get_unconst ();
 }
 
 
diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh
index 89c867d..bcf89e4 100644
--- a/src/hb-ot-glyf-table.hh
+++ b/src/hb-ot-glyf-table.hh
@@ -103,14 +103,14 @@
   static bool
   _add_head_and_set_loca_version (hb_subset_plan_t *plan, bool use_short_loca)
   {
-    hb_blob_t *head_blob = OT::Sanitizer<OT::head>().sanitize (hb_face_reference_table (plan->source, HB_OT_TAG_head));
+    hb_blob_t *head_blob = hb_sanitize_context_t().reference_table<head> (plan->source);
     hb_blob_t *head_prime_blob = hb_blob_copy_writable_or_fail (head_blob);
     hb_blob_destroy (head_blob);
 
     if (unlikely (!head_prime_blob))
       return false;
 
-    OT::head *head_prime = (OT::head *) hb_blob_get_data_writable (head_prime_blob, nullptr);
+    head *head_prime = (head *) hb_blob_get_data_writable (head_prime_blob, nullptr);
     head_prime->indexToLocFormat.set (use_short_loca ? 0 : 1);
     bool success = plan->add_table (HB_OT_TAG_head, head_prime_blob);
 
@@ -236,20 +236,20 @@
     {
       memset (this, 0, sizeof (accelerator_t));
 
-      hb_blob_t *head_blob = Sanitizer<head>().sanitize (face->reference_table (HB_OT_TAG_head));
+      hb_blob_t *head_blob = hb_sanitize_context_t().reference_table<head> (face);
       const head *head_table = head_blob->as<head> ();
-      if (head_table == &Null(head) || (unsigned int) head_table->indexToLocFormat > 1 || head_table->glyphDataFormat != 0)
+      if (head_table->indexToLocFormat > 1 || head_table->glyphDataFormat != 0)
       {
-	/* head table is not present, or in an unknown format.  Leave num_glyphs=0, that takes care of disabling us. */
+	/* Unknown format.  Leave num_glyphs=0, that takes care of disabling us. */
 	hb_blob_destroy (head_blob);
 	return;
       }
       short_offset = 0 == head_table->indexToLocFormat;
       hb_blob_destroy (head_blob);
 
-      loca_blob = Sanitizer<loca>().sanitize (face->reference_table (HB_OT_TAG_loca));
+      loca_blob = hb_sanitize_context_t().reference_table<loca> (face);
       loca_table = loca_blob->as<loca> ();
-      glyf_blob = Sanitizer<glyf>().sanitize (face->reference_table (HB_OT_TAG_glyf));
+      glyf_blob = hb_sanitize_context_t().reference_table<glyf> (face);
       glyf_table = glyf_blob->as<glyf> ();
 
       num_glyphs = MAX (1u, hb_blob_get_length (loca_blob) / (short_offset ? 2 : 4)) - 1;
@@ -270,7 +270,7 @@
     inline bool get_composite (hb_codepoint_t glyph,
 			       CompositeGlyphHeader::Iterator *composite /* OUT */) const
     {
-      if (this->glyf_table == &Null(glyf) || !num_glyphs)
+      if (unlikely (!num_glyphs))
 	return false;
 
       unsigned int start_offset, end_offset;
diff --git a/src/hb-ot-head-table.hh b/src/hb-ot-head-table.hh
index 965e30a..fded120 100644
--- a/src/hb-ot-head-table.hh
+++ b/src/hb-ot-head-table.hh
@@ -141,8 +141,8 @@
 					 * -1: Only strongly right to left;
 					 * -2: Like -1 but also contains neutrals. */
   public:
-  HBINT16	indexToLocFormat;	/* 0 for short offsets, 1 for long. */
-  HBINT16	glyphDataFormat;	/* 0 for current format. */
+  HBUINT16	indexToLocFormat;	/* 0 for short offsets, 1 for long. */
+  HBUINT16	glyphDataFormat;	/* 0 for current format. */
 
   DEFINE_SIZE_STATIC (54);
 };
diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh
index 2c62664..13fa9d6 100644
--- a/src/hb-ot-hmtx-table.hh
+++ b/src/hb-ot-hmtx-table.hh
@@ -69,7 +69,7 @@
   inline bool subset_update_header (hb_subset_plan_t *plan,
                                     unsigned int num_hmetrics) const
   {
-    hb_blob_t *src_blob = OT::Sanitizer<H> ().sanitize (plan->source->reference_table (H::tableTag));
+    hb_blob_t *src_blob = hb_sanitize_context_t().reference_table<H> (plan->source, H::tableTag);
     hb_blob_t *dest_blob = hb_blob_copy_writable_or_fail(src_blob);
     hb_blob_destroy (src_blob);
 
@@ -195,7 +195,7 @@
       bool got_font_extents = false;
       if (T::os2Tag)
       {
-	hb_blob_t *os2_blob = Sanitizer<os2> ().sanitize (face->reference_table (T::os2Tag));
+	hb_blob_t *os2_blob = hb_sanitize_context_t().reference_table<os2> (face);
 	const os2 *os2_table = os2_blob->as<os2> ();
 #define USE_TYPO_METRICS (1u<<7)
 	if (0 != (os2_table->fsSelection & USE_TYPO_METRICS))
@@ -208,7 +208,7 @@
 	hb_blob_destroy (os2_blob);
       }
 
-      hb_blob_t *_hea_blob = Sanitizer<H> ().sanitize (face->reference_table (H::tableTag));
+      hb_blob_t *_hea_blob = hb_sanitize_context_t().reference_table<H> (face);
       const H *_hea_table = _hea_blob->as<H> ();
       num_advances = _hea_table->numberOfLongMetrics;
       if (!got_font_extents)
@@ -222,7 +222,7 @@
 
       has_font_extents = got_font_extents;
 
-      blob = Sanitizer<hmtxvmtx> ().sanitize (face->reference_table (T::tableTag));
+      blob = hb_sanitize_context_t().reference_table<hmtxvmtx> (face, T::tableTag);
 
       /* Cap num_metrics() and num_advances() based on table length. */
       unsigned int len = hb_blob_get_length (blob);
@@ -240,7 +240,7 @@
       }
       table = blob->as<hmtxvmtx> ();
 
-      var_blob = Sanitizer<HVARVVAR> ().sanitize (face->reference_table (T::variationsTag));
+      var_blob = hb_sanitize_context_t().reference_table<HVARVVAR> (face, T::variationsTag);
       var_table = var_blob->as<HVARVVAR> ();
     }
 
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index b0fdea4..b4d8109 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -362,7 +362,7 @@
   {
     inline void init (hb_face_t *face)
     {
-      blob = Sanitizer<kern>().sanitize (face->reference_table (HB_OT_TAG_kern));
+      blob = hb_sanitize_context_t().reference_table<kern> (face);
       table = blob->as<kern> ();
       table_length = blob->length;
     }
diff --git a/src/hb-ot-layout-base-table.hh b/src/hb-ot-layout-base-table.hh
index 33dce89..96da07f 100644
--- a/src/hb-ot-layout-base-table.hh
+++ b/src/hb-ot-layout-base-table.hh
@@ -33,13 +33,16 @@
 
 namespace OT {
 
-#define NOT_INDEXED   ((unsigned int) -1)
-
 /*
  * BASE -- Baseline
  * https://docs.microsoft.com/en-us/typography/opentype/spec/base
  */
 
+
+/* XXX Review this. */
+#define NOT_INDEXED		((unsigned int) -1)
+
+
 struct BaseCoordFormat1
 {
   inline int get_coord (void) const { return coordinate; }
@@ -52,7 +55,7 @@
 
   protected:
   HBUINT16	format;		/* Format identifier--format = 1 */
-  HBINT16	coordinate;	/* X or Y value, in design units */
+  FWORD		coordinate;	/* X or Y value, in design units */
   public:
   DEFINE_SIZE_STATIC (4);
 };
@@ -73,7 +76,7 @@
 
   protected:
   HBUINT16	format;		/* Format identifier--format = 2 */
-  HBINT16	coordinate;	/* X or Y value, in design units */
+  FWORD		coordinate;	/* X or Y value, in design units */
   GlyphID	referenceGlyph;	/* Glyph ID of control glyph */
   HBUINT16	coordPoint;	/* Index of contour point on the
 				 * reference glyph */
@@ -97,7 +100,7 @@
 
   protected:
   HBUINT16		format;		/* Format identifier--format = 3 */
-  HBINT16		coordinate;	/* X or Y value, in design units */
+  FWORD			coordinate;	/* X or Y value, in design units */
   OffsetTo<Device>	deviceTable;	/* Offset to Device table for X or
 					 * Y value, from beginning of
 					 * BaseCoord table (may be NULL). */
@@ -109,6 +112,7 @@
 {
   inline int get_coord (void) const
   {
+    /* XXX wire up direction and font. */
     switch (u.format) {
     case 1: return u.format1.get_coord ();
     case 2: return u.format2.get_coord ();
@@ -142,14 +146,10 @@
 
 struct FeatMinMaxRecord
 {
-  inline int get_min_value (void) const
-  { return (this+minCoord).get_coord(); }
+  inline int get_min_value (void) const { return (this+minCoord).get_coord(); }
+  inline int get_max_value (void) const { return (this+maxCoord).get_coord(); }
 
-  inline int get_max_value (void) const
-  { return (this+maxCoord).get_coord(); }
-
-  inline const Tag &get_tag () const
-  { return tag; }
+  inline const Tag& get_tag () const { return tag; }
 
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
@@ -181,7 +181,7 @@
     unsigned int count = featMinMaxRecords.len;
     for (unsigned int i = 0; i < count; i++)
     {
-      Tag tag = featMinMaxRecords[i].get_tag();
+      Tag tag = featMinMaxRecords[i].get_tag ();
       int cmp = tag.cmp(featureTableTag);
       if (cmp == 0) return i;
       if (cmp > 0)  return NOT_INDEXED;
@@ -233,13 +233,13 @@
   { return baseLangSysTag; }
 
   inline unsigned int get_feature_tag_index (Tag featureTableTag) const
-  { return (this+minMax).get_feature_tag_index(featureTableTag); }
+  { return (this+minMax).get_feature_tag_index( featureTableTag); }
 
   inline int get_min_value (unsigned int featureTableTagIndex) const
-  { return (this+minMax).get_min_value(featureTableTagIndex); }
+  { return (this+minMax).get_min_value( featureTableTagIndex); }
 
   inline int get_max_value (unsigned int featureTableTagIndex) const
-  { return (this+minMax).get_max_value(featureTableTagIndex); }
+  { return (this+minMax).get_max_value (featureTableTagIndex); }
 
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
@@ -263,34 +263,34 @@
 
   inline int get_base_coord (unsigned int baselineTagIndex) const
   {
-    return (this+baseCoords[baselineTagIndex]).get_coord();
+    return (this+baseCoords[baselineTagIndex]).get_coord ();
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-      defaultIndex <= baseCoordCount &&
-      baseCoords.sanitize (c, this));
+		  baseCoords.sanitize (c, this));
   }
 
   protected:
   Index				defaultIndex;
-  HBUINT16			baseCoordCount;
   OffsetArrayOf<BaseCoord>	baseCoords;
   public:
-  DEFINE_SIZE_ARRAY (6, baseCoords);
-
+  DEFINE_SIZE_ARRAY (4, baseCoords);
 };
 
 struct BaseScript {
 
   inline unsigned int get_lang_tag_index (Tag baseLangSysTag) const
   {
+    /* XXX bsearch */
     Tag tag;
     int cmp;
-    for (unsigned int i = 0; i < baseLangSysCount; i++) {
-      tag = baseLangSysRecords[i].get_tag();
+    unsigned int count = baseLangSysRecords.len;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      tag = baseLangSysRecords[i].get_tag ();
       // taking advantage of alphabetical order
       cmp = tag.cmp(baseLangSysTag);
       if (cmp == 0) return i;
@@ -301,51 +301,50 @@
 
   inline unsigned int get_feature_tag_index (unsigned int baseLangSysIndex, Tag featureTableTag) const
   {
-    if (baseLangSysIndex == NOT_INDEXED) {
+    if (baseLangSysIndex == NOT_INDEXED)
+    {
       if (unlikely(defaultMinMax)) return NOT_INDEXED;
-      return (this+defaultMinMax).get_feature_tag_index(featureTableTag);
+      return (this+defaultMinMax).get_feature_tag_index (featureTableTag);
     }
-    if (unlikely(baseLangSysIndex >= baseLangSysCount)) return NOT_INDEXED;
-    return baseLangSysRecords[baseLangSysIndex].get_feature_tag_index(featureTableTag);
+    return baseLangSysRecords[baseLangSysIndex].get_feature_tag_index (featureTableTag);
   }
 
   inline int get_min_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
     if (baseLangSysIndex == NOT_INDEXED)
-      return (this+defaultMinMax).get_min_value(featureTableTagIndex);
-    return baseLangSysRecords[baseLangSysIndex].get_max_value(featureTableTagIndex);
+      return (this+defaultMinMax).get_min_value (featureTableTagIndex);
+    return baseLangSysRecords[baseLangSysIndex].get_max_value (featureTableTagIndex);
   }
 
   inline int get_max_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
     if (baseLangSysIndex == NOT_INDEXED)
-      return (this+defaultMinMax).get_min_value(featureTableTagIndex);
-    return baseLangSysRecords[baseLangSysIndex].get_max_value(featureTableTagIndex);
+      return (this+defaultMinMax).get_min_value (featureTableTagIndex);
+    return baseLangSysRecords[baseLangSysIndex].get_max_value (featureTableTagIndex);
   }
 
   inline unsigned int get_default_base_tag_index (void) const
-  { return (this+baseValues).get_default_base_tag_index(); }
+  { return (this+baseValues).get_default_base_tag_index (); }
 
   inline int get_base_coord (unsigned int baselineTagIndex) const
-  { return (this+baseValues).get_base_coord(baselineTagIndex); }
+  { return (this+baseValues).get_base_coord (baselineTagIndex); }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-      baseValues.sanitize (c, this) &&
-      defaultMinMax.sanitize (c, this) &&
-      baseLangSysRecords.sanitize (c, this));
+		  baseValues.sanitize (c, this) &&
+		  defaultMinMax.sanitize (c, this) &&
+		  baseLangSysRecords.sanitize (c, this));
   }
 
   protected:
-  OffsetTo<BaseValues>        baseValues;
-  OffsetTo<MinMax>            defaultMinMax;
-  HBUINT16                      baseLangSysCount;
-  ArrayOf<BaseLangSysRecord>  baseLangSysRecords;
+  OffsetTo<BaseValues>		baseValues;
+  OffsetTo<MinMax>		defaultMinMax;
+  ArrayOf<BaseLangSysRecord>	baseLangSysRecords;
 
   public:
-    DEFINE_SIZE_ARRAY (8, baseLangSysRecords);
+    DEFINE_SIZE_ARRAY (6, baseLangSysRecords);
 };
 
 
@@ -355,29 +354,28 @@
   { return baseScriptTag; }
 
   inline unsigned int get_default_base_tag_index(void) const
-  { return (this+baseScript).get_default_base_tag_index(); }
+  { return (this+baseScript).get_default_base_tag_index (); }
 
   inline int get_base_coord(unsigned int baselineTagIndex) const
-  { return (this+baseScript).get_base_coord(baselineTagIndex); }
+  { return (this+baseScript).get_base_coord (baselineTagIndex); }
 
   inline unsigned int get_lang_tag_index (Tag baseLangSysTag) const
-  { return (this+baseScript).get_lang_tag_index(baseLangSysTag); }
+  { return (this+baseScript).get_lang_tag_index (baseLangSysTag); }
 
   inline unsigned int get_feature_tag_index (unsigned int baseLangSysIndex, Tag featureTableTag) const
-  { return (this+baseScript).get_feature_tag_index(baseLangSysIndex, featureTableTag); }
+  { return (this+baseScript).get_feature_tag_index (baseLangSysIndex, featureTableTag); }
 
   inline int get_max_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  { return (this+baseScript).get_max_value(baseLangSysIndex, featureTableTagIndex); }
+  { return (this+baseScript).get_max_value (baseLangSysIndex, featureTableTagIndex); }
 
   inline int get_min_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  { return (this+baseScript).get_min_value(baseLangSysIndex, featureTableTagIndex); }
+  { return (this+baseScript).get_min_value (baseLangSysIndex, featureTableTagIndex); }
 
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-      baseScript != Null(OffsetTo<BaseScript>) &&
-      baseScript.sanitize (c, base));
+		  baseScript.sanitize (c, base));
   }
 
   protected:
@@ -392,7 +390,9 @@
 
   inline unsigned int get_base_script_index (Tag baseScriptTag) const
   {
-    for (unsigned int i = 0; i < baseScriptCount; i++)
+    /* XXX bsearch? */
+    unsigned int count = baseScriptRecords.len;
+    for (unsigned int i = 0; i < count; i++)
       if (baseScriptRecords[i].get_tag() == baseScriptTag)
         return i;
     return NOT_INDEXED;
@@ -400,7 +400,6 @@
 
   inline unsigned int get_default_base_tag_index (unsigned int baseScriptIndex) const
   {
-    if (unlikely(baseScriptIndex >= baseScriptCount)) return NOT_INDEXED;
     return baseScriptRecords[baseScriptIndex].get_default_base_tag_index();
   }
 
@@ -411,13 +410,11 @@
 
   inline unsigned int get_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
   {
-    if (unlikely(baseScriptIndex >= baseScriptCount)) return NOT_INDEXED;
     return baseScriptRecords[baseScriptIndex].get_lang_tag_index(baseLangSysTag);
   }
 
   inline unsigned int get_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
   {
-    if (unlikely(baseScriptIndex >= baseScriptCount)) return NOT_INDEXED;
     return baseScriptRecords[baseScriptIndex].get_feature_tag_index(baseLangSysIndex, featureTableTag);
   }
 
@@ -435,24 +432,23 @@
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-      baseScriptRecords.sanitize (c, this));
+		  baseScriptRecords.sanitize (c, this));
   }
 
   protected:
-  HBUINT16                    baseScriptCount;
-  ArrayOf<BaseScriptRecord> baseScriptRecords;
+  ArrayOf<BaseScriptRecord>	baseScriptRecords;
 
   public:
-  DEFINE_SIZE_ARRAY (4, baseScriptRecords);
-
+  DEFINE_SIZE_ARRAY (2, baseScriptRecords);
 };
 
 struct BaseTagList
 {
-
-  inline unsigned int get_tag_index(Tag baselineTag) const
+  inline unsigned int get_tag_index (Tag baselineTag) const
   {
-    for (unsigned int i = 0; i < baseTagCount; i++)
+    /* TODO bsearch? */
+    unsigned int count = baselineTags.len;
+    for (unsigned int i = 0; i < count; i++)
       if (baselineTags[i] == baselineTag)
         return i;
     return NOT_INDEXED;
@@ -465,42 +461,37 @@
   }
 
   protected:
-  HBUINT16        baseTagCount;
   SortedArrayOf<Tag>  baselineTags;
 
   public:
-  DEFINE_SIZE_ARRAY (4, baselineTags);
+  DEFINE_SIZE_ARRAY (2, baselineTags);
 };
 
 struct Axis
 {
 
-  inline unsigned int get_base_tag_index(Tag baselineTag) const
+  inline unsigned int get_base_tag_index (Tag baselineTag) const
   {
-    if (unlikely(baseTagList == Null(OffsetTo<BaseTagList>))) return NOT_INDEXED;
     return (this+baseTagList).get_tag_index(baselineTag);
   }
 
   inline unsigned int get_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const
   {
-    if (unlikely(baseScriptList == Null(OffsetTo<BaseScriptList>))) return NOT_INDEXED;
     return (this+baseScriptList).get_default_base_tag_index(baseScriptIndex);
   }
 
-  inline int get_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
+  inline int get_base_coord (unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
   {
     return (this+baseScriptList).get_base_coord(baseScriptIndex, baselineTagIndex);
   }
 
   inline unsigned int get_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
   {
-    if (unlikely(baseScriptList == Null(OffsetTo<BaseScriptList>))) return NOT_INDEXED;
     return (this+baseScriptList).get_lang_tag_index(baseScriptIndex, baseLangSysTag);
   }
 
   inline unsigned int get_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
   {
-    if (unlikely(baseScriptList == Null(OffsetTo<BaseScriptList>))) return NOT_INDEXED;
     return (this+baseScriptList).get_feature_tag_index(baseScriptIndex, baseLangSysIndex, featureTableTag);
   }
 
@@ -518,13 +509,13 @@
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-      baseTagList.sanitize (c, this) &&
-      baseScriptList.sanitize (c, this));
+		  baseTagList.sanitize (c, this) &&
+		  baseScriptList.sanitize (c, this));
   }
 
   protected:
-  OffsetTo<BaseTagList>     baseTagList;
-  OffsetTo<BaseScriptList>  baseScriptList;
+  OffsetTo<BaseTagList>		baseTagList;
+  OffsetTo<BaseScriptList>	baseScriptList;
 
   public:
   DEFINE_SIZE_STATIC (4);
@@ -534,96 +525,78 @@
 {
   static const hb_tag_t tableTag = HB_OT_TAG_BASE;
 
-  inline bool has_vert_axis(void)
-  { return vertAxis != Null(OffsetTo<Axis>); }
+  inline bool has_v_axis(void) { return vAxis != 0; }
 
-  inline bool has_horiz_axis(void)
-  { return horizAxis != Null(OffsetTo<Axis>); }
+  inline bool has_h_axis(void) { return hAxis != 0; }
 
-  // horizontal axis base coords:
-
-  inline unsigned int get_horiz_base_tag_index(Tag baselineTag) const
+  inline unsigned int get_h_base_tag_index (Tag baselineTag) const
   {
-    if (unlikely(horizAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+horizAxis).get_base_tag_index(baselineTag);
+    return (this+hAxis).get_base_tag_index(baselineTag);
   }
 
-  inline unsigned int get_horiz_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const
+  inline unsigned int get_h_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const
   {
-    if (unlikely(horizAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+horizAxis).get_default_base_tag_index_for_script_index(baseScriptIndex);
+    return (this+hAxis).get_default_base_tag_index_for_script_index(baseScriptIndex);
   }
 
-  inline int get_horiz_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
+  inline int get_h_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
   {
-    return (this+horizAxis).get_base_coord(baseScriptIndex, baselineTagIndex);
+    return (this+hAxis).get_base_coord(baseScriptIndex, baselineTagIndex);
   }
 
-  // vertical axis base coords:
-
-  inline unsigned int get_vert_base_tag_index(Tag baselineTag) const
+  inline unsigned int get_v_base_tag_index(Tag baselineTag) const
   {
-    if (unlikely(vertAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+vertAxis).get_base_tag_index(baselineTag);
+    return (this+vAxis).get_base_tag_index(baselineTag);
   }
 
-  inline unsigned int get_vert_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const
+  inline unsigned int get_v_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const
   {
-    if (unlikely(vertAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+vertAxis).get_default_base_tag_index_for_script_index(baseScriptIndex);
+    return (this+vAxis).get_default_base_tag_index_for_script_index(baseScriptIndex);
   }
 
-  inline int get_vert_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
+  inline int get_v_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
   {
-    return (this+vertAxis).get_base_coord(baseScriptIndex, baselineTagIndex);
+    return (this+vAxis).get_base_coord(baseScriptIndex, baselineTagIndex);
   }
 
-  // horizontal axis min/max coords:
-
-  inline unsigned int get_horiz_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
+  inline unsigned int get_h_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
   {
-    if (unlikely(horizAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+horizAxis).get_lang_tag_index (baseScriptIndex, baseLangSysTag);
+    return (this+hAxis).get_lang_tag_index (baseScriptIndex, baseLangSysTag);
   }
 
-  inline unsigned int get_horiz_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
+  inline unsigned int get_h_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
   {
-    if (unlikely(horizAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+horizAxis).get_feature_tag_index (baseScriptIndex, baseLangSysIndex, featureTableTag);
+    return (this+hAxis).get_feature_tag_index (baseScriptIndex, baseLangSysIndex, featureTableTag);
   }
 
-  inline int get_horiz_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline int get_h_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
-    return (this+horizAxis).get_max_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
+    return (this+hAxis).get_max_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
   }
 
-  inline int get_horiz_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline int get_h_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
-    return (this+horizAxis).get_min_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
+    return (this+hAxis).get_min_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
   }
 
-    // vertical axis min/max coords:
-
-  inline unsigned int get_vert_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
+  inline unsigned int get_v_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
   {
-    if (unlikely(vertAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+vertAxis).get_lang_tag_index (baseScriptIndex, baseLangSysTag);
+    return (this+vAxis).get_lang_tag_index (baseScriptIndex, baseLangSysTag);
   }
 
-  inline unsigned int get_vert_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
+  inline unsigned int get_v_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
   {
-    if (unlikely(vertAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+vertAxis).get_feature_tag_index (baseScriptIndex, baseLangSysIndex, featureTableTag);
+    return (this+vAxis).get_feature_tag_index (baseScriptIndex, baseLangSysIndex, featureTableTag);
   }
 
-  inline int get_vert_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline int get_v_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
-    return (this+vertAxis).get_max_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
+    return (this+vAxis).get_max_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
   }
 
-  inline int get_vert_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline int get_v_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
-    return (this+vertAxis).get_min_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
+    return (this+vAxis).get_min_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -631,15 +604,15 @@
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  likely (version.major == 1) &&
-		  horizAxis.sanitize (c, this) &&
-		  vertAxis.sanitize (c, this) &&
+		  hAxis.sanitize (c, this) &&
+		  vAxis.sanitize (c, this) &&
 		  (version.to_int () < 0x00010001u || varStore.sanitize (c, this)));
   }
 
   protected:
   FixedVersion<>  version;
-  OffsetTo<Axis>  horizAxis;
-  OffsetTo<Axis>  vertAxis;
+  OffsetTo<Axis>  hAxis;
+  OffsetTo<Axis>  vAxis;
   LOffsetTo<VariationStore>
 		varStore;		/* Offset to the table of Item Variation
 					 * Store--from beginning of BASE
diff --git a/src/hb-ot-layout-common-private.hh b/src/hb-ot-layout-common-private.hh
index 21a8382..89d5eae 100644
--- a/src/hb-ot-layout-common-private.hh
+++ b/src/hb-ot-layout-common-private.hh
@@ -41,6 +41,15 @@
 #ifndef HB_MAX_CONTEXT_LENGTH
 #define HB_MAX_CONTEXT_LENGTH	64
 #endif
+#ifndef HB_CLOSURE_MAX_STAGES
+/*
+ * The maximum number of times a lookup can be applied during shaping.
+ * Used to limit the number of iterations of the closure algorithm.
+ * This must be larger than the number of times add_pause() is
+ * called in a collect_features call of any shaper.
+ */
+#define HB_CLOSURE_MAX_STAGES	32
+#endif
 
 
 namespace OT {
@@ -164,7 +173,7 @@
   public:
   DEFINE_SIZE_STATIC (6);
 };
-DEFINE_NULL_DATA (OT, RangeRecord, "\000\001");
+DECLARE_NULL_NAMESPACE_BYTES (OT, RangeRecord);
 
 
 struct IndexArray : ArrayOf<Index>
@@ -181,6 +190,11 @@
     }
     return this->len;
   }
+
+  inline void add_indexes_to (hb_set_t* output /* OUT */) const
+  {
+    output->add_array (arrayZ, len);
+  }
 };
 
 
@@ -199,6 +213,8 @@
 					   unsigned int *feature_count /* IN/OUT */,
 					   unsigned int *feature_indexes /* OUT */) const
   { return featureIndex.get_indexes (start_offset, feature_count, feature_indexes); }
+  inline void add_feature_indexes_to (hb_set_t *feature_indexes) const
+  { featureIndex.add_indexes_to (feature_indexes); }
 
   inline bool has_required_feature (void) const { return reqFeatureIndex != 0xFFFFu; }
   inline unsigned int get_required_feature_index (void) const
@@ -224,8 +240,7 @@
   public:
   DEFINE_SIZE_ARRAY (6, featureIndex);
 };
-DEFINE_NULL_DATA (OT, LangSys, "\0\0\xFF\xFF");
-
+DECLARE_NULL_NAMESPACE_BYTES (OT, LangSys);
 
 struct Script
 {
@@ -540,9 +555,6 @@
 	  c->try_set (&featureParams, new_offset) &&
 	  !featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE))
 	return_trace (false);
-
-      if (c->edit_count > 1)
-        c->edit_count--; /* This was a "legitimate" edit; don't contribute to error count. */
     }
 
     return_trace (true);
@@ -597,6 +609,14 @@
   inline OffsetArrayOf<SubTableType>& get_subtables (void)
   { return CastR<OffsetArrayOf<SubTableType> > (subTable); }
 
+  inline unsigned int get_size (void) const
+  {
+    const HBUINT16 &markFilteringSet = StructAfter<const HBUINT16> (subTable);
+    if (lookupFlag & LookupFlag::UseMarkFilteringSet)
+      return (const char *) &StructAfter<const char> (markFilteringSet) - (const char *) this;
+    return (const char *) &markFilteringSet - (const char *) this;
+  }
+
   inline unsigned int get_type (void) const { return lookupType; }
 
   /* lookup_props is a 32-bit integer where the lower 16-bit is LookupFlag and
@@ -639,6 +659,7 @@
     if (unlikely (!subTable.serialize (c, num_subtables))) return_trace (false);
     if (lookupFlag & LookupFlag::UseMarkFilteringSet)
     {
+      if (unlikely (!c->extend (*this))) return_trace (false);
       HBUINT16 &markFilteringSet = StructAfter<HBUINT16> (subTable);
       markFilteringSet.set (lookup_props >> 16);
     }
diff --git a/src/hb-ot-layout-gdef-table.hh b/src/hb-ot-layout-gdef-table.hh
index 60a8d3a..d2b41a8 100644
--- a/src/hb-ot-layout-gdef-table.hh
+++ b/src/hb-ot-layout-gdef-table.hh
@@ -349,6 +349,7 @@
     ComponentGlyph	= 4
   };
 
+  inline bool has_data (void) const { return version.to_int () != 0; }
   inline bool has_glyph_classes (void) const { return glyphClassDef != 0; }
   inline unsigned int get_glyph_class (hb_codepoint_t glyph) const
   { return (this+glyphClassDef).get_class (glyph); }
diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index b52c014..fe34a32 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -1628,14 +1628,14 @@
 template <typename context_t>
 /*static*/ inline typename context_t::return_t PosLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
 {
-  const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->gpos);
+  const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->table.GPOS);
   const PosLookup &l = gpos.get_lookup (lookup_index);
   return l.dispatch (c);
 }
 
 /*static*/ inline bool PosLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
 {
-  const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->gpos);
+  const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->table.GPOS);
   const PosLookup &l = gpos.get_lookup (lookup_index);
   unsigned int saved_lookup_props = c->lookup_props;
   unsigned int saved_lookup_index = c->lookup_index;
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index 91b898b..1d5a02a 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -47,7 +47,7 @@
        * https://github.com/harfbuzz/harfbuzz/issues/363 */
       hb_codepoint_t glyph_id = iter.get_glyph ();
       if (c->glyphs->has (glyph_id))
-	c->glyphs->add ((glyph_id + deltaGlyphID) & 0xFFFFu);
+	c->out->add ((glyph_id + deltaGlyphID) & 0xFFFFu);
     }
   }
 
@@ -132,7 +132,7 @@
       if (unlikely (iter.get_coverage () >= count))
         break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       if (c->glyphs->has (iter.get_glyph ()))
-	c->glyphs->add (substitute[iter.get_coverage ()]);
+	c->out->add (substitute[iter.get_coverage ()]);
     }
   }
 
@@ -263,7 +263,7 @@
     TRACE_CLOSURE (this);
     unsigned int count = substitute.len;
     for (unsigned int i = 0; i < count; i++)
-      c->glyphs->add (substitute[i]);
+      c->out->add (substitute[i]);
   }
 
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
@@ -464,7 +464,7 @@
 	const AlternateSet &alt_set = this+alternateSet[iter.get_coverage ()];
 	unsigned int count = alt_set.len;
 	for (unsigned int i = 0; i < count; i++)
-	  c->glyphs->add (alt_set[i]);
+	  c->out->add (alt_set[i]);
       }
     }
   }
@@ -605,7 +605,7 @@
     for (unsigned int i = 1; i < count; i++)
       if (!c->glyphs->has (component[i]))
         return;
-    c->glyphs->add (ligGlyph);
+    c->out->add (ligGlyph);
   }
 
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
@@ -957,7 +957,7 @@
       if (unlikely (iter.get_coverage () >= count))
         break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       if (c->glyphs->has (iter.get_glyph ()))
-	c->glyphs->add (substitute[iter.get_coverage ()]);
+	c->out->add (substitute[iter.get_coverage ()]);
     }
   }
 
@@ -1163,7 +1163,12 @@
       return_trace (HB_VOID);
 
     c->set_recurse_func (dispatch_closure_recurse_func);
-    return_trace (dispatch (c));
+
+    hb_closure_context_t::return_t ret = dispatch (c);
+
+    c->flush ();
+
+    return_trace (ret);
   }
 
   inline hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
@@ -1265,7 +1270,12 @@
   {
     if (!c->should_visit_lookup (lookup_index))
       return HB_VOID;
-    return dispatch_recurse_func (c, lookup_index);
+
+    hb_closure_context_t::return_t ret = dispatch_recurse_func (c, lookup_index);
+
+    c->flush ();
+
+    return ret;
   }
 
   template <typename context_t>
@@ -1325,7 +1335,7 @@
 {
   _hb_buffer_assert_gsubgpos_vars (buffer);
 
-  const GDEF &gdef = *hb_ot_layout_from_face (font->face)->gdef;
+  const GDEF &gdef = *hb_ot_layout_from_face (font->face)->table.GDEF;
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
   {
@@ -1349,14 +1359,14 @@
 template <typename context_t>
 /*static*/ inline typename context_t::return_t SubstLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
 {
-  const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub);
+  const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->table.GSUB);
   const SubstLookup &l = gsub.get_lookup (lookup_index);
   return l.dispatch (c);
 }
 
 /*static*/ inline bool SubstLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
 {
-  const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub);
+  const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->table.GSUB);
   const SubstLookup &l = gsub.get_lookup (lookup_index);
   unsigned int saved_lookup_props = c->lookup_props;
   unsigned int saved_lookup_index = c->lookup_index;
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index aed00f1..40a2fc4 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -69,12 +69,13 @@
 
   bool is_lookup_done (unsigned int lookup_index)
   {
-    // Have we visited this lookup with the current set of glyphs?
+    /* Have we visited this lookup with the current set of glyphs? */
     return done_lookups->get (lookup_index) == glyphs->get_population ();
   }
 
   hb_face_t *face;
   hb_set_t *glyphs;
+  hb_auto_t<hb_set_t> out[1];
   recurse_func_t recurse_func;
   unsigned int nesting_level_left;
   unsigned int debug_depth;
@@ -90,8 +91,19 @@
 			  debug_depth (0),
                           done_lookups (done_lookups_) {}
 
+  ~hb_closure_context_t (void)
+  {
+    flush ();
+  }
+
   void set_recurse_func (recurse_func_t func) { recurse_func = func; }
 
+  void flush (void)
+  {
+    hb_set_union (glyphs, out);
+    hb_set_clear (out);
+  }
+
   private:
   hb_map_t *done_lookups;
 };
@@ -468,7 +480,7 @@
 			iter_input (), iter_context (),
 			font (font_), face (font->face), buffer (buffer_),
 			recurse_func (nullptr),
-			gdef (*hb_ot_layout_from_face (face)->gdef),
+			gdef (*hb_ot_layout_from_face (face)->table.GDEF),
 			var_store (gdef.get_var_store ()),
 			direction (buffer_->props.direction),
 			lookup_mask (1),
@@ -2308,6 +2320,7 @@
 
 struct GSUBGPOS
 {
+  inline bool has_data (void) const { return version.to_int () != 0; }
   inline unsigned int get_script_count (void) const
   { return (this+scriptList).len; }
   inline const Tag& get_script_tag (unsigned int i) const
diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh
index b2f974b..612bc7f 100644
--- a/src/hb-ot-layout-private.hh
+++ b/src/hb-ot-layout-private.hh
@@ -121,25 +121,6 @@
  * hb_ot_layout_t
  */
 
-namespace OT {
-  struct BASE;
-  struct COLR;
-  struct CPAL;
-  struct GDEF;
-  struct GSUB;
-  struct GPOS;
-  struct MATH;
-  struct fvar;
-  struct avar;
-}
-
-namespace AAT {
-  struct ankr;
-  struct kerx;
-  struct morx;
-  struct trak;
-}
-
 struct hb_ot_layout_lookup_accelerator_t
 {
   template <typename TLookup>
@@ -161,27 +142,73 @@
   hb_set_digest_t digest;
 };
 
+/* Most of these tables are NOT needed for shaping.  But we need to hook them *somewhere*.
+ * This is as good as any place. */
+#define HB_OT_LAYOUT_TABLES \
+    /* OpenType shaping. */ \
+    HB_OT_LAYOUT_TABLE(OT, GDEF) \
+    HB_OT_LAYOUT_TABLE(OT, GSUB) \
+    HB_OT_LAYOUT_TABLE(OT, GPOS) \
+    HB_OT_LAYOUT_TABLE(OT, JSTF) \
+    HB_OT_LAYOUT_TABLE(OT, BASE) \
+    /* AAT shaping. */ \
+    HB_OT_LAYOUT_TABLE(AAT, morx) \
+    HB_OT_LAYOUT_TABLE(AAT, kerx) \
+    HB_OT_LAYOUT_TABLE(AAT, ankr) \
+    HB_OT_LAYOUT_TABLE(AAT, trak) \
+    /* OpenType variations. */ \
+    HB_OT_LAYOUT_TABLE(OT, fvar) \
+    HB_OT_LAYOUT_TABLE(OT, avar) \
+    HB_OT_LAYOUT_TABLE(OT, MVAR) \
+    /* OpenType color. */ \
+    HB_OT_LAYOUT_TABLE(OT, COLR) \
+    HB_OT_LAYOUT_TABLE(OT, CPAL) \
+    HB_OT_LAYOUT_TABLE(OT, CBDT) \
+    HB_OT_LAYOUT_TABLE(OT, CBLC) \
+    HB_OT_LAYOUT_TABLE(OT, sbix) \
+    HB_OT_LAYOUT_TABLE(OT, svg) \
+    /* OpenType math. */ \
+    HB_OT_LAYOUT_TABLE(OT, MATH) \
+    /* OpenType fundamentals. */ \
+    HB_OT_LAYOUT_TABLE(OT, post) \
+    /* */
+
+/* Declare tables. */
+#define HB_OT_LAYOUT_TABLE(Namespace, Type) namespace Namespace { struct Type; }
+HB_OT_LAYOUT_TABLES
+#undef HB_OT_LAYOUT_TABLE
+
 struct hb_ot_layout_t
 {
-  hb_blob_t *gdef_blob;
-  hb_blob_t *gsub_blob;
-  hb_blob_t *gpos_blob;
-
-  const struct OT::GDEF *gdef;
-  const struct OT::GSUB *gsub;
-  const struct OT::GPOS *gpos;
-
-  /* TODO Move the following out of this struct. */
-  OT::hb_table_lazy_loader_t<struct OT::BASE> base;
-  OT::hb_table_lazy_loader_t<struct OT::MATH> math;
-  OT::hb_table_lazy_loader_t<struct OT::fvar> fvar;
-  OT::hb_table_lazy_loader_t<struct OT::avar> avar;
-
   unsigned int gsub_lookup_count;
   unsigned int gpos_lookup_count;
 
   hb_ot_layout_lookup_accelerator_t *gsub_accels;
   hb_ot_layout_lookup_accelerator_t *gpos_accels;
+
+  /* Various non-shaping tables. */
+  struct tables_t
+  {
+    HB_INTERNAL void init0 (hb_face_t *face);
+    HB_INTERNAL void fini (void);
+
+#define HB_OT_LAYOUT_TABLE_ORDER(Namespace, Type) \
+      HB_PASTE (ORDER_, HB_PASTE (Namespace, HB_PASTE (_, Type)))
+    enum order_t
+    {
+      ORDER_ZERO,
+#define HB_OT_LAYOUT_TABLE(Namespace, Type) \
+	HB_OT_LAYOUT_TABLE_ORDER (Namespace, Type),
+      HB_OT_LAYOUT_TABLES
+#undef HB_OT_LAYOUT_TABLE
+    };
+
+    hb_face_t *face; /* MUST be JUST before the lazy loaders. */
+#define HB_OT_LAYOUT_TABLE(Namespace, Type) \
+    hb_table_lazy_loader_t<struct Namespace::Type, HB_OT_LAYOUT_TABLE_ORDER (Namespace, Type)> Type;
+    HB_OT_LAYOUT_TABLES
+#undef HB_OT_LAYOUT_TABLE
+  } table;
 };
 
 
@@ -192,7 +219,7 @@
 _hb_ot_layout_destroy (hb_ot_layout_t *layout);
 
 
-#define hb_ot_layout_from_face(face) ((hb_ot_layout_t *) face->shaper_data.ot)
+#define hb_ot_layout_from_face(face) ((hb_ot_layout_t *) face->shaper_data.ot.get_relaxed ())
 
 
 /*
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index 655c36c..09ff0e6 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -31,6 +31,7 @@
 #include "hb-open-type-private.hh"
 #include "hb-ot-layout-private.hh"
 #include "hb-ot-map-private.hh"
+#include "hb-map-private.hh"
 
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-ot-layout-gsub-table.hh"
@@ -44,9 +45,124 @@
 #include "hb-ot-color-sbix-table.hh"
 #include "hb-ot-color-svg-table.hh"
 #include "hb-ot-name-table.hh"
-#include "hb-map-private.hh"
 
 
+static bool
+_hb_ot_blacklist_gdef (unsigned int gdef_len,
+		       unsigned int gsub_len,
+		       unsigned int gpos_len)
+{
+  /* The ugly business of blacklisting individual fonts' tables happen here!
+   * See this thread for why we finally had to bend in and do this:
+   * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html
+   *
+   * In certain versions of Times New Roman Italic and Bold Italic,
+   * ASCII double quotation mark U+0022 has wrong glyph class 3 (mark)
+   * in GDEF.  Many versions of Tahoma have bad GDEF tables that
+   * incorrectly classify some spacing marks such as certain IPA
+   * symbols as glyph class 3. So do older versions of Microsoft
+   * Himalaya, and the version of Cantarell shipped by Ubuntu 16.04.
+   *
+   * Nuke the GDEF tables of to avoid unwanted width-zeroing.
+   *
+   * See https://bugzilla.mozilla.org/show_bug.cgi?id=1279925
+   *     https://bugzilla.mozilla.org/show_bug.cgi?id=1279693
+   *     https://bugzilla.mozilla.org/show_bug.cgi?id=1279875
+   */
+#define ENCODE(x,y,z) ((int64_t) (x) << 32 | (int64_t) (y) << 16 | (z))
+  switch ENCODE(gdef_len, gsub_len, gpos_len)
+  {
+    /* sha1sum:c5ee92f0bca4bfb7d06c4d03e8cf9f9cf75d2e8a Windows 7? timesi.ttf */
+    case ENCODE (442, 2874, 42038):
+    /* sha1sum:37fc8c16a0894ab7b749e35579856c73c840867b Windows 7? timesbi.ttf */
+    case ENCODE (430, 2874, 40662):
+    /* sha1sum:19fc45110ea6cd3cdd0a5faca256a3797a069a80 Windows 7 timesi.ttf */
+    case ENCODE (442, 2874, 39116):
+    /* sha1sum:6d2d3c9ed5b7de87bc84eae0df95ee5232ecde26 Windows 7 timesbi.ttf */
+    case ENCODE (430, 2874, 39374):
+    /* sha1sum:8583225a8b49667c077b3525333f84af08c6bcd8 OS X 10.11.3 Times New Roman Italic.ttf */
+    case ENCODE (490, 3046, 41638):
+    /* sha1sum:ec0f5a8751845355b7c3271d11f9918a966cb8c9 OS X 10.11.3 Times New Roman Bold Italic.ttf */
+    case ENCODE (478, 3046, 41902):
+    /* sha1sum:96eda93f7d33e79962451c6c39a6b51ee893ce8c  tahoma.ttf from Windows 8 */
+    case ENCODE (898, 12554, 46470):
+    /* sha1sum:20928dc06014e0cd120b6fc942d0c3b1a46ac2bc  tahomabd.ttf from Windows 8 */
+    case ENCODE (910, 12566, 47732):
+    /* sha1sum:4f95b7e4878f60fa3a39ca269618dfde9721a79e  tahoma.ttf from Windows 8.1 */
+    case ENCODE (928, 23298, 59332):
+    /* sha1sum:6d400781948517c3c0441ba42acb309584b73033  tahomabd.ttf from Windows 8.1 */
+    case ENCODE (940, 23310, 60732):
+    /* tahoma.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
+    case ENCODE (964, 23836, 60072):
+    /* tahomabd.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
+    case ENCODE (976, 23832, 61456):
+    /* sha1sum:e55fa2dfe957a9f7ec26be516a0e30b0c925f846  tahoma.ttf from Windows 10 */
+    case ENCODE (994, 24474, 60336):
+    /* sha1sum:7199385abb4c2cc81c83a151a7599b6368e92343  tahomabd.ttf from Windows 10 */
+    case ENCODE (1006, 24470, 61740):
+    /* tahoma.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
+    case ENCODE (1006, 24576, 61346):
+    /* tahomabd.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
+    case ENCODE (1018, 24572, 62828):
+    /* sha1sum:b9c84d820c49850d3d27ec498be93955b82772b5  tahoma.ttf from Windows 10 AU */
+    case ENCODE (1006, 24576, 61352):
+    /* sha1sum:2bdfaab28174bdadd2f3d4200a30a7ae31db79d2  tahomabd.ttf from Windows 10 AU */
+    case ENCODE (1018, 24572, 62834):
+    /* sha1sum:b0d36cf5a2fbe746a3dd277bffc6756a820807a7  Tahoma.ttf from Mac OS X 10.9 */
+    case ENCODE (832, 7324, 47162):
+    /* sha1sum:12fc4538e84d461771b30c18b5eb6bd434e30fba  Tahoma Bold.ttf from Mac OS X 10.9 */
+    case ENCODE (844, 7302, 45474):
+    /* sha1sum:eb8afadd28e9cf963e886b23a30b44ab4fd83acc  himalaya.ttf from Windows 7 */
+    case ENCODE (180, 13054, 7254):
+    /* sha1sum:73da7f025b238a3f737aa1fde22577a6370f77b0  himalaya.ttf from Windows 8 */
+    case ENCODE (192, 12638, 7254):
+    /* sha1sum:6e80fd1c0b059bbee49272401583160dc1e6a427  himalaya.ttf from Windows 8.1 */
+    case ENCODE (192, 12690, 7254):
+    /* 8d9267aea9cd2c852ecfb9f12a6e834bfaeafe44  cantarell-fonts-0.0.21/otf/Cantarell-Regular.otf */
+    /* 983988ff7b47439ab79aeaf9a45bd4a2c5b9d371  cantarell-fonts-0.0.21/otf/Cantarell-Oblique.otf */
+    case ENCODE (188, 248, 3852):
+    /* 2c0c90c6f6087ffbfea76589c93113a9cbb0e75f  cantarell-fonts-0.0.21/otf/Cantarell-Bold.otf */
+    /* 55461f5b853c6da88069ffcdf7f4dd3f8d7e3e6b  cantarell-fonts-0.0.21/otf/Cantarell-Bold-Oblique.otf */
+    case ENCODE (188, 264, 3426):
+    /* d125afa82a77a6475ac0e74e7c207914af84b37a padauk-2.80/Padauk.ttf RHEL 7.2 */
+    case ENCODE (1058, 47032, 11818):
+    /* 0f7b80437227b90a577cc078c0216160ae61b031 padauk-2.80/Padauk-Bold.ttf RHEL 7.2*/
+    case ENCODE (1046, 47030, 12600):
+    /* d3dde9aa0a6b7f8f6a89ef1002e9aaa11b882290 padauk-2.80/Padauk.ttf Ubuntu 16.04 */
+    case ENCODE (1058, 71796, 16770):
+    /* 5f3c98ccccae8a953be2d122c1b3a77fd805093f padauk-2.80/Padauk-Bold.ttf Ubuntu 16.04 */
+    case ENCODE (1046, 71790, 17862):
+    /* 6c93b63b64e8b2c93f5e824e78caca555dc887c7 padauk-2.80/Padauk-book.ttf */
+    case ENCODE (1046, 71788, 17112):
+    /* d89b1664058359b8ec82e35d3531931125991fb9 padauk-2.80/Padauk-bookbold.ttf */
+    case ENCODE (1058, 71794, 17514):
+    /* 824cfd193aaf6234b2b4dc0cf3c6ef576c0d00ef padauk-3.0/Padauk-book.ttf */
+    case ENCODE (1330, 109904, 57938):
+    /* 91fcc10cf15e012d27571e075b3b4dfe31754a8a padauk-3.0/Padauk-bookbold.ttf */
+    case ENCODE (1330, 109904, 58972):
+    /* sha1sum: c26e41d567ed821bed997e937bc0c41435689e85  Padauk.ttf
+     *  "Padauk Regular" "Version 2.5", see https://crbug.com/681813 */
+    case ENCODE (1004, 59092, 14836):
+      return true;
+#undef ENCODE
+  }
+  return false;
+}
+
+void hb_ot_layout_t::tables_t::init0 (hb_face_t *face)
+{
+  this->face = face;
+#define HB_OT_LAYOUT_TABLE(Namespace, Type) Type.init0 ();
+  HB_OT_LAYOUT_TABLES
+#undef HB_OT_LAYOUT_TABLE
+}
+void hb_ot_layout_t::tables_t::fini (void)
+{
+#define HB_OT_LAYOUT_TABLE(Namespace, Type) Type.fini ();
+  HB_OT_LAYOUT_TABLES
+#undef HB_OT_LAYOUT_TABLE
+}
+
 hb_ot_layout_t *
 _hb_ot_layout_create (hb_face_t *face)
 {
@@ -54,143 +170,33 @@
   if (unlikely (!layout))
     return nullptr;
 
-  layout->gdef_blob = OT::Sanitizer<OT::GDEF>().sanitize (face->reference_table (HB_OT_TAG_GDEF));
-  layout->gdef = layout->gdef_blob->as<OT::GDEF> ();
+  layout->table.init0 (face);
 
-  layout->gsub_blob = OT::Sanitizer<OT::GSUB>().sanitize (face->reference_table (HB_OT_TAG_GSUB));
-  layout->gsub = layout->gsub_blob->as<OT::GSUB> ();
+  const OT::GSUB &gsub = *layout->table.GSUB;
+  const OT::GPOS &gpos = *layout->table.GPOS;
 
-  layout->gpos_blob = OT::Sanitizer<OT::GPOS>().sanitize (face->reference_table (HB_OT_TAG_GPOS));
-  layout->gpos = layout->gpos_blob->as<OT::GPOS> ();
+  if (unlikely (_hb_ot_blacklist_gdef (layout->table.GDEF.get_blob ()->length,
+				       layout->table.GSUB.get_blob ()->length,
+				       layout->table.GPOS.get_blob ()->length)))
+    layout->table.GDEF.set_stored (hb_blob_get_empty ());
 
-  layout->math.init (face);
-  layout->fvar.init (face);
-  layout->avar.init (face);
+  unsigned int gsub_lookup_count = layout->gsub_lookup_count = gsub.get_lookup_count ();
+  unsigned int gpos_lookup_count = layout->gpos_lookup_count = gpos.get_lookup_count ();
 
-  {
-    /*
-     * The ugly business of blacklisting individual fonts' tables happen here!
-     * See this thread for why we finally had to bend in and do this:
-     * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html
-     */
-    unsigned int gdef_len = layout->gdef_blob->length;
-    unsigned int gsub_len = layout->gsub_blob->length;
-    unsigned int gpos_len = layout->gpos_blob->length;
-    if (0
-      /* sha1sum:c5ee92f0bca4bfb7d06c4d03e8cf9f9cf75d2e8a Windows 7? timesi.ttf */
-      || (442 == gdef_len && 42038 == gpos_len && 2874 == gsub_len)
-      /* sha1sum:37fc8c16a0894ab7b749e35579856c73c840867b Windows 7? timesbi.ttf */
-      || (430 == gdef_len && 40662 == gpos_len && 2874 == gsub_len)
-      /* sha1sum:19fc45110ea6cd3cdd0a5faca256a3797a069a80 Windows 7 timesi.ttf */
-      || (442 == gdef_len && 39116 == gpos_len && 2874 == gsub_len)
-      /* sha1sum:6d2d3c9ed5b7de87bc84eae0df95ee5232ecde26 Windows 7 timesbi.ttf */
-      || (430 == gdef_len && 39374 == gpos_len && 2874 == gsub_len)
-      /* sha1sum:8583225a8b49667c077b3525333f84af08c6bcd8 OS X 10.11.3 Times New Roman Italic.ttf */
-      || (490 == gdef_len && 41638 == gpos_len && 3046 == gsub_len)
-      /* sha1sum:ec0f5a8751845355b7c3271d11f9918a966cb8c9 OS X 10.11.3 Times New Roman Bold Italic.ttf */
-      || (478 == gdef_len && 41902 == gpos_len && 3046 == gsub_len)
-    )
-    {
-      /* In certain versions of Times New Roman Italic and Bold Italic,
-       * ASCII double quotation mark U+0022, mapped to glyph 5, has wrong
-       * glyph class 3 (mark) in GDEF.  Nuke the GDEF to avoid zero-width
-       * double-quote.  See:
-       * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html
-       */
-     if (3 == layout->gdef->get_glyph_class (5))
-       layout->gdef = &Null(OT::GDEF);
-    }
-    else if (0
-      /* sha1sum:96eda93f7d33e79962451c6c39a6b51ee893ce8c  tahoma.ttf from Windows 8 */
-      || (898 == gdef_len && 46470 == gpos_len && 12554 == gsub_len)
-      /* sha1sum:20928dc06014e0cd120b6fc942d0c3b1a46ac2bc  tahomabd.ttf from Windows 8 */
-      || (910 == gdef_len && 47732 == gpos_len && 12566 == gsub_len)
-      /* sha1sum:4f95b7e4878f60fa3a39ca269618dfde9721a79e  tahoma.ttf from Windows 8.1 */
-      || (928 == gdef_len && 59332 == gpos_len && 23298 == gsub_len)
-      /* sha1sum:6d400781948517c3c0441ba42acb309584b73033  tahomabd.ttf from Windows 8.1 */
-      || (940 == gdef_len && 60732 == gpos_len && 23310 == gsub_len)
-      /* tahoma.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
-      || (964 == gdef_len && 60072 == gpos_len && 23836 == gsub_len)
-      /* tahomabd.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
-      || (976 == gdef_len && 61456 == gpos_len && 23832 == gsub_len)
-      /* sha1sum:e55fa2dfe957a9f7ec26be516a0e30b0c925f846  tahoma.ttf from Windows 10 */
-      || (994 == gdef_len && 60336 == gpos_len && 24474 == gsub_len)
-      /* sha1sum:7199385abb4c2cc81c83a151a7599b6368e92343  tahomabd.ttf from Windows 10 */
-      || (1006 == gdef_len && 61740 == gpos_len && 24470 == gsub_len)
-      /* tahoma.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
-      || (1006 == gdef_len && 61346 == gpos_len && 24576 == gsub_len)
-      /* tahomabd.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
-      || (1018 == gdef_len && 62828 == gpos_len && 24572 == gsub_len)
-      /* sha1sum:b9c84d820c49850d3d27ec498be93955b82772b5  tahoma.ttf from Windows 10 AU */
-      || (1006 == gdef_len && 61352 == gpos_len && 24576 == gsub_len)
-      /* sha1sum:2bdfaab28174bdadd2f3d4200a30a7ae31db79d2  tahomabd.ttf from Windows 10 AU */
-      || (1018 == gdef_len && 62834 == gpos_len && 24572 == gsub_len)
-      /* sha1sum:b0d36cf5a2fbe746a3dd277bffc6756a820807a7  Tahoma.ttf from Mac OS X 10.9 */
-      || (832 == gdef_len && 47162 == gpos_len && 7324 == gsub_len)
-      /* sha1sum:12fc4538e84d461771b30c18b5eb6bd434e30fba  Tahoma Bold.ttf from Mac OS X 10.9 */
-      || (844 == gdef_len && 45474 == gpos_len && 7302 == gsub_len)
-      /* sha1sum:eb8afadd28e9cf963e886b23a30b44ab4fd83acc  himalaya.ttf from Windows 7 */
-      || (180 == gdef_len && 7254 == gpos_len && 13054 == gsub_len)
-      /* sha1sum:73da7f025b238a3f737aa1fde22577a6370f77b0  himalaya.ttf from Windows 8 */
-      || (192 == gdef_len && 7254 == gpos_len && 12638 == gsub_len)
-      /* sha1sum:6e80fd1c0b059bbee49272401583160dc1e6a427  himalaya.ttf from Windows 8.1 */
-      || (192 == gdef_len && 7254 == gpos_len && 12690 == gsub_len)
-      /* 8d9267aea9cd2c852ecfb9f12a6e834bfaeafe44  cantarell-fonts-0.0.21/otf/Cantarell-Regular.otf */
-      /* 983988ff7b47439ab79aeaf9a45bd4a2c5b9d371  cantarell-fonts-0.0.21/otf/Cantarell-Oblique.otf */
-      || (188 == gdef_len && 3852 == gpos_len && 248 == gsub_len)
-      /* 2c0c90c6f6087ffbfea76589c93113a9cbb0e75f  cantarell-fonts-0.0.21/otf/Cantarell-Bold.otf */
-      /* 55461f5b853c6da88069ffcdf7f4dd3f8d7e3e6b  cantarell-fonts-0.0.21/otf/Cantarell-Bold-Oblique.otf */
-      || (188 == gdef_len && 3426 == gpos_len && 264 == gsub_len)
-      /* d125afa82a77a6475ac0e74e7c207914af84b37a padauk-2.80/Padauk.ttf RHEL 7.2 */
-      || (1058 == gdef_len && 11818 == gpos_len && 47032 == gsub_len)
-      /* 0f7b80437227b90a577cc078c0216160ae61b031 padauk-2.80/Padauk-Bold.ttf RHEL 7.2*/
-      || (1046 == gdef_len && 12600 == gpos_len && 47030 == gsub_len)
-      /* d3dde9aa0a6b7f8f6a89ef1002e9aaa11b882290 padauk-2.80/Padauk.ttf Ubuntu 16.04 */
-      || (1058 == gdef_len && 16770 == gpos_len && 71796 == gsub_len)
-      /* 5f3c98ccccae8a953be2d122c1b3a77fd805093f padauk-2.80/Padauk-Bold.ttf Ubuntu 16.04 */
-      || (1046 == gdef_len && 17862 == gpos_len && 71790 == gsub_len)
-      /* 6c93b63b64e8b2c93f5e824e78caca555dc887c7 padauk-2.80/Padauk-book.ttf */
-      || (1046 == gdef_len && 17112 == gpos_len && 71788 == gsub_len)
-      /* d89b1664058359b8ec82e35d3531931125991fb9 padauk-2.80/Padauk-bookbold.ttf */
-      || (1058 == gdef_len && 17514 == gpos_len && 71794 == gsub_len)
-      /* 824cfd193aaf6234b2b4dc0cf3c6ef576c0d00ef padauk-3.0/Padauk-book.ttf */
-      || (1330 == gdef_len && 57938 == gpos_len && 109904 == gsub_len)
-      /* 91fcc10cf15e012d27571e075b3b4dfe31754a8a padauk-3.0/Padauk-bookbold.ttf */
-      || (1330 == gdef_len && 58972 == gpos_len && 109904 == gsub_len)
-      /* sha1sum: c26e41d567ed821bed997e937bc0c41435689e85  Padauk.ttf
-       *  "Padauk Regular" "Version 2.5", see https://crbug.com/681813 */
-      || (1004 == gdef_len && 14836 == gpos_len && 59092 == gsub_len)
-    )
-    {
-      /* Many versions of Tahoma have bad GDEF tables that incorrectly classify some spacing marks
-       * such as certain IPA symbols as glyph class 3. So do older versions of Microsoft Himalaya,
-       * and the version of Cantarell shipped by Ubuntu 16.04.
-       * Nuke the GDEF tables of these fonts to avoid unwanted width-zeroing.
-       * See https://bugzilla.mozilla.org/show_bug.cgi?id=1279925
-       *     https://bugzilla.mozilla.org/show_bug.cgi?id=1279693
-       *     https://bugzilla.mozilla.org/show_bug.cgi?id=1279875
-       */
-      layout->gdef = &Null(OT::GDEF);
-    }
-  }
+  layout->gsub_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (gsub_lookup_count, sizeof (hb_ot_layout_lookup_accelerator_t));
+  layout->gpos_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (gpos_lookup_count, sizeof (hb_ot_layout_lookup_accelerator_t));
 
-  layout->gsub_lookup_count = layout->gsub->get_lookup_count ();
-  layout->gpos_lookup_count = layout->gpos->get_lookup_count ();
-
-  layout->gsub_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t));
-  layout->gpos_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t));
-
-  if (unlikely ((layout->gsub_lookup_count && !layout->gsub_accels) ||
-		(layout->gpos_lookup_count && !layout->gpos_accels)))
+  if (unlikely ((gsub_lookup_count && !layout->gsub_accels) ||
+		(gpos_lookup_count && !layout->gpos_accels)))
   {
     _hb_ot_layout_destroy (layout);
     return nullptr;
   }
 
-  for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
-    layout->gsub_accels[i].init (layout->gsub->get_lookup (i));
-  for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
-    layout->gpos_accels[i].init (layout->gpos->get_lookup (i));
+  for (unsigned int i = 0; i < gsub_lookup_count; i++)
+    layout->gsub_accels[i].init (gsub.get_lookup (i));
+  for (unsigned int i = 0; i < gpos_lookup_count; i++)
+    layout->gpos_accels[i].init (gpos.get_lookup (i));
 
   return layout;
 }
@@ -208,13 +214,7 @@
   free (layout->gsub_accels);
   free (layout->gpos_accels);
 
-  hb_blob_destroy (layout->gdef_blob);
-  hb_blob_destroy (layout->gsub_blob);
-  hb_blob_destroy (layout->gpos_blob);
-
-  layout->math.fini ();
-  layout->fvar.fini ();
-  layout->avar.fini ();
+  layout->table.fini ();
 
   free (layout);
 }
@@ -231,19 +231,19 @@
 _get_gdef (hb_face_t *face)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::GDEF);
-  return *hb_ot_layout_from_face (face)->gdef;
+  return *hb_ot_layout_from_face (face)->table.GDEF;
 }
 static inline const OT::GSUB&
 _get_gsub (hb_face_t *face)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::GSUB);
-  return *hb_ot_layout_from_face (face)->gsub;
+  return *hb_ot_layout_from_face (face)->table.GSUB;
 }
 static inline const OT::GPOS&
 _get_gpos (hb_face_t *face)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::GPOS);
-  return *hb_ot_layout_from_face (face)->gpos;
+  return *hb_ot_layout_from_face (face)->table.GPOS;
 }
 
 /*
@@ -518,6 +518,19 @@
   return l.has_required_feature ();
 }
 
+static void
+_hb_ot_layout_language_add_feature_indexes_to (hb_face_t    *face,
+                                               hb_tag_t      table_tag,
+                                               unsigned int  script_index,
+                                               unsigned int  language_index,
+                                               hb_set_t     *feature_indexes /* OUT */)
+{
+  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
+  l.add_feature_indexes_to (feature_indexes);
+}
+
+
 unsigned int
 hb_ot_layout_language_get_feature_indexes (hb_face_t    *face,
 					   hb_tag_t      table_tag,
@@ -656,12 +669,12 @@
 }
 
 static void
-_hb_ot_layout_collect_lookups_features (hb_face_t      *face,
-					hb_tag_t        table_tag,
-					unsigned int    script_index,
-					unsigned int    language_index,
-					const hb_tag_t *features,
-					hb_set_t       *lookup_indexes /* OUT */)
+_hb_ot_layout_collect_features_features (hb_face_t      *face,
+                                         hb_tag_t        table_tag,
+                                         unsigned int    script_index,
+                                         unsigned int    language_index,
+                                         const hb_tag_t *features,
+                                         hb_set_t       *feature_indexes /* OUT */)
 {
   if (!features)
   {
@@ -672,33 +685,14 @@
 						    language_index,
 						    &required_feature_index,
 						    nullptr))
-      _hb_ot_layout_collect_lookups_lookups (face,
-					     table_tag,
-					     required_feature_index,
-					     lookup_indexes);
+      feature_indexes->add (required_feature_index);
 
     /* All features */
-    unsigned int feature_indices[32];
-    unsigned int offset, len;
-
-    offset = 0;
-    do {
-      len = ARRAY_LENGTH (feature_indices);
-      hb_ot_layout_language_get_feature_indexes (face,
-						 table_tag,
-						 script_index,
-						 language_index,
-						 offset, &len,
-						 feature_indices);
-
-      for (unsigned int i = 0; i < len; i++)
-	_hb_ot_layout_collect_lookups_lookups (face,
-					       table_tag,
-					       feature_indices[i],
-					       lookup_indexes);
-
-      offset += len;
-    } while (len == ARRAY_LENGTH (feature_indices));
+    _hb_ot_layout_language_add_feature_indexes_to (face,
+                                                   table_tag,
+                                                   script_index,
+                                                   language_index,
+                                                   feature_indexes);
   }
   else
   {
@@ -711,28 +705,25 @@
 					      language_index,
 					      *features,
 					      &feature_index))
-        _hb_ot_layout_collect_lookups_lookups (face,
-					       table_tag,
-					       feature_index,
-					       lookup_indexes);
+        feature_indexes->add (feature_index);
     }
   }
 }
 
 static void
-_hb_ot_layout_collect_lookups_languages (hb_face_t      *face,
-					 hb_tag_t        table_tag,
-					 unsigned int    script_index,
-					 const hb_tag_t *languages,
-					 const hb_tag_t *features,
-					 hb_set_t       *lookup_indexes /* OUT */)
+_hb_ot_layout_collect_features_languages (hb_face_t      *face,
+                                          hb_tag_t        table_tag,
+                                          unsigned int    script_index,
+                                          const hb_tag_t *languages,
+                                          const hb_tag_t *features,
+                                          hb_set_t       *feature_indexes /* OUT */)
 {
-  _hb_ot_layout_collect_lookups_features (face,
-					  table_tag,
-					  script_index,
-					  HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
-					  features,
-					  lookup_indexes);
+  _hb_ot_layout_collect_features_features (face,
+                                           table_tag,
+                                           script_index,
+                                           HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
+                                           features,
+                                           feature_indexes);
 
   if (!languages)
   {
@@ -742,12 +733,12 @@
 								script_index,
 								0, nullptr, nullptr);
     for (unsigned int language_index = 0; language_index < count; language_index++)
-      _hb_ot_layout_collect_lookups_features (face,
-					      table_tag,
-					      script_index,
-					      language_index,
-					      features,
-					      lookup_indexes);
+      _hb_ot_layout_collect_features_features (face,
+                                               table_tag,
+                                               script_index,
+                                               language_index,
+                                               features,
+                                               feature_indexes);
   }
   else
   {
@@ -759,12 +750,58 @@
 					     script_index,
 					     *languages,
 					     &language_index))
-        _hb_ot_layout_collect_lookups_features (face,
-						table_tag,
-						script_index,
-						language_index,
-						features,
-						lookup_indexes);
+        _hb_ot_layout_collect_features_features (face,
+                                                 table_tag,
+                                                 script_index,
+                                                 language_index,
+                                                 features,
+                                                 feature_indexes);
+    }
+  }
+}
+
+/**
+ * hb_ot_layout_collect_features:
+ *
+ * Since: 1.8.5
+ **/
+void
+hb_ot_layout_collect_features (hb_face_t      *face,
+                               hb_tag_t        table_tag,
+                               const hb_tag_t *scripts,
+                               const hb_tag_t *languages,
+                               const hb_tag_t *features,
+                               hb_set_t       *feature_indexes /* OUT */)
+{
+  if (!scripts)
+  {
+    /* All scripts */
+    unsigned int count = hb_ot_layout_table_get_script_tags (face,
+							     table_tag,
+							     0, nullptr, nullptr);
+    for (unsigned int script_index = 0; script_index < count; script_index++)
+      _hb_ot_layout_collect_features_languages (face,
+                                                table_tag,
+                                                script_index,
+                                                languages,
+                                                features,
+                                                feature_indexes);
+  }
+  else
+  {
+    for (; *scripts; scripts++)
+    {
+      unsigned int script_index;
+      if (hb_ot_layout_table_find_script (face,
+					  table_tag,
+					  *scripts,
+					  &script_index))
+        _hb_ot_layout_collect_features_languages (face,
+                                                  table_tag,
+                                                  script_index,
+                                                  languages,
+                                                  features,
+                                                  feature_indexes);
     }
   }
 }
@@ -782,37 +819,10 @@
 			      const hb_tag_t *features,
 			      hb_set_t       *lookup_indexes /* OUT */)
 {
-  if (!scripts)
-  {
-    /* All scripts */
-    unsigned int count = hb_ot_layout_table_get_script_tags (face,
-							     table_tag,
-							     0, nullptr, nullptr);
-    for (unsigned int script_index = 0; script_index < count; script_index++)
-      _hb_ot_layout_collect_lookups_languages (face,
-					       table_tag,
-					       script_index,
-					       languages,
-					       features,
-					       lookup_indexes);
-  }
-  else
-  {
-    for (; *scripts; scripts++)
-    {
-      unsigned int script_index;
-      if (hb_ot_layout_table_find_script (face,
-					  table_tag,
-					  *scripts,
-					  &script_index))
-        _hb_ot_layout_collect_lookups_languages (face,
-						 table_tag,
-						 script_index,
-						 languages,
-						 features,
-						 lookup_indexes);
-    }
-  }
+  hb_auto_t<hb_set_t> feature_indexes;
+  hb_ot_layout_collect_features (face, table_tag, scripts, languages, features, &feature_indexes);
+  for (hb_codepoint_t feature_index = HB_SET_VALUE_INVALID; hb_set_next (&feature_indexes, &feature_index);)
+    _hb_ot_layout_collect_lookups_lookups (face, table_tag, feature_index, lookup_indexes);
 }
 
 /**
@@ -841,13 +851,13 @@
   {
     case HB_OT_TAG_GSUB:
     {
-      const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
+      const OT::SubstLookup& l = hb_ot_layout_from_face (face)->table.GSUB->get_lookup (lookup_index);
       l.collect_glyphs (&c);
       return;
     }
     case HB_OT_TAG_GPOS:
     {
-      const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index);
+      const OT::PosLookup& l = hb_ot_layout_from_face (face)->table.GPOS->get_lookup (lookup_index);
       l.collect_glyphs (&c);
       return;
     }
@@ -894,7 +904,7 @@
 hb_bool_t
 hb_ot_layout_has_substitution (hb_face_t *face)
 {
-  return &_get_gsub (face) != &Null(OT::GSUB);
+  return _get_gsub (face).has_data ();
 }
 
 /**
@@ -923,7 +933,7 @@
   if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false;
   OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context);
 
-  const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
+  const OT::SubstLookup& l = hb_ot_layout_from_face (face)->table.GSUB->get_lookup (lookup_index);
 
   return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_accels[lookup_index]);
 }
@@ -969,6 +979,7 @@
   OT::hb_closure_context_t c (face, glyphs, &done_lookups);
   const OT::GSUB& gsub = _get_gsub (face);
 
+  unsigned int iteration_count = 0;
   unsigned int glyphs_length;
   do
   {
@@ -983,7 +994,9 @@
       for (unsigned int i = 0; i < gsub.get_lookup_count (); i++)
         gsub.get_lookup (i).closure (&c, i);
     }
-  } while (glyphs_length != glyphs->get_population ());
+    iteration_count++;
+  } while (iteration_count <= HB_CLOSURE_MAX_STAGES
+           && glyphs_length != glyphs->get_population ());
 }
 
 /*
@@ -993,7 +1006,7 @@
 hb_bool_t
 hb_ot_layout_has_positioning (hb_face_t *face)
 {
-  return &_get_gpos (face) != &Null(OT::GPOS);
+  return _get_gpos (face).has_data ();
 }
 
 void
@@ -1078,7 +1091,7 @@
   typedef OT::SubstLookup Lookup;
 
   GSUBProxy (hb_face_t *face) :
-    table (*hb_ot_layout_from_face (face)->gsub),
+    table (*hb_ot_layout_from_face (face)->table.GSUB),
     accels (hb_ot_layout_from_face (face)->gsub_accels) {}
 
   const OT::GSUB &table;
@@ -1092,7 +1105,7 @@
   typedef OT::PosLookup Lookup;
 
   GPOSProxy (hb_face_t *face) :
-    table (*hb_ot_layout_from_face (face)->gpos),
+    table (*hb_ot_layout_from_face (face)->table.GPOS),
     accels (hb_ot_layout_from_face (face)->gpos_accels) {}
 
   const OT::GPOS &table;
@@ -1101,7 +1114,7 @@
 
 
 struct hb_get_subtables_context_t :
-       OT::hb_dispatch_context_t<hb_get_subtables_context_t, hb_void_t, HB_DEBUG_APPLY>
+       hb_dispatch_context_t<hb_get_subtables_context_t, hb_void_t, HB_DEBUG_APPLY>
 {
   template <typename Type>
   static inline bool apply_to (const void *obj, OT::hb_ot_apply_context_t *c)
@@ -1328,5 +1341,5 @@
 // hb_bool_t
 // hb_ot_base_has_data (hb_face_t *face)
 // {
-//   return &_get_base (face) != &Null(OT::BASE);
+//   return _get_base (face).has_data ();
 // }
diff --git a/src/hb-ot-layout.h b/src/hb-ot-layout.h
index 0278796..586fb15 100644
--- a/src/hb-ot-layout.h
+++ b/src/hb-ot-layout.h
@@ -194,6 +194,13 @@
 hb_ot_layout_table_get_lookup_count (hb_face_t    *face,
 				     hb_tag_t      table_tag);
 
+HB_EXTERN void
+hb_ot_layout_collect_features (hb_face_t      *face,
+                               hb_tag_t        table_tag,
+                               const hb_tag_t *scripts,
+                               const hb_tag_t *languages,
+                               const hb_tag_t *features,
+                               hb_set_t       *feature_indexes /* OUT */);
 
 HB_EXTERN void
 hb_ot_layout_collect_lookups (hb_face_t      *face,
diff --git a/src/hb-ot-math-table.hh b/src/hb-ot-math-table.hh
index 5fef2d2..2dd7145 100644
--- a/src/hb-ot-math-table.hh
+++ b/src/hb-ot-math-table.hh
@@ -686,6 +686,8 @@
 {
   static const hb_tag_t tableTag	= HB_OT_TAG_MATH;
 
+  inline bool has_data (void) const { return version.to_int () != 0; }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
diff --git a/src/hb-ot-math.cc b/src/hb-ot-math.cc
index 1667a7d..66ce207 100644
--- a/src/hb-ot-math.cc
+++ b/src/hb-ot-math.cc
@@ -34,7 +34,7 @@
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::MATH);
   hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-  return *(layout->math.get ());
+  return *(layout->table.MATH.get ());
 }
 
 /*
@@ -55,7 +55,7 @@
 hb_bool_t
 hb_ot_math_has_data (hb_face_t *face)
 {
-  return &_get_math (face) != &Null(OT::MATH);
+  return _get_math (face).has_data ();
 }
 
 /**
diff --git a/src/hb-ot-maxp-table.hh b/src/hb-ot-maxp-table.hh
index 390b60d..75aac4f 100644
--- a/src/hb-ot-maxp-table.hh
+++ b/src/hb-ot-maxp-table.hh
@@ -100,14 +100,14 @@
 
   inline bool subset (hb_subset_plan_t *plan) const
   {
-    hb_blob_t *maxp_blob = OT::Sanitizer<OT::maxp>().sanitize (hb_face_reference_table (plan->source, HB_OT_TAG_maxp));
+    hb_blob_t *maxp_blob = hb_sanitize_context_t().reference_table<maxp> (plan->source);
     hb_blob_t *maxp_prime_blob = hb_blob_copy_writable_or_fail (maxp_blob);
     hb_blob_destroy (maxp_blob);
 
     if (unlikely (!maxp_prime_blob)) {
       return false;
     }
-    OT::maxp *maxp_prime = (OT::maxp *) hb_blob_get_data (maxp_prime_blob, nullptr);
+    maxp *maxp_prime = (maxp *) hb_blob_get_data (maxp_prime_blob, nullptr);
 
     maxp_prime->set_num_glyphs (plan->glyphs.len);
     if (plan->drop_hints)
@@ -118,7 +118,7 @@
     return result;
   }
 
-  static inline void drop_hint_fields (hb_subset_plan_t *plan, OT::maxp *maxp_prime)
+  static inline void drop_hint_fields (hb_subset_plan_t *plan, maxp *maxp_prime)
   {
     if (maxp_prime->version.major == 1)
     {
diff --git a/src/hb-ot-os2-table.hh b/src/hb-ot-os2-table.hh
index c52b7eb..56bbab7 100644
--- a/src/hb-ot-os2-table.hh
+++ b/src/hb-ot-os2-table.hh
@@ -51,12 +51,12 @@
 
   inline bool subset (hb_subset_plan_t *plan) const
   {
-    hb_blob_t *os2_blob = OT::Sanitizer<OT::os2>().sanitize (hb_face_reference_table (plan->source, HB_OT_TAG_os2));
+    hb_blob_t *os2_blob = hb_sanitize_context_t().reference_table<os2> (plan->source);
     hb_blob_t *os2_prime_blob = hb_blob_create_sub_blob (os2_blob, 0, -1);
     // TODO(grieger): move to hb_blob_copy_writable_or_fail
     hb_blob_destroy (os2_blob);
 
-    OT::os2 *os2_prime = (OT::os2 *) hb_blob_get_data_writable (os2_prime_blob, nullptr);
+    os2 *os2_prime = (os2 *) hb_blob_get_data_writable (os2_prime_blob, nullptr);
     if (unlikely (!os2_prime)) {
       hb_blob_destroy (os2_prime_blob);
       return false;
diff --git a/src/hb-ot-post-table.hh b/src/hb-ot-post-table.hh
index d05fc09..4f08a51 100644
--- a/src/hb-ot-post-table.hh
+++ b/src/hb-ot-post-table.hh
@@ -85,7 +85,7 @@
   inline bool subset (hb_subset_plan_t *plan) const
   {
     unsigned int post_prime_length;
-    hb_blob_t *post_blob = OT::Sanitizer<post>().sanitize (hb_face_reference_table (plan->source, HB_OT_TAG_post));
+    hb_blob_t *post_blob = hb_sanitize_context_t().reference_table<post>(plan->source);
     hb_blob_t *post_prime_blob = hb_blob_create_sub_blob (post_blob, 0, post::static_size);
     post *post_prime = (post *) hb_blob_get_data_writable (post_prime_blob, &post_prime_length);
     hb_blob_destroy (post_blob);
@@ -110,7 +110,7 @@
     {
       index_to_offset.init ();
 
-      blob = Sanitizer<post>().sanitize (face->reference_table (HB_OT_TAG_post));
+      blob = hb_sanitize_context_t().reference_table<post> (face);
       const post *table = blob->as<post> ();
       unsigned int table_length = blob->length;
 
@@ -130,7 +130,7 @@
     inline void fini (void)
     {
       index_to_offset.fini ();
-      free (gids_sorted_by_name);
+      free (gids_sorted_by_name.get ());
     }
 
     inline bool get_glyph_name (hb_codepoint_t glyph,
@@ -162,7 +162,7 @@
 	return false;
 
     retry:
-      uint16_t *gids = (uint16_t *) hb_atomic_ptr_get (&gids_sorted_by_name);
+      uint16_t *gids = gids_sorted_by_name.get ();
 
       if (unlikely (!gids))
       {
@@ -174,7 +174,8 @@
 	  gids[i] = i;
 	hb_sort_r (gids, count, sizeof (gids[0]), cmp_gids, (void *) this);
 
-	if (!hb_atomic_ptr_cmpexch (&gids_sorted_by_name, nullptr, gids)) {
+	if (unlikely (!gids_sorted_by_name.cmpexch (nullptr, gids)))
+	{
 	  free (gids);
 	  goto retry;
 	}
@@ -255,7 +256,7 @@
     const ArrayOf<HBUINT16> *glyphNameIndex;
     hb_vector_t<uint32_t, 1> index_to_offset;
     const uint8_t *pool;
-    mutable uint16_t *gids_sorted_by_name;
+    hb_atomic_ptr_t<uint16_t *> gids_sorted_by_name;
   };
 
   public:
diff --git a/src/hb-ot-shape-complex-arabic-fallback.hh b/src/hb-ot-shape-complex-arabic-fallback.hh
index 5a257f0..a55511a 100644
--- a/src/hb-ot-shape-complex-arabic-fallback.hh
+++ b/src/hb-ot-shape-complex-arabic-fallback.hh
@@ -79,12 +79,12 @@
    * May not be good-enough for presidential candidate interviews, but good-enough for us... */
   hb_stable_sort (&glyphs[0], num_glyphs, (int(*)(const OT::GlyphID*, const OT::GlyphID *)) OT::GlyphID::cmp, &substitutes[0]);
 
-  OT::Supplier<OT::GlyphID> glyphs_supplier      (glyphs, num_glyphs);
-  OT::Supplier<OT::GlyphID> substitutes_supplier (substitutes, num_glyphs);
+  Supplier<OT::GlyphID> glyphs_supplier      (glyphs, num_glyphs);
+  Supplier<OT::GlyphID> substitutes_supplier (substitutes, num_glyphs);
 
   /* Each glyph takes four bytes max, and there's some overhead. */
   char buf[(SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1) * 4 + 128];
-  OT::hb_serialize_context_t c (buf, sizeof (buf));
+  hb_serialize_context_t c (buf, sizeof (buf));
   OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> ();
   bool ret = lookup->serialize_single (&c,
 				       OT::LookupFlag::IgnoreMarks,
@@ -155,15 +155,15 @@
   if (!num_ligatures)
     return nullptr;
 
-  OT::Supplier<OT::GlyphID>   first_glyphs_supplier                      (first_glyphs, num_first_glyphs);
-  OT::Supplier<unsigned int > ligature_per_first_glyph_count_supplier    (ligature_per_first_glyph_count_list, num_first_glyphs);
-  OT::Supplier<OT::GlyphID>   ligatures_supplier                         (ligature_list, num_ligatures);
-  OT::Supplier<unsigned int > component_count_supplier                   (component_count_list, num_ligatures);
-  OT::Supplier<OT::GlyphID>   component_supplier                         (component_list, num_ligatures);
+  Supplier<OT::GlyphID>   first_glyphs_supplier                      (first_glyphs, num_first_glyphs);
+  Supplier<unsigned int > ligature_per_first_glyph_count_supplier    (ligature_per_first_glyph_count_list, num_first_glyphs);
+  Supplier<OT::GlyphID>   ligatures_supplier                         (ligature_list, num_ligatures);
+  Supplier<unsigned int > component_count_supplier                   (component_count_list, num_ligatures);
+  Supplier<OT::GlyphID>   component_supplier                         (component_list, num_ligatures);
 
   /* 16 bytes per ligature ought to be enough... */
   char buf[ARRAY_LENGTH_CONST (ligature_list) * 16 + 128];
-  OT::hb_serialize_context_t c (buf, sizeof (buf));
+  hb_serialize_context_t c (buf, sizeof (buf));
   OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> ();
   bool ret = lookup->serialize_ligature (&c,
 					 OT::LookupFlag::IgnoreMarks,
@@ -205,8 +205,6 @@
   hb_ot_layout_lookup_accelerator_t accel_array[ARABIC_FALLBACK_MAX_LOOKUPS];
 };
 
-static const arabic_fallback_plan_t arabic_fallback_plan_nil = {};
-
 #if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(HB_NO_WIN1256)
 #define HB_WITH_WIN1256
 #endif
@@ -215,7 +213,8 @@
 #include "hb-ot-shape-complex-arabic-win1256.hh"
 #endif
 
-struct ManifestLookup {
+struct ManifestLookup
+{
   OT::Tag tag;
   OT::OffsetTo<OT::SubstLookup> lookupOffset;
 };
@@ -299,7 +298,7 @@
 {
   arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) calloc (1, sizeof (arabic_fallback_plan_t));
   if (unlikely (!fallback_plan))
-    return const_cast<arabic_fallback_plan_t *> (&arabic_fallback_plan_nil);
+    return const_cast<arabic_fallback_plan_t *> (&Null(arabic_fallback_plan_t));
 
   fallback_plan->num_lookups = 0;
   fallback_plan->free_lookups = false;
@@ -314,14 +313,15 @@
   if (arabic_fallback_plan_init_win1256 (fallback_plan, plan, font))
     return fallback_plan;
 
+  assert (fallback_plan->num_lookups == 0);
   free (fallback_plan);
-  return const_cast<arabic_fallback_plan_t *> (&arabic_fallback_plan_nil);
+  return const_cast<arabic_fallback_plan_t *> (&Null(arabic_fallback_plan_t));
 }
 
 static void
 arabic_fallback_plan_destroy (arabic_fallback_plan_t *fallback_plan)
 {
-  if (!fallback_plan || fallback_plan == &arabic_fallback_plan_nil)
+  if (!fallback_plan || fallback_plan->num_lookups == 0)
     return;
 
   for (unsigned int i = 0; i < fallback_plan->num_lookups; i++)
diff --git a/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc
index b1cba1c..f4b397b 100644
--- a/src/hb-ot-shape-complex-arabic.cc
+++ b/src/hb-ot-shape-complex-arabic.cc
@@ -250,7 +250,7 @@
    * mask_array[NONE] == 0. */
   hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1];
 
-  arabic_fallback_plan_t *fallback_plan;
+  hb_atomic_ptr_t<arabic_fallback_plan_t> fallback_plan;
 
   unsigned int do_fallback : 1;
   unsigned int has_stch : 1;
@@ -280,7 +280,7 @@
 {
   arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) data;
 
-  arabic_fallback_plan_destroy (arabic_plan->fallback_plan);
+  arabic_fallback_plan_destroy (arabic_plan->fallback_plan.get ());
 
   free (data);
 }
@@ -403,12 +403,13 @@
     return;
 
 retry:
-  arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) hb_atomic_ptr_get (&arabic_plan->fallback_plan);
+  arabic_fallback_plan_t *fallback_plan = arabic_plan->fallback_plan.get ();
   if (unlikely (!fallback_plan))
   {
     /* This sucks.  We need a font to build the fallback plan... */
     fallback_plan = arabic_fallback_plan_create (plan, font);
-    if (unlikely (!hb_atomic_ptr_cmpexch (&(const_cast<arabic_shape_plan_t *> (arabic_plan))->fallback_plan, nullptr, fallback_plan))) {
+    if (unlikely (!arabic_plan->fallback_plan.cmpexch (nullptr, fallback_plan)))
+    {
       arabic_fallback_plan_destroy (fallback_plan);
       goto retry;
     }
diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh
index 9554994..bb7fff3 100644
--- a/src/hb-ot-shape-complex-indic-private.hh
+++ b/src/hb-ot-shape-complex-indic-private.hh
@@ -300,7 +300,9 @@
   0x0CB0u, /* Kannada */
   0x0D30u, /* Malayalam */	/* No Reph, Logical Repha */
 
-  0x0DBBu, /* Sinhala */		/* Reph formed only with ZWJ */
+  0x0DBBu, /* Sinhala */	/* Reph formed only with ZWJ */
+
+  0x179Au, /* Khmer */
 };
 
 static inline bool
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index b8d9a3a..b52656f 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -251,10 +251,10 @@
 {
   ASSERT_POD ();
 
-  inline bool get_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const
+  inline bool load_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const
   {
-    hb_codepoint_t glyph = virama_glyph;
-    if (unlikely (virama_glyph == (hb_codepoint_t) -1))
+    hb_codepoint_t glyph = virama_glyph.get_relaxed ();
+    if (unlikely (glyph == (hb_codepoint_t) -1))
     {
       if (!config->virama || !font->get_nominal_glyph (config->virama, &glyph))
 	glyph = 0;
@@ -262,8 +262,8 @@
        * Maybe one day... */
 
       /* Our get_nominal_glyph() function needs a font, so we can't get the virama glyph
-       * during shape planning...  Instead, overwrite it here.  It's safe.  Don't worry! */
-      virama_glyph = glyph;
+       * during shape planning...  Instead, overwrite it here. */
+      virama_glyph.set_relaxed ((int) glyph);
     }
 
     *pglyph = glyph;
@@ -273,7 +273,7 @@
   const indic_config_t *config;
 
   bool is_old_spec;
-  mutable hb_codepoint_t virama_glyph;
+  mutable hb_atomic_int_t virama_glyph;
 
   would_substitute_feature_t rphf;
   would_substitute_feature_t pref;
@@ -298,7 +298,7 @@
     }
 
   indic_plan->is_old_spec = indic_plan->config->has_old_spec && ((plan->map.chosen_script[0] & 0x000000FFu) != '2');
-  indic_plan->virama_glyph = (hb_codepoint_t) -1;
+  indic_plan->virama_glyph.set_relaxed (-1);
 
   /* Use zero-context would_substitute() matching for new-spec of the main
    * Indic scripts, and scripts with one spec only, but not for old-specs.
@@ -419,7 +419,7 @@
     return;
 
   hb_codepoint_t virama;
-  if (indic_plan->get_virama_glyph (font, &virama))
+  if (indic_plan->load_virama_glyph (font, &virama))
   {
     hb_face_t *face = font->face;
     unsigned int count = buffer->len;
@@ -667,10 +667,10 @@
    * last consonant.
    *
    * Reports suggest that in some scripts Uniscribe does this only if there
-   * is *not* a Halant after last consonant already (eg. Kannada), while it
-   * does it unconditionally in other scripts (eg. Malayalam, Bengali).  We
-   * don't currently know about other scripts, so we whitelist Malayalam and
-   * Bengali for now.
+   * is *not* a Halant after last consonant already.  We know that is the
+   * case for Kannada, while it reorders unconditionally in other scripts,
+   * eg. Malayalam, Bengali, and Devanagari.  We don't currently know about
+   * other scripts, so we blacklist Kannada.
    *
    * Kannada test case:
    * U+0C9A,U+0CCD,U+0C9A,U+0CCD
@@ -681,15 +681,19 @@
    * U+0D38,U+0D4D,U+0D31,U+0D4D,U+0D31,U+0D4D
    * With lohit-ttf-20121122/Lohit-Malayalam.ttf
    *
-   * Bengali test case
+   * Bengali test case:
    * U+0998,U+09CD,U+09AF,U+09CD
    * With Windows XP vrinda.ttf
    * https://github.com/harfbuzz/harfbuzz/issues/1073
+   *
+   * Devanagari test case:
+   * U+091F,U+094D,U+0930,U+094D
+   * With chandas.ttf
+   * https://github.com/harfbuzz/harfbuzz/issues/1071
    */
   if (indic_plan->is_old_spec)
   {
-    bool disallow_double_halants = buffer->props.script != HB_SCRIPT_MALAYALAM &&
-				   buffer->props.script != HB_SCRIPT_BENGALI;
+    bool disallow_double_halants = buffer->props.script == HB_SCRIPT_KANNADA;
     for (unsigned int i = base + 1; i < end; i++)
       if (info[i].indic_category() == OT_H)
       {
@@ -1036,9 +1040,11 @@
    * phase, and that might have messed up our properties.  Recover
    * from a particular case of that where we're fairly sure that a
    * class of OT_H is desired but has been lost. */
-  if (indic_plan->virama_glyph)
+  /* We don't call load_virama_glyph(), since we know it's already
+   * loaded. */
+  hb_codepoint_t virama_glyph = indic_plan->virama_glyph.get_relaxed ();
+  if (virama_glyph)
   {
-    unsigned int virama_glyph = indic_plan->virama_glyph;
     for (unsigned int i = start; i < end; i++)
       if (info[i].codepoint == virama_glyph &&
 	  _hb_glyph_info_ligated (&info[i]) &&
diff --git a/src/hb-ot-shape-complex-khmer.cc b/src/hb-ot-shape-complex-khmer.cc
index 7876d36..264515e 100644
--- a/src/hb-ot-shape-complex-khmer.cc
+++ b/src/hb-ot-shape-complex-khmer.cc
@@ -42,7 +42,7 @@
 {
   /*
    * Basic features.
-   * These features are applied in order, one at a time, after initial_reordering.
+   * These features are applied in order, one at a time, after reordering.
    */
   {HB_TAG('p','r','e','f'), F_NONE},
   {HB_TAG('b','l','w','f'), F_NONE},
@@ -51,9 +51,7 @@
   {HB_TAG('c','f','a','r'), F_NONE},
   /*
    * Other features.
-   * These features are applied all at once, after final_reordering.
-   * Default Bengali font in Windows for example has intermixed
-   * lookups for init,pres,abvs,blws features.
+   * These features are applied all at once.
    */
   {HB_TAG('p','r','e','s'), F_GLOBAL},
   {HB_TAG('a','b','v','s'), F_GLOBAL},
@@ -92,13 +90,9 @@
 		 hb_font_t *font,
 		 hb_buffer_t *buffer);
 static void
-initial_reordering (const hb_ot_shape_plan_t *plan,
-		    hb_font_t *font,
-		    hb_buffer_t *buffer);
-static void
-final_reordering (const hb_ot_shape_plan_t *plan,
-		  hb_font_t *font,
-		  hb_buffer_t *buffer);
+reorder (const hb_ot_shape_plan_t *plan,
+	 hb_font_t *font,
+	 hb_buffer_t *buffer);
 static void
 clear_syllables (const hb_ot_shape_plan_t *plan,
 		 hb_font_t *font,
@@ -111,20 +105,28 @@
 
   /* Do this before any lookups have been applied. */
   map->add_gsub_pause (setup_syllables);
+  map->add_gsub_pause (reorder);
 
+  /* Testing suggests that Uniscribe does NOT pause between basic
+   * features.  Test with KhmerUI.ttf and the following three
+   * sequences:
+   *
+   *   U+1789,U+17BC
+   *   U+1789,U+17D2,U+1789
+   *   U+1789,U+17D2,U+1789,U+17BC
+   *
+   * https://github.com/harfbuzz/harfbuzz/issues/974
+   */
   map->add_global_bool_feature (HB_TAG('l','o','c','l'));
-  /* The Indic specs do not require ccmp, but we apply it here since if
-   * there is a use of it, it's typically at the beginning. */
   map->add_global_bool_feature (HB_TAG('c','c','m','p'));
 
-
   unsigned int i = 0;
-  map->add_gsub_pause (initial_reordering);
   for (; i < KHMER_BASIC_FEATURES; i++) {
     map->add_feature (khmer_features[i].tag, 1, khmer_features[i].flags | F_MANUAL_ZWJ | F_MANUAL_ZWNJ);
-    map->add_gsub_pause (nullptr);
   }
-  map->add_gsub_pause (final_reordering);
+
+  map->add_gsub_pause (clear_syllables);
+
   for (; i < KHMER_NUM_FEATURES; i++) {
     map->add_feature (khmer_features[i].tag, 1, khmer_features[i].flags | F_MANUAL_ZWJ | F_MANUAL_ZWNJ);
   }
@@ -132,7 +134,6 @@
   map->add_global_bool_feature (HB_TAG('c','a','l','t'));
   map->add_global_bool_feature (HB_TAG('c','l','i','g'));
 
-  map->add_gsub_pause (clear_syllables);
 }
 
 static void
@@ -264,162 +265,58 @@
     buffer->unsafe_to_break (start, end);
 }
 
-static int
-compare_khmer_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
-{
-  int a = pa->khmer_position();
-  int b = pb->khmer_position();
-
-  return a < b ? -1 : a == b ? 0 : +1;
-}
-
 
 /* Rules from:
  * https://docs.microsoft.com/en-us/typography/script-development/devanagari */
 
 static void
-initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
-				       hb_face_t *face,
-				       hb_buffer_t *buffer,
-				       unsigned int start, unsigned int end)
+reorder_consonant_syllable (const hb_ot_shape_plan_t *plan,
+			    hb_face_t *face,
+			    hb_buffer_t *buffer,
+			    unsigned int start, unsigned int end)
 {
   const khmer_shape_plan_t *khmer_plan = (const khmer_shape_plan_t *) plan->data;
   hb_glyph_info_t *info = buffer->info;
 
-  /* 1. Khmer shaping assumes that a syllable will begin with a Cons, IndV, or Number. */
-
-  /* The first consonant is always the base. */
-  unsigned int base = start;
-  info[base].khmer_position() = POS_BASE_C;
-
-  /* Mark all subsequent consonants as below. */
-  for (unsigned int i = base + 1; i < end; i++)
-    if (is_consonant_or_vowel (info[i]))
-      info[i].khmer_position() = POS_BELOW_C;
-
-  /* Mark final consonants.  A final consonant is one appearing after a matra,
-   * like in Khmer. */
-  for (unsigned int i = base + 1; i < end; i++)
-    if (info[i].khmer_category() == OT_M) {
-      for (unsigned int j = i + 1; j < end; j++)
-        if (is_consonant_or_vowel (info[j])) {
-	  info[j].khmer_position() = POS_FINAL_C;
-	  break;
-	}
-      break;
-    }
-
-  /* Attach misc marks to previous char to move with them. */
+  /* Setup masks. */
   {
-    khmer_position_t last_pos = POS_START;
-    for (unsigned int i = start; i < end; i++)
-    {
-      if ((FLAG_UNSAFE (info[i].khmer_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | MEDIAL_FLAGS | FLAG (OT_Coeng))))
-      {
-	info[i].khmer_position() = last_pos;
-	if (unlikely (info[i].khmer_category() == OT_Coeng &&
-		      info[i].khmer_position() == POS_PRE_M))
-	{
-	  /*
-	   * Uniscribe doesn't move the Halant with Left Matra.
-	   * TEST: U+092B,U+093F,U+094DE
-	   * We follow.  This is important for the Sinhala
-	   * U+0DDA split matra since it decomposes to U+0DD9,U+0DCA
-	   * where U+0DD9 is a left matra and U+0DCA is the virama.
-	   * We don't want to move the virama with the left matra.
-	   * TEST: U+0D9A,U+0DDA
-	   */
-	  for (unsigned int j = i; j > start; j--)
-	    if (info[j - 1].khmer_position() != POS_PRE_M) {
-	      info[i].khmer_position() = info[j - 1].khmer_position();
-	      break;
-	    }
-	}
-      } else if (info[i].khmer_position() != POS_SMVD) {
-        last_pos = (khmer_position_t) info[i].khmer_position();
-      }
-    }
-  }
-  /* For post-base consonants let them own anything before them
-   * since the last consonant or matra. */
-  {
-    unsigned int last = base;
-    for (unsigned int i = base + 1; i < end; i++)
-      if (is_consonant_or_vowel (info[i]))
-      {
-	for (unsigned int j = last + 1; j < i; j++)
-	  if (info[j].khmer_position() < POS_SMVD)
-	    info[j].khmer_position() = info[i].khmer_position();
-	last = i;
-      } else if (info[i].khmer_category() == OT_M)
-        last = i;
-  }
-
-  {
-    /* Use syllable() for sort accounting temporarily. */
-    unsigned int syllable = info[start].syllable();
-    for (unsigned int i = start; i < end; i++)
-      info[i].syllable() = i - start;
-
-    /* Sit tight, rock 'n roll! */
-    hb_stable_sort (info + start, end - start, compare_khmer_order);
-    /* Find base again */
-    base = end;
-    for (unsigned int i = start; i < end; i++)
-      if (info[i].khmer_position() == POS_BASE_C)
-      {
-	base = i;
-	break;
-      }
-
-    if (unlikely (end - start >= 127))
-      buffer->merge_clusters (start, end);
-    else
-      /* Note!  syllable() is a one-byte field. */
-      for (unsigned int i = base; i < end; i++)
-	if (info[i].syllable() != 255)
-	{
-	  unsigned int max = i;
-	  unsigned int j = start + info[i].syllable();
-	  while (j != i)
-	  {
-	    max = MAX (max, j);
-	    unsigned int next = start + info[j].syllable();
-	    info[j].syllable() = 255; /* So we don't process j later again. */
-	    j = next;
-	  }
-	  if (i != max)
-	    buffer->merge_clusters (i, max + 1);
-	}
-
-    /* Put syllable back in. */
-    for (unsigned int i = start; i < end; i++)
-      info[i].syllable() = syllable;
-  }
-
-  /* Setup masks now */
-
-  {
-    hb_mask_t mask;
-
     /* Post-base */
-    mask = khmer_plan->mask_array[BLWF] | khmer_plan->mask_array[ABVF] | khmer_plan->mask_array[PSTF];
-    for (unsigned int i = base + 1; i < end; i++)
+    hb_mask_t mask = khmer_plan->mask_array[BLWF] | khmer_plan->mask_array[ABVF] | khmer_plan->mask_array[PSTF];
+    for (unsigned int i = start + 1; i < end; i++)
       info[i].mask  |= mask;
   }
 
-  unsigned int pref_len = 2;
-  if (khmer_plan->mask_array[PREF] && base + pref_len < end)
+  unsigned int num_coengs = 0;
+  for (unsigned int i = start + 1; i < end; i++)
   {
-    /* Find a Halant,Ra sequence and mark it for pre-base-reordering processing. */
-    for (unsigned int i = base + 1; i + pref_len - 1 < end; i++) {
-      hb_codepoint_t glyphs[2];
-      for (unsigned int j = 0; j < pref_len; j++)
-        glyphs[j] = info[i + j].codepoint;
-      if (khmer_plan->pref.would_substitute (glyphs, pref_len, face))
+    /* """
+     * When a COENG + (Cons | IndV) combination are found (and subscript count
+     * is less than two) the character combination is handled according to the
+     * subscript type of the character following the COENG.
+     *
+     * ...
+     *
+     * Subscript Type 2 - The COENG + RO characters are reordered to immediately
+     * before the base glyph. Then the COENG + RO characters are assigned to have
+     * the 'pref' OpenType feature applied to them.
+     * """
+     */
+    if (info[i].khmer_category() == OT_Coeng && num_coengs <= 2 && i + 1 < end)
+    {
+      num_coengs++;
+
+      if (info[i + 1].khmer_category() == OT_Ra)
       {
-	for (unsigned int j = 0; j < pref_len; j++)
-	  info[i++].mask |= khmer_plan->mask_array[PREF];
+	for (unsigned int j = 0; j < 2; j++)
+	  info[i + j].mask |= khmer_plan->mask_array[PREF];
+
+	/* Move the Coeng,Ro sequence to the start. */
+	buffer->merge_clusters (start, i + 2);
+	hb_glyph_info_t t0 = info[i];
+	hb_glyph_info_t t1 = info[i + 1];
+	memmove (&info[start + 2], &info[start], (i - start) * sizeof (info[0]));
+	info[start] = t0;
+	info[start + 1] = t1;
 
 	/* Mark the subsequent stuff with 'cfar'.  Used in Khmer.
 	 * Read the feature spec.
@@ -428,12 +325,22 @@
 	 * U+1784,U+17D2,U+1782,U+17D2,U+179A
 	 */
 	if (khmer_plan->mask_array[CFAR])
-	  for (; i < end; i++)
-	    info[i].mask |= khmer_plan->mask_array[CFAR];
+	  for (unsigned int j = i + 2; j < end; j++)
+	    info[j].mask |= khmer_plan->mask_array[CFAR];
 
-	break;
+	num_coengs = 2; /* Done. */
       }
     }
+
+    /* Reorder left matra piece. */
+    else if (info[i].khmer_position() == POS_PRE_M)
+    {
+      /* Move to the start. */
+      buffer->merge_clusters (start, i + 1);
+      hb_glyph_info_t t = info[i];
+      memmove (&info[start + 1], &info[start], (i - start) * sizeof (info[0]));
+      info[start] = t;
+    }
   }
 }
 
@@ -448,7 +355,7 @@
   {
     case broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */
     case consonant_syllable:
-     initial_reordering_consonant_syllable (plan, face, buffer, start, end);
+     reorder_consonant_syllable (plan, face, buffer, start, end);
      break;
 
     case non_khmer_cluster:
@@ -518,263 +425,26 @@
 }
 
 static void
-initial_reordering (const hb_ot_shape_plan_t *plan,
-		    hb_font_t *font,
-		    hb_buffer_t *buffer)
+reorder (const hb_ot_shape_plan_t *plan,
+	 hb_font_t *font,
+	 hb_buffer_t *buffer)
 {
   insert_dotted_circles (plan, font, buffer);
 
   foreach_syllable (buffer, start, end)
     initial_reordering_syllable (plan, font->face, buffer, start, end);
-}
-
-static void
-final_reordering_syllable (const hb_ot_shape_plan_t *plan,
-			   hb_buffer_t *buffer,
-			   unsigned int start, unsigned int end)
-{
-  const khmer_shape_plan_t *khmer_plan = (const khmer_shape_plan_t *) plan->data;
-  hb_glyph_info_t *info = buffer->info;
-
-
-  /* This function relies heavily on halant glyphs.  Lots of ligation
-   * and possibly multiple substitutions happened prior to this
-   * phase, and that might have messed up our properties.  Recover
-   * from a particular case of that where we're fairly sure that a
-   * class of OT_Coeng is desired but has been lost. */
-  if (khmer_plan->virama_glyph)
-  {
-    unsigned int virama_glyph = khmer_plan->virama_glyph;
-    for (unsigned int i = start; i < end; i++)
-      if (info[i].codepoint == virama_glyph &&
-	  _hb_glyph_info_ligated (&info[i]) &&
-	  _hb_glyph_info_multiplied (&info[i]))
-      {
-        /* This will make sure that this glyph passes is_coeng() test. */
-	info[i].khmer_category() = OT_Coeng;
-	_hb_glyph_info_clear_ligated_and_multiplied (&info[i]);
-      }
-  }
-
-
-  /* 4. Final reordering:
-   *
-   * After the localized forms and basic shaping forms GSUB features have been
-   * applied (see below), the shaping engine performs some final glyph
-   * reordering before applying all the remaining font features to the entire
-   * syllable.
-   */
-
-  bool try_pref = !!khmer_plan->mask_array[PREF];
-
-  /* Find base again */
-  unsigned int base;
-  for (base = start; base < end; base++)
-    if (info[base].khmer_position() >= POS_BASE_C)
-    {
-      if (try_pref && base + 1 < end)
-      {
-	for (unsigned int i = base + 1; i < end; i++)
-	  if ((info[i].mask & khmer_plan->mask_array[PREF]) != 0)
-	  {
-	    if (!(_hb_glyph_info_substituted (&info[i]) &&
-		  _hb_glyph_info_ligated_and_didnt_multiply (&info[i])))
-	    {
-	      /* Ok, this was a 'pref' candidate but didn't form any.
-	       * Base is around here... */
-	      base = i;
-	      while (base < end && is_coeng (info[base]))
-		base++;
-	      info[base].khmer_position() = POS_BASE_C;
-
-	      try_pref = false;
-	    }
-	    break;
-	  }
-      }
-
-      if (start < base && info[base].khmer_position() > POS_BASE_C)
-        base--;
-      break;
-    }
-  if (base == end && start < base &&
-      is_one_of (info[base - 1], FLAG (OT_ZWJ)))
-    base--;
-  if (base < end)
-    while (start < base &&
-	   is_one_of (info[base], (FLAG (OT_N) | FLAG (OT_Coeng))))
-      base--;
-
-
-  /*   o Reorder matras:
-   *
-   *     If a pre-base matra character had been reordered before applying basic
-   *     features, the glyph can be moved closer to the main consonant based on
-   *     whether half-forms had been formed. Actual position for the matra is
-   *     defined as “after last standalone halant glyph, after initial matra
-   *     position and before the main consonant”. If ZWJ or ZWNJ follow this
-   *     halant, position is moved after it.
-   */
-
-  if (start + 1 < end && start < base) /* Otherwise there can't be any pre-base matra characters. */
-  {
-    /* If we lost track of base, alas, position before last thingy. */
-    unsigned int new_pos = base == end ? base - 2 : base - 1;
-
-    while (new_pos > start &&
-	   !(is_one_of (info[new_pos], (FLAG (OT_M) | FLAG (OT_Coeng)))))
-      new_pos--;
-
-    /* If we found no Halant we are done.
-     * Otherwise only proceed if the Halant does
-     * not belong to the Matra itself! */
-    if (is_coeng (info[new_pos]) &&
-	info[new_pos].khmer_position() != POS_PRE_M)
-    {
-      /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
-      if (new_pos + 1 < end && is_joiner (info[new_pos + 1]))
-	new_pos++;
-    }
-    else
-      new_pos = start; /* No move. */
-
-    if (start < new_pos && info[new_pos].khmer_position () != POS_PRE_M)
-    {
-      /* Now go see if there's actually any matras... */
-      for (unsigned int i = new_pos; i > start; i--)
-	if (info[i - 1].khmer_position () == POS_PRE_M)
-	{
-	  unsigned int old_pos = i - 1;
-	  if (old_pos < base && base <= new_pos) /* Shouldn't actually happen. */
-	    base--;
-
-	  hb_glyph_info_t tmp = info[old_pos];
-	  memmove (&info[old_pos], &info[old_pos + 1], (new_pos - old_pos) * sizeof (info[0]));
-	  info[new_pos] = tmp;
-
-	  /* Note: this merge_clusters() is intentionally *after* the reordering.
-	   * Indic matra reordering is special and tricky... */
-	  buffer->merge_clusters (new_pos, MIN (end, base + 1));
-
-	  new_pos--;
-	}
-    } else {
-      for (unsigned int i = start; i < base; i++)
-	if (info[i].khmer_position () == POS_PRE_M) {
-	  buffer->merge_clusters (i, MIN (end, base + 1));
-	  break;
-	}
-    }
-  }
-
-
-  /*   o Reorder pre-base-reordering consonants:
-   *
-   *     If a pre-base-reordering consonant is found, reorder it according to
-   *     the following rules:
-   */
-
-  if (try_pref && base + 1 < end) /* Otherwise there can't be any pre-base-reordering Ra. */
-  {
-    for (unsigned int i = base + 1; i < end; i++)
-      if ((info[i].mask & khmer_plan->mask_array[PREF]) != 0)
-      {
-	/*       1. Only reorder a glyph produced by substitution during application
-	 *          of the <pref> feature. (Note that a font may shape a Ra consonant with
-	 *          the feature generally but block it in certain contexts.)
-	 */
-        /* Note: We just check that something got substituted.  We don't check that
-	 * the <pref> feature actually did it...
-	 *
-	 * Reorder pref only if it ligated. */
-	if (_hb_glyph_info_ligated_and_didnt_multiply (&info[i]))
-	{
-	  /*
-	   *       2. Try to find a target position the same way as for pre-base matra.
-	   *          If it is found, reorder pre-base consonant glyph.
-	   *
-	   *       3. If position is not found, reorder immediately before main
-	   *          consonant.
-	   */
-
-	  unsigned int new_pos = base;
-	  while (new_pos > start &&
-		 !(is_one_of (info[new_pos - 1], FLAG(OT_M) | FLAG (OT_Coeng))))
-	    new_pos--;
-
-	  /* In Khmer coeng model, a H,Ra can go *after* matras.  If it goes after a
-	   * split matra, it should be reordered to *before* the left part of such matra. */
-	  if (new_pos > start && info[new_pos - 1].khmer_category() == OT_M)
-	  {
-	    unsigned int old_pos = i;
-	    for (unsigned int j = base + 1; j < old_pos; j++)
-	      if (info[j].khmer_category() == OT_M)
-	      {
-		new_pos--;
-		break;
-	      }
-	  }
-
-	  if (new_pos > start && is_coeng (info[new_pos - 1]))
-	  {
-	    /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
-	    if (new_pos < end && is_joiner (info[new_pos]))
-	      new_pos++;
-	  }
-
-	  {
-	    unsigned int old_pos = i;
-
-	    buffer->merge_clusters (new_pos, old_pos + 1);
-	    hb_glyph_info_t tmp = info[old_pos];
-	    memmove (&info[new_pos + 1], &info[new_pos], (old_pos - new_pos) * sizeof (info[0]));
-	    info[new_pos] = tmp;
-
-	    if (new_pos <= base && base < old_pos)
-	      base++;
-	  }
-	}
-
-        break;
-      }
-  }
-
-
-  /*
-   * Finish off the clusters and go home!
-   */
-  if (hb_options ().uniscribe_bug_compatible)
-  {
-    /* Uniscribe merges the entire syllable into a single cluster... Except for Tamil & Sinhala.
-     * This means, half forms are submerged into the main consonant's cluster.
-     * This is unnecessary, and makes cursor positioning harder, but that's what
-     * Uniscribe does. */
-    buffer->merge_clusters (start, end);
-  }
-}
-
-
-static void
-final_reordering (const hb_ot_shape_plan_t *plan,
-		  hb_font_t *font HB_UNUSED,
-		  hb_buffer_t *buffer)
-{
-  unsigned int count = buffer->len;
-  if (unlikely (!count)) return;
-
-  foreach_syllable (buffer, start, end)
-    final_reordering_syllable (plan, buffer, start, end);
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, khmer_category);
   HB_BUFFER_DEALLOCATE_VAR (buffer, khmer_position);
 }
 
-
 static void
 clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
 		 hb_font_t *font HB_UNUSED,
 		 hb_buffer_t *buffer)
 {
+  /* TODO: In USE, we clear syllables right after reorder.  Figure out
+   * what Uniscribe does. */
   hb_glyph_info_t *info = buffer->info;
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
diff --git a/src/hb-ot-shape-complex-private.hh b/src/hb-ot-shape-complex-private.hh
index ed6849b..37a4d91 100644
--- a/src/hb-ot-shape-complex-private.hh
+++ b/src/hb-ot-shape-complex-private.hh
@@ -279,20 +279,7 @@
 	return &_hb_ot_complex_shaper_indic;
 
     case HB_SCRIPT_KHMER:
-      /* A number of Khmer fonts in the wild don't have a 'pref' feature,
-       * and as such won't shape properly via the Indic shaper;
-       * however, they typically have 'liga' / 'clig' features that implement
-       * the necessary "reordering" by means of ligature substitutions.
-       * So we send such pref-less fonts through the generic shaper instead. */
-      if (planner->map.found_script[0] &&
-	  hb_ot_layout_language_find_feature (planner->face, HB_OT_TAG_GSUB,
-					      planner->map.script_index[0],
-					      planner->map.language_index[0],
-					      HB_TAG ('p','r','e','f'),
-					      nullptr))
 	return &_hb_ot_complex_shaper_khmer;
-      else
-	return &_hb_ot_complex_shaper_default;
 
     case HB_SCRIPT_MYANMAR:
       if (planner->map.chosen_script[0] == HB_TAG ('m','y','m','2'))
diff --git a/src/hb-ot-shape-complex-use-table.cc b/src/hb-ot-shape-complex-use-table.cc
index 25f7bf4..9c796f0 100644
--- a/src/hb-ot-shape-complex-use-table.cc
+++ b/src/hb-ot-shape-complex-use-table.cc
@@ -510,8 +510,8 @@
 
   /* 11100 */ VMAbv, VMAbv, VMAbv,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11110 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
-  /* 11120 */     B,     B,     B,     B,     B,     B,     B,  VAbv,  VAbv,  VAbv,  VBlw,  VBlw,  VPre,  VAbv,  VAbv,  VAbv,
-  /* 11130 */  VAbv,  VBlw,  VBlw,     H,  VAbv,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11120 */     B,     B,     B,     B,     B,     B,     B,  VBlw,  VBlw,  VBlw,  VAbv,  VAbv,  VPre,  VBlw,  VAbv,  VAbv,
+  /* 11130 */  VBlw,  VAbv,  VAbv,     H, CMAbv,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11140 */     O,     O,     O,     O,     B,  VPst,  VPst,     O,     O,     O,     O,     O,     O,     O,     O,     O,
 
   /* Mahajani */
diff --git a/src/hb-ot-shape-complex-use.cc b/src/hb-ot-shape-complex-use.cc
index 66b9571..f8e86c9 100644
--- a/src/hb-ot-shape-complex-use.cc
+++ b/src/hb-ot-shape-complex-use.cc
@@ -561,25 +561,6 @@
 }
 
 static bool
-decompose_use (const hb_ot_shape_normalize_context_t *c,
-                hb_codepoint_t  ab,
-                hb_codepoint_t *a,
-                hb_codepoint_t *b)
-{
-  switch (ab)
-  {
-    /* Chakma:
-     * Special case where the Unicode decomp gives matras in the wrong order
-     * for cluster validation.
-     */
-    case 0x1112Eu : *a = 0x11127u; *b= 0x11131u; return true;
-    case 0x1112Fu : *a = 0x11127u; *b= 0x11132u; return true;
-  }
-
-  return (bool) c->unicode->decompose (ab, a, b);
-}
-
-static bool
 compose_use (const hb_ot_shape_normalize_context_t *c,
 	     hb_codepoint_t  a,
 	     hb_codepoint_t  b,
@@ -602,7 +583,7 @@
   nullptr, /* preprocess_text */
   nullptr, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
-  decompose_use,
+  nullptr, /* decompose */
   compose_use,
   setup_masks_use,
   nullptr, /* disable_otl */
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 1189b20..71632b5 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -27,8 +27,8 @@
  */
 
 #define HB_SHAPER ot
-#define hb_ot_shaper_face_data_t hb_ot_layout_t
-#define hb_ot_shaper_shape_plan_data_t hb_ot_shape_plan_t
+#define hb_ot_face_data_t hb_ot_layout_t
+#define hb_ot_shape_plan_data_t hb_ot_shape_plan_t
 #include "hb-shaper-impl-private.hh"
 
 #include "hb-ot-shape-private.hh"
@@ -41,7 +41,7 @@
 #include "hb-set-private.hh"
 
 #include "hb-ot-layout-gsubgpos-private.hh"
-//#include "hb-aat-layout-private.hh"
+#include "hb-aat-layout-private.hh"
 
 static hb_tag_t common_features[] = {
   HB_TAG('c','c','m','p'),
@@ -132,14 +132,14 @@
 
 HB_SHAPER_DATA_ENSURE_DEFINE(ot, face)
 
-hb_ot_shaper_face_data_t *
+hb_ot_face_data_t *
 _hb_ot_shaper_face_data_create (hb_face_t *face)
 {
   return _hb_ot_layout_create (face);
 }
 
 void
-_hb_ot_shaper_face_data_destroy (hb_ot_shaper_face_data_t *data)
+_hb_ot_shaper_face_data_destroy (hb_ot_face_data_t *data)
 {
   _hb_ot_layout_destroy (data);
 }
@@ -151,16 +151,16 @@
 
 HB_SHAPER_DATA_ENSURE_DEFINE(ot, font)
 
-struct hb_ot_shaper_font_data_t {};
+struct hb_ot_font_data_t {};
 
-hb_ot_shaper_font_data_t *
+hb_ot_font_data_t *
 _hb_ot_shaper_font_data_create (hb_font_t *font HB_UNUSED)
 {
-  return (hb_ot_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_ot_shaper_font_data_destroy (hb_ot_shaper_font_data_t *data)
+_hb_ot_shaper_font_data_destroy (hb_ot_font_data_t *data)
 {
 }
 
@@ -169,7 +169,7 @@
  * shaper shape_plan data
  */
 
-hb_ot_shaper_shape_plan_data_t *
+hb_ot_shape_plan_data_t *
 _hb_ot_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan,
 				      const hb_feature_t *user_features,
 				      unsigned int        num_user_features,
@@ -194,14 +194,17 @@
   if (plan->shaper->data_create) {
     plan->data = plan->shaper->data_create (plan);
     if (unlikely (!plan->data))
+    {
+      free (plan);
       return nullptr;
+    }
   }
 
   return plan;
 }
 
 void
-_hb_ot_shaper_shape_plan_data_destroy (hb_ot_shaper_shape_plan_data_t *plan)
+_hb_ot_shaper_shape_plan_data_destroy (hb_ot_shape_plan_data_t *plan)
 {
   if (plan->shaper->data_destroy)
     plan->shaper->data_destroy (const_cast<void *> (plan->data));
@@ -623,8 +626,8 @@
 
   c->plan->substitute (c->font, buffer);
 
-  /* XXX Call morx instead. */
-  //hb_aat_layout_substitute (c->font, c->buffer);
+  if (0) /* XXX Call morx instead. */
+    hb_aat_layout_substitute (c->font, c->buffer);
 }
 
 static inline void
@@ -677,8 +680,8 @@
 
   if (HB_DIRECTION_IS_HORIZONTAL (direction))
   {
-    for (unsigned int i = 0; i < count; i++)
-      pos[i].x_advance = c->font->get_glyph_h_advance (info[i].codepoint);
+    c->font->get_glyph_h_advances (count, &info[0].codepoint, sizeof(info[0]),
+                                   &pos[0].x_advance, sizeof(pos[0]));
     /* The nil glyph_h_origin() func returns 0, so no need to apply it. */
     if (c->font->has_glyph_h_origin_func ())
       for (unsigned int i = 0; i < count; i++)
@@ -688,9 +691,10 @@
   }
   else
   {
+    c->font->get_glyph_v_advances (count, &info[0].codepoint, sizeof(info[0]),
+                                   &pos[0].y_advance, sizeof(pos[0]));
     for (unsigned int i = 0; i < count; i++)
     {
-      pos[i].y_advance = c->font->get_glyph_v_advance (info[i].codepoint);
       c->font->subtract_glyph_v_origin (info[i].codepoint,
 					&pos[i].x_offset,
 					&pos[i].y_offset);
diff --git a/src/hb-ot-var-fvar-table.hh b/src/hb-ot-var-fvar-table.hh
index 82d2996..101476e 100644
--- a/src/hb-ot-var-fvar-table.hh
+++ b/src/hb-ot-var-fvar-table.hh
@@ -87,6 +87,8 @@
 {
   static const hb_tag_t tableTag	= HB_OT_TAG_fvar;
 
+  inline bool has_data (void) const { return version.to_int () != 0; }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
diff --git a/src/hb-ot-var.cc b/src/hb-ot-var.cc
index f0612a6..6081ddf 100644
--- a/src/hb-ot-var.cc
+++ b/src/hb-ot-var.cc
@@ -41,14 +41,14 @@
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::fvar);
   hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-  return *(layout->fvar.get ());
+  return *(layout->table.fvar.get ());
 }
 static inline const OT::avar&
 _get_avar (hb_face_t *face)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::avar);
   hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-  return *(layout->avar.get ());
+  return *(layout->table.avar.get ());
 }
 
 /**
@@ -65,7 +65,7 @@
 hb_bool_t
 hb_ot_var_has_data (hb_face_t *face)
 {
-  return &_get_fvar (face) != &Null(OT::fvar);
+  return _get_fvar (face).has_data ();
 }
 
 /**
diff --git a/src/hb-private.hh b/src/hb-private.hh
index ff339df..efe7bda 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -84,19 +84,9 @@
 #endif
 
 
-/* Compiler attributes */
-
-
-template <typename T>
-struct _hb_alignof
-{
-  struct s
-  {
-    char c;
-    T t;
-  };
-  static constexpr unsigned int value = offsetof (s, t);
-};
+/*
+ * Compiler attributes
+ */
 
 #if __cplusplus < 201103L
 
@@ -108,7 +98,6 @@
 #define constexpr const
 #endif
 
-// Static assertions
 #ifndef static_assert
 #define static_assert(e, msg) \
 	HB_UNUSED typedef int HB_PASTE(static_assertion_failed_at_line_, __LINE__) [(e) ? 1 : -1]
@@ -122,11 +111,34 @@
 #define thread_local
 #endif
 
+template <typename T>
+struct _hb_alignof
+{
+  struct s
+  {
+    char c;
+    T t;
+  };
+  static constexpr size_t value = offsetof (s, t);
+};
 #ifndef alignof
 #define alignof(x) (_hb_alignof<x>::value)
-#endif // alignof
+#endif
 
-#endif // __cplusplus < 201103L
+/* https://github.com/harfbuzz/harfbuzz/issues/1127 */
+#ifndef explicit_operator
+#define explicit_operator
+#endif
+
+#else /* __cplusplus >= 201103L */
+
+/* https://github.com/harfbuzz/harfbuzz/issues/1127 */
+#ifndef explicit_operator
+#define explicit_operator explicit
+#endif
+
+#endif /* __cplusplus < 201103L */
+
 
 #if (defined(__GNUC__) || defined(__clang__)) && defined(__OPTIMIZE__)
 #define likely(expr) (__builtin_expect (!!(expr), 1))
@@ -161,7 +173,11 @@
 #ifndef HB_INTERNAL
 # if !defined(HB_NO_VISIBILITY) && !defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(_MSC_VER) && !defined(__SUNPRO_CC)
 #  define HB_INTERNAL __attribute__((__visibility__("hidden")))
-# else
+# elif defined(__MINGW32__)
+   /* We use -export-symbols on mingw32, since it does not support visibility
+    * attribute. */
+#  define HB_INTERNAL
+#else
 #  define HB_INTERNAL
 #  define HB_NO_VISIBILITY 1
 # endif
@@ -282,13 +298,12 @@
 #define HB_STMT_START do
 #define HB_STMT_END   while (0)
 
+/* Static-assert as expression. */
 template <unsigned int cond> class hb_assert_constant_t;
 template <> class hb_assert_constant_t<1> {};
-
 #define ASSERT_STATIC_EXPR_ZERO(_cond) (0 * (unsigned int) sizeof (hb_assert_constant_t<_cond>))
 
 /* Lets assert int types.  Saves trouble down the road. */
-
 static_assert ((sizeof (int8_t) == 1), "");
 static_assert ((sizeof (uint8_t) == 1), "");
 static_assert ((sizeof (int16_t) == 2), "");
@@ -297,7 +312,6 @@
 static_assert ((sizeof (uint32_t) == 4), "");
 static_assert ((sizeof (int64_t) == 8), "");
 static_assert ((sizeof (uint64_t) == 8), "");
-
 static_assert ((sizeof (hb_codepoint_t) == 4), "");
 static_assert ((sizeof (hb_position_t) == 4), "");
 static_assert ((sizeof (hb_mask_t) == 4), "");
@@ -334,95 +348,37 @@
   TypeName(const TypeName&); \
   void operator=(const TypeName&)
 
+
 /*
- * Static pools
+ * Compiler-assisted vectorization parameters.
  */
 
-/* Global nul-content Null pool.  Enlarge as necessary. */
+/*
+ * Disable vectorization for now.  To correctly use them, we should
+ * use posix_memalign() to allocate in hb_vector_t.  Otherwise, can
+ * cause misaligned access.
+ *
+ * https://bugs.chromium.org/p/chromium/issues/detail?id=860184
+ */
+#if !defined(HB_VECTOR_SIZE)
+#  define HB_VECTOR_SIZE 0
+#endif
 
-#define HB_NULL_POOL_SIZE 264
-static_assert (HB_NULL_POOL_SIZE % sizeof (void *) == 0, "Align HB_NULL_POOL_SIZE.");
-
-#ifdef HB_NO_VISIBILITY
-static
+/* The `vector_size' attribute was introduced in gcc 3.1. */
+#if !defined(HB_VECTOR_SIZE)
+#  if defined( __GNUC__ ) && ( __GNUC__ >= 4 )
+#    define HB_VECTOR_SIZE 128
+#  else
+#    define HB_VECTOR_SIZE 0
+#  endif
+#endif
+static_assert (0 == (HB_VECTOR_SIZE & (HB_VECTOR_SIZE - 1)), "HB_VECTOR_SIZE is not power of 2.");
+static_assert (0 == (HB_VECTOR_SIZE % 64), "HB_VECTOR_SIZE is not multiple of 64.");
+#if HB_VECTOR_SIZE
+typedef uint64_t hb_vector_size_impl_t __attribute__((vector_size (HB_VECTOR_SIZE / 8)));
 #else
-extern HB_INTERNAL
+typedef uint64_t hb_vector_size_impl_t;
 #endif
-void * const _hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)]
-#ifdef HB_NO_VISIBILITY
-= {}
-#endif
-;
-/* Generic nul-content Null objects. */
-template <typename Type>
-static inline Type const & Null (void) {
-  static_assert (sizeof (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
-  return *reinterpret_cast<Type const *> (_hb_NullPool);
-}
-#define Null(Type) Null<Type>()
-
-/* Specializaiton for arbitrary-content arbitrary-sized Null objects. */
-#define DEFINE_NULL_DATA(Namespace, Type, data) \
-} /* Close namespace. */ \
-static const char _Null##Type[sizeof (Namespace::Type) + 1] = data; /* +1 is for nul-termination in data */ \
-template <> \
-/*static*/ inline const Namespace::Type& Null<Namespace::Type> (void) { \
-  return *reinterpret_cast<const Namespace::Type *> (_Null##Type); \
-} \
-namespace Namespace { \
-/* The following line really exists such that we end in a place needing semicolon */ \
-static_assert (Namespace::Type::min_size + 1 <= sizeof (_Null##Type), "Null pool too small.  Enlarge.")
-
-
-/* Global writable pool.  Enlarge as necessary. */
-
-/* To be fully correct, CrapPool must be thread_local. However, we do not rely on CrapPool
- * for correct operation. It only exist to catch and divert program logic bugs instead of
- * causing bad memory access. So, races there are not actually introducing incorrectness
- * in the code. Has ~12kb binary size overhead to have it, also clang build fails with it. */
-#ifdef HB_NO_VISIBILITY
-static
-#else
-extern HB_INTERNAL
-#endif
-/*thread_local*/ void * _hb_CrapPool[HB_NULL_POOL_SIZE / sizeof (void *)]
-#ifdef HB_NO_VISIBILITY
-= {}
-#endif
-;
-/* CRAP pool: Common Region for Access Protection. */
-template <typename Type>
-static inline Type& Crap (void) {
-  static_assert (sizeof (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
-  Type *obj = reinterpret_cast<Type *> (_hb_CrapPool);
-  *obj = Null(Type);
-  return *obj;
-}
-#define Crap(Type) Crap<Type>()
-
-template <typename Type>
-struct CrapOrNull {
-  static inline Type & get (void) { return Crap(Type); }
-};
-template <typename Type>
-struct CrapOrNull<const Type> {
-  static inline Type const & get (void) { return Null(Type); }
-};
-#define CrapOrNull(Type) CrapOrNull<Type>::get ()
-
-
-/* ASCII tag/character handling */
-
-static inline bool ISALPHA (unsigned char c)
-{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
-static inline bool ISALNUM (unsigned char c)
-{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); }
-static inline bool ISSPACE (unsigned char c)
-{ return c == ' ' || c =='\f'|| c =='\n'|| c =='\r'|| c =='\t'|| c =='\v'; }
-static inline unsigned char TOUPPER (unsigned char c)
-{ return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; }
-static inline unsigned char TOLOWER (unsigned char c)
-{ return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; }
 
 
 /* HB_NDEBUG disables some sanity checks that are very safe to disable and
@@ -435,41 +391,7 @@
 #endif
 
 
-/* Misc */
-
-template <typename T> class hb_assert_unsigned_t;
-template <> class hb_assert_unsigned_t<unsigned char> {};
-template <> class hb_assert_unsigned_t<unsigned short> {};
-template <> class hb_assert_unsigned_t<unsigned int> {};
-template <> class hb_assert_unsigned_t<unsigned long> {};
-
-template <typename T> static inline bool
-hb_in_range (T u, T lo, T hi)
-{
-  /* The sizeof() is here to force template instantiation.
-   * I'm sure there are better ways to do this but can't think of
-   * one right now.  Declaring a variable won't work as HB_UNUSED
-   * is unusable on some platforms and unused types are less likely
-   * to generate a warning than unused variables. */
-  static_assert ((sizeof (hb_assert_unsigned_t<T>) >= 0), "");
-
-  /* The casts below are important as if T is smaller than int,
-   * the subtract results will become a signed int! */
-  return (T)(u - lo) <= (T)(hi - lo);
-}
-
-template <typename T> static inline bool
-hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2)
-{
-  return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2);
-}
-
-template <typename T> static inline bool
-hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3)
-{
-  return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3);
-}
-
+/* Flags */
 
 /* Enable bitwise ops on enums marked as flags_t */
 /* To my surprise, looks like the function resolver is happy to silently cast
@@ -493,7 +415,6 @@
 	  static inline T& operator ^= (T& l, T r) { l = l ^ r; return l; } \
 	}
 
-
 /* Useful for set-operations on small enums.
  * For example, for testing "x ∈ {x1, x2, x3}" use:
  * (FLAG_UNSAFE(x) & (FLAG(x1) | FLAG(x2) | FLAG(x3)))
@@ -503,58 +424,6 @@
 #define FLAG_RANGE(x,y) (ASSERT_STATIC_EXPR_ZERO ((x) < (y)) + FLAG(y+1) - FLAG(x))
 
 
-
-/* Compiler-assisted vectorization. */
-
-/*
- * Disable vectorization for now.  To correctly use them, we should
- * use posix_memalign() to allocate them.  Otherwise, can cause
- * misaligned access.
- *
- * https://bugs.chromium.org/p/chromium/issues/detail?id=860184
- */
-#if !defined(HB_VECTOR_SIZE)
-#  define HB_VECTOR_SIZE 0
-#endif
-
-/* The `vector_size' attribute was introduced in gcc 3.1. */
-#if !defined(HB_VECTOR_SIZE)
-#  if defined( __GNUC__ ) && ( __GNUC__ >= 4 )
-#    define HB_VECTOR_SIZE 128
-#  else
-#    define HB_VECTOR_SIZE 0
-#  endif
-#endif
-
-
-/* Global runtime options. */
-
-struct hb_options_t
-{
-  unsigned int initialized : 1;
-  unsigned int uniscribe_bug_compatible : 1;
-};
-
-union hb_options_union_t {
-  unsigned int i;
-  hb_options_t opts;
-};
-static_assert ((sizeof (int) == sizeof (hb_options_union_t)), "");
-
-HB_INTERNAL void
-_hb_options_init (void);
-
-extern HB_INTERNAL hb_options_union_t _hb_options;
-
-static inline hb_options_t
-hb_options (void)
-{
-  if (unlikely (!_hb_options.i))
-    _hb_options_init ();
-
-  return _hb_options.opts;
-}
-
 /* Size signifying variable-sized array */
 #define VAR 1
 
@@ -605,6 +474,7 @@
 #include "hb-debug.hh"
 #include "hb-dsalgs.hh"
 #include "hb-mutex-private.hh"
+#include "hb-null.hh"
 #include "hb-object-private.hh"
 
 #endif /* HB_PRIVATE_HH */
diff --git a/src/hb-shape-plan-private.hh b/src/hb-shape-plan-private.hh
index c2c4987..7d020ff 100644
--- a/src/hb-shape-plan-private.hh
+++ b/src/hb-shape-plan-private.hh
@@ -51,6 +51,7 @@
 
   struct hb_shaper_data_t shaper_data;
 };
+DECLARE_NULL_INSTANCE (hb_shape_plan_t);
 
 #define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS \
 	, const hb_feature_t *user_features \
diff --git a/src/hb-shape-plan.cc b/src/hb-shape-plan.cc
index 37ff1a6..0d61d9e 100644
--- a/src/hb-shape-plan.cc
+++ b/src/hb-shape-plan.cc
@@ -49,11 +49,13 @@
 
 #define HB_SHAPER_PLAN(shaper) \
 	HB_STMT_START { \
-	  if (hb_##shaper##_shaper_face_data_ensure (shape_plan->face_unsafe)) { \
-	    HB_SHAPER_DATA (shaper, shape_plan) = \
+	  if (hb_##shaper##_shaper_face_data_ensure (shape_plan->face_unsafe)) \
+	  { \
+	    /* XXX-MT-bug What happened to *ensure*ing this?!!!! */ \
+	    HB_SHAPER_DATA (shaper, shape_plan).set_relaxed ( \
 	      HB_SHAPER_DATA_CREATE_FUNC (shaper, shape_plan) (shape_plan, \
 							       user_features, num_user_features, \
-							       coords, num_coords); \
+							       coords, num_coords)); \
 	    shape_plan->shaper_func = _hb_##shaper##_shape; \
 	    shape_plan->shaper_name = #shaper; \
 	    return; \
@@ -88,6 +90,31 @@
  * hb_shape_plan_t
  */
 
+DEFINE_NULL_INSTANCE (hb_shape_plan_t) =
+{
+  HB_OBJECT_HEADER_STATIC,
+
+  true, /* default_shaper_list */
+  nullptr, /* face */
+  HB_SEGMENT_PROPERTIES_DEFAULT, /* props */
+
+  nullptr, /* shaper_func */
+  nullptr, /* shaper_name */
+
+  nullptr, /* user_features */
+  0,    /* num_user_featurs */
+
+  nullptr, /* coords */
+  0,    /* num_coords */
+
+  {
+#define HB_SHAPER_IMPLEMENT(shaper) HB_ATOMIC_PTR_INIT (HB_SHAPER_DATA_INVALID),
+#include "hb-shaper-list.hh"
+#undef HB_SHAPER_IMPLEMENT
+  },
+};
+
+
 /**
  * hb_shape_plan_create: (Xconstructor)
  * @face: 
@@ -188,30 +215,7 @@
 hb_shape_plan_t *
 hb_shape_plan_get_empty (void)
 {
-  static const hb_shape_plan_t _hb_shape_plan_nil = {
-    HB_OBJECT_HEADER_STATIC,
-
-    true, /* default_shaper_list */
-    nullptr, /* face */
-    HB_SEGMENT_PROPERTIES_DEFAULT, /* props */
-
-    nullptr, /* shaper_func */
-    nullptr, /* shaper_name */
-
-    nullptr, /* user_features */
-    0,    /* num_user_featurs */
-
-    nullptr, /* coords */
-    0,    /* num_coords */
-
-    {
-#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
-#include "hb-shaper-list.hh"
-#undef HB_SHAPER_IMPLEMENT
-    }
-  };
-
-  return const_cast<hb_shape_plan_t *> (&_hb_shape_plan_nil);
+  return const_cast<hb_shape_plan_t *> (&Null(hb_shape_plan_t));
 }
 
 /**
@@ -337,7 +341,7 @@
 
 #define HB_SHAPER_EXECUTE(shaper) \
 	HB_STMT_START { \
-	  return HB_SHAPER_DATA (shaper, shape_plan) && \
+	  return HB_SHAPER_DATA (shaper, shape_plan).get () && \
 		 hb_##shaper##_shaper_font_data_ensure (font) && \
 		 _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \
 	} HB_STMT_END
@@ -515,7 +519,7 @@
 
 
 retry:
-  hb_face_t::plan_node_t *cached_plan_nodes = (hb_face_t::plan_node_t *) hb_atomic_ptr_get (&face->shape_plans);
+  hb_face_t::plan_node_t *cached_plan_nodes = face->shape_plans.get ();
 
   /* Don't look for plan in the cache if there were variation coordinates XXX Fix me. */
   if (!hb_coords_present (coords, num_coords))
@@ -550,7 +554,8 @@
   node->shape_plan = shape_plan;
   node->next = cached_plan_nodes;
 
-  if (!hb_atomic_ptr_cmpexch (&face->shape_plans, cached_plan_nodes, node)) {
+  if (unlikely (!face->shape_plans.cmpexch (cached_plan_nodes, node)))
+  {
     hb_shape_plan_destroy (shape_plan);
     free (node);
     goto retry;
diff --git a/src/hb-shape.cc b/src/hb-shape.cc
index c1e7365..3c2e6c4 100644
--- a/src/hb-shape.cc
+++ b/src/hb-shape.cc
@@ -32,6 +32,7 @@
 #include "hb-shape-plan-private.hh"
 #include "hb-buffer-private.hh"
 #include "hb-font-private.hh"
+#include "hb-machinery-private.hh"
 
 /**
  * SECTION:hb-shape
@@ -45,21 +46,50 @@
  * contains the output glyphs and their positions.
  **/
 
-static const char **static_shaper_list;
+
+static void free_static_shaper_list (void);
+static const char *nil_shaper_list[] = {nullptr};
+
+static struct hb_shaper_list_lazy_loader_t : hb_lazy_loader_t<const char *,
+							      hb_shaper_list_lazy_loader_t>
+{
+  static inline const char ** create (void)
+  {
+    const char **shaper_list = (const char **) calloc (1 + HB_SHAPERS_COUNT, sizeof (const char *));
+    if (unlikely (!shaper_list))
+      return nullptr;
+
+    const hb_shaper_pair_t *shapers = _hb_shapers_get ();
+    unsigned int i;
+    for (i = 0; i < HB_SHAPERS_COUNT; i++)
+      shaper_list[i] = shapers[i].name;
+    shaper_list[i] = nullptr;
+
+#ifdef HB_USE_ATEXIT
+    atexit (free_static_shaper_list);
+#endif
+
+    return shaper_list;
+  }
+  static inline void destroy (const char **l)
+  {
+    free (l);
+  }
+  static inline const char ** get_null (void)
+  {
+    return nil_shaper_list;
+  }
+} static_shaper_list;
 
 #ifdef HB_USE_ATEXIT
 static
 void free_static_shaper_list (void)
 {
-retry:
-  const char **shaper_list = (const char **) hb_atomic_ptr_get (&static_shaper_list);
-  if (!hb_atomic_ptr_cmpexch (&static_shaper_list, shaper_list, nullptr))
-    goto retry;
-
-  free (shaper_list);
+  static_shaper_list.free_instance ();
 }
 #endif
 
+
 /**
  * hb_shape_list_shapers:
  *
@@ -73,35 +103,7 @@
 const char **
 hb_shape_list_shapers (void)
 {
-retry:
-  const char **shaper_list = (const char **) hb_atomic_ptr_get (&static_shaper_list);
-
-  if (unlikely (!shaper_list))
-  {
-    /* Not found; allocate one. */
-    shaper_list = (const char **) calloc (1 + HB_SHAPERS_COUNT, sizeof (const char *));
-    if (unlikely (!shaper_list)) {
-      static const char *nil_shaper_list[] = {nullptr};
-      return nil_shaper_list;
-    }
-
-    const hb_shaper_pair_t *shapers = _hb_shapers_get ();
-    unsigned int i;
-    for (i = 0; i < HB_SHAPERS_COUNT; i++)
-      shaper_list[i] = shapers[i].name;
-    shaper_list[i] = nullptr;
-
-    if (!hb_atomic_ptr_cmpexch (&static_shaper_list, nullptr, shaper_list)) {
-      free (shaper_list);
-      goto retry;
-    }
-
-#ifdef HB_USE_ATEXIT
-    atexit (free_static_shaper_list); /* First person registers atexit() callback. */
-#endif
-  }
-
-  return shaper_list;
+  return static_shaper_list.get_unconst ();
 }
 
 
diff --git a/src/hb-shaper-impl-private.hh b/src/hb-shaper-impl-private.hh
index 7844081..4a10279 100644
--- a/src/hb-shaper-impl-private.hh
+++ b/src/hb-shaper-impl-private.hh
@@ -36,7 +36,7 @@
 
 
 #ifdef HB_SHAPER
-#define HB_SHAPER_DATA_GET(object) HB_SHAPER_DATA (HB_SHAPER, object)
+#define HB_SHAPER_DATA_GET(object) HB_SHAPER_DATA (HB_SHAPER, object).get ()
 #endif
 
 
diff --git a/src/hb-shaper-private.hh b/src/hb-shaper-private.hh
index ce2d9f2..fb04bbc 100644
--- a/src/hb-shaper-private.hh
+++ b/src/hb-shaper-private.hh
@@ -49,25 +49,14 @@
 _hb_shapers_get (void);
 
 
-/* For embedding in face / font / ... */
-struct hb_shaper_data_t {
-#define HB_SHAPER_IMPLEMENT(shaper) void *shaper;
-#include "hb-shaper-list.hh"
-#undef HB_SHAPER_IMPLEMENT
-};
-
-#define HB_SHAPERS_COUNT (sizeof (hb_shaper_data_t) / sizeof (void *))
-
 /* Means: succeeded, but don't need to keep any data. */
 #define HB_SHAPER_DATA_SUCCEEDED ((void *) +1)
-
 /* Means: tried but failed to create. */
 #define HB_SHAPER_DATA_INVALID ((void *) -1)
-#define HB_SHAPER_DATA_IS_INVALID(data) ((void *) (data) == HB_SHAPER_DATA_INVALID)
 
-#define HB_SHAPER_DATA_TYPE_NAME(shaper, object)	hb_##shaper##_shaper_##object##_data_t
+#define HB_SHAPER_DATA_TYPE_NAME(shaper, object)	hb_##shaper##_##object##_data_t
 #define HB_SHAPER_DATA_TYPE(shaper, object)		struct HB_SHAPER_DATA_TYPE_NAME(shaper, object)
-#define HB_SHAPER_DATA_INSTANCE(shaper, object, instance)	(* (HB_SHAPER_DATA_TYPE(shaper, object) **) &(instance)->shaper_data.shaper)
+#define HB_SHAPER_DATA_INSTANCE(shaper, object, instance)	(* reinterpret_cast<hb_atomic_ptr_t<HB_SHAPER_DATA_TYPE(shaper, object) *> *> (&(instance)->shaper_data.shaper))
 #define HB_SHAPER_DATA(shaper, object)			HB_SHAPER_DATA_INSTANCE(shaper, object, object)
 #define HB_SHAPER_DATA_CREATE_FUNC(shaper, object)	_hb_##shaper##_shaper_##object##_data_create
 #define HB_SHAPER_DATA_DESTROY_FUNC(shaper, object)	_hb_##shaper##_shaper_##object##_data_destroy
@@ -83,7 +72,7 @@
 	HB_SHAPER_DATA_ENSURE_FUNC (shaper, object) (hb_##object##_t *object)
 
 #define HB_SHAPER_DATA_DESTROY(shaper, object) \
-    if (HB_SHAPER_DATA_TYPE (shaper, object) *data = HB_SHAPER_DATA (shaper, object)) \
+    if (HB_SHAPER_DATA_TYPE (shaper, object) *data = HB_SHAPER_DATA (shaper, object).get ()) \
       if (data != HB_SHAPER_DATA_INVALID && data != HB_SHAPER_DATA_SUCCEEDED) \
         HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (data);
 
@@ -95,12 +84,24 @@
 HB_SHAPER_DATA_ENSURE_FUNC(shaper, object) (hb_##object##_t *object) \
 {\
   retry: \
-  HB_SHAPER_DATA_TYPE (shaper, object) *data = (HB_SHAPER_DATA_TYPE (shaper, object) *) hb_atomic_ptr_get (&HB_SHAPER_DATA (shaper, object)); \
+  HB_SHAPER_DATA_TYPE (shaper, object) *data = HB_SHAPER_DATA (shaper, object).get (); \
   if (likely (data) && !(condition)) { \
+    /* XXX-MT-bug \
+     * Note that evaluating condition above can be dangerous if another thread \
+     * got here first and destructed data.  That's, as always, bad use pattern. \
+     * If you modify the font (change font size), other threads must not be \
+     * using it at the same time.  However, since this check is delayed to \
+     * when one actually tries to shape something, this is a XXX race condition \
+     * (and the only know we have that I know of) right now.  Ie. you modify the \
+     * font size in one thread, then (supposedly safely) try to use it from two \
+     * or more threads and BOOM!  I'm not sure how to fix this.  We want RCU. \
+     * Maybe when it doesn't matter when we finally implement AAT shaping, as
+     * this (condition) is currently only used by hb-coretext. */ \
     /* Drop and recreate. */ \
     /* If someone dropped it in the mean time, throw it away and don't touch it. \
      * Otherwise, destruct it. */ \
-    if (hb_atomic_ptr_cmpexch (&HB_SHAPER_DATA (shaper, object), data, nullptr)) { \
+    if (likely (HB_SHAPER_DATA (shaper, object).cmpexch (data, nullptr))) \
+    { \
       HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (data); \
     } \
     goto retry; \
@@ -109,7 +110,7 @@
     data = HB_SHAPER_DATA_CREATE_FUNC (shaper, object) (object); \
     if (unlikely (!data)) \
       data = (HB_SHAPER_DATA_TYPE (shaper, object) *) HB_SHAPER_DATA_INVALID; \
-    if (!hb_atomic_ptr_cmpexch (&HB_SHAPER_DATA (shaper, object), nullptr, data)) { \
+    if (unlikely (!HB_SHAPER_DATA (shaper, object).cmpexch (nullptr, data))) { \
       if (data && \
 	  data != HB_SHAPER_DATA_INVALID && \
 	  data != HB_SHAPER_DATA_SUCCEEDED) \
@@ -117,8 +118,17 @@
       goto retry; \
     } \
   } \
-  return data != nullptr && !HB_SHAPER_DATA_IS_INVALID (data); \
+  return data != nullptr && (void *) data != HB_SHAPER_DATA_INVALID; \
 }
 
 
+/* For embedding in face / font / ... */
+struct hb_shaper_data_t {
+#define HB_SHAPER_IMPLEMENT(shaper) hb_atomic_ptr_t<void *> shaper;
+#include "hb-shaper-list.hh"
+#undef HB_SHAPER_IMPLEMENT
+};
+#define HB_SHAPERS_COUNT (sizeof (hb_shaper_data_t) / sizeof (void *))
+
+
 #endif /* HB_SHAPER_PRIVATE_HH */
diff --git a/src/hb-shaper.cc b/src/hb-shaper.cc
index d44d8c9..e423f25 100644
--- a/src/hb-shaper.cc
+++ b/src/hb-shaper.cc
@@ -26,7 +26,7 @@
 
 #include "hb-private.hh"
 #include "hb-shaper-private.hh"
-#include "hb-atomic-private.hh"
+#include "hb-machinery-private.hh"
 
 
 static const hb_shaper_pair_t all_shapers[] = {
@@ -36,51 +36,28 @@
 };
 
 
-/* Thread-safe, lock-free, shapers */
+static void free_static_shapers (void);
 
-static const hb_shaper_pair_t *static_shapers;
-
-#ifdef HB_USE_ATEXIT
-static
-void free_static_shapers (void)
+static struct hb_shapers_lazy_loader_t : hb_lazy_loader_t<const hb_shaper_pair_t,
+							  hb_shapers_lazy_loader_t>
 {
-retry:
-  hb_shaper_pair_t *shapers = (hb_shaper_pair_t *) hb_atomic_ptr_get (&static_shapers);
-  if (!hb_atomic_ptr_cmpexch (&static_shapers, shapers, nullptr))
-    goto retry;
-
-  if (unlikely (shapers != all_shapers))
-    free ((void *) shapers);
-}
-#endif
-
-const hb_shaper_pair_t *
-_hb_shapers_get (void)
-{
-retry:
-  hb_shaper_pair_t *shapers = (hb_shaper_pair_t *) hb_atomic_ptr_get (&static_shapers);
-
-  if (unlikely (!shapers))
+  static inline hb_shaper_pair_t *create (void)
   {
     char *env = getenv ("HB_SHAPER_LIST");
-    if (!env || !*env) {
-      (void) hb_atomic_ptr_cmpexch (&static_shapers, nullptr, &all_shapers[0]);
-      return (const hb_shaper_pair_t *) all_shapers;
-    }
+    if (!env || !*env)
+      return nullptr;
 
-    /* Not found; allocate one. */
-    shapers = (hb_shaper_pair_t *) calloc (1, sizeof (all_shapers));
-    if (unlikely (!shapers)) {
-      (void) hb_atomic_ptr_cmpexch (&static_shapers, nullptr, &all_shapers[0]);
-      return (const hb_shaper_pair_t *) all_shapers;
-    }
+    hb_shaper_pair_t *shapers = (hb_shaper_pair_t *) calloc (1, sizeof (all_shapers));
+    if (unlikely (!shapers))
+      return nullptr;
 
     memcpy (shapers, all_shapers, sizeof (all_shapers));
 
      /* Reorder shaper list to prefer requested shapers. */
     unsigned int i = 0;
     char *end, *p = env;
-    for (;;) {
+    for (;;)
+    {
       end = strchr (p, ',');
       if (!end)
 	end = p + strlen (p);
@@ -102,15 +79,32 @@
 	p = end + 1;
     }
 
-    if (!hb_atomic_ptr_cmpexch (&static_shapers, nullptr, shapers)) {
-      free (shapers);
-      goto retry;
-    }
+#ifdef HB_USE_ATEXIT
+    atexit (free_static_shapers);
+#endif
+
+    return shapers;
+  }
+  static inline void destroy (const hb_shaper_pair_t *p)
+  {
+    free ((void *) p);
+  }
+  static inline const hb_shaper_pair_t *get_null (void)
+  {
+    return all_shapers;
+  }
+} static_shapers;
 
 #ifdef HB_USE_ATEXIT
-    atexit (free_static_shapers); /* First person registers atexit() callback. */
+static
+void free_static_shapers (void)
+{
+  static_shapers.free_instance ();
+}
 #endif
-  }
 
-  return shapers;
+const hb_shaper_pair_t *
+_hb_shapers_get (void)
+{
+  return static_shapers.get_unconst ();
 }
diff --git a/src/hb-static.cc b/src/hb-static.cc
index e26e5c8..ddecbba 100644
--- a/src/hb-static.cc
+++ b/src/hb-static.cc
@@ -26,7 +26,41 @@
 
 #include "hb-private.hh"
 
+#include "hb-open-type-private.hh"
+#include "hb-ot-layout-common-private.hh"
+
+#include "hb-face-private.hh"
+#include "hb-ot-head-table.hh"
+#include "hb-ot-maxp-table.hh"
+
 #ifndef HB_NO_VISIBILITY
-void * const _hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {};
-/*thread_local*/ void * _hb_CrapPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {};
+
+hb_vector_size_impl_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)] = {};
+/*thread_local*/ hb_vector_size_impl_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)] = {};
+
+DEFINE_NULL_NAMESPACE_BYTES (OT, Index) =  {0xFF,0xFF};
+DEFINE_NULL_NAMESPACE_BYTES (OT, LangSys) = {0x00,0x00, 0xFF,0xFF, 0x00,0x00};
+DEFINE_NULL_NAMESPACE_BYTES (OT, RangeRecord) = {0x00,0x01, 0x00,0x00, 0x00, 0x00};
+
+
+void
+hb_face_t::load_num_glyphs (void) const
+{
+  hb_sanitize_context_t c = hb_sanitize_context_t ();
+  c.set_num_glyphs (0); /* So we don't recurse ad infinitum. */
+  hb_blob_t *maxp_blob = c.reference_table<OT::maxp> (this);
+  const OT::maxp *maxp_table = maxp_blob->as<OT::maxp> ();
+  num_glyphs = maxp_table->get_num_glyphs ();
+  hb_blob_destroy (maxp_blob);
+}
+
+void
+hb_face_t::load_upem (void) const
+{
+  hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table<OT::head> (this);
+  const OT::head *head_table = head_blob->as<OT::head> ();
+  upem = head_table->get_upem ();
+  hb_blob_destroy (head_blob);
+}
+
 #endif
diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc
index c8fa39b..36af3be 100644
--- a/src/hb-subset-glyf.cc
+++ b/src/hb-subset-glyf.cc
@@ -292,7 +292,7 @@
                          hb_blob_t       **glyf_prime, /* OUT */
                          hb_blob_t       **loca_prime /* OUT */)
 {
-  hb_blob_t *glyf_blob = OT::Sanitizer<OT::glyf>().sanitize (plan->source->reference_table (HB_OT_TAG_glyf));
+  hb_blob_t *glyf_blob = hb_sanitize_context_t ().reference_table<OT::glyf> (plan->source);
   const char *glyf_data = hb_blob_get_data(glyf_blob, nullptr);
 
   OT::glyf::accelerator_t glyf;
diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc
index 55c4e3f..1256682 100644
--- a/src/hb-subset-plan.cc
+++ b/src/hb-subset-plan.cc
@@ -56,11 +56,15 @@
 static void
 _gsub_closure (hb_face_t *face, hb_set_t *gids_to_retain)
 {
-  // TODO(grieger): This uses all lookups, instead collect
-  //                the set of lookups that are relevant.
-  //                See fontTools implementation.
+  hb_auto_t<hb_set_t> lookup_indices;
+  hb_ot_layout_collect_lookups (face,
+                                HB_OT_TAG_GSUB,
+                                nullptr,
+                                nullptr,
+                                nullptr,
+                                &lookup_indices);
   hb_ot_layout_lookups_substitute_closure (face,
-                                           nullptr,
+                                           &lookup_indices,
                                            gids_to_retain);
 }
 
diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index a65b58d..411c6b8 100644
--- a/src/hb-subset.cc
+++ b/src/hb-subset.cc
@@ -78,14 +78,12 @@
 static bool
 _subset (hb_subset_plan_t *plan)
 {
-  OT::Sanitizer<TableType> sanitizer;
-
-  hb_blob_t *source_blob = sanitizer.sanitize (plan->source->reference_table (TableType::tableTag));
+  hb_blob_t *source_blob = hb_sanitize_context_t ().reference_table<TableType> (plan->source);
   const TableType *table = source_blob->as<TableType> ();
 
   hb_tag_t tag = TableType::tableTag;
   hb_bool_t result = false;
-  if (table != &Null(TableType))
+  if (source_blob->data)
   {
     result = table->subset(plan);
   } else {
@@ -159,14 +157,14 @@
   if (unlikely (!buf))
     return nullptr;
 
-  OT::hb_serialize_context_t c (buf, face_length);
+  hb_serialize_context_t c (buf, face_length);
   OT::OpenTypeFontFile *f = c.start_serialize<OT::OpenTypeFontFile> ();
 
   bool is_cff = data->tables.lsearch (HB_TAG ('C','F','F',' ')) || data->tables.lsearch (HB_TAG ('C','F','F','2'));
   hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag;
 
-  OT::Supplier<hb_tag_t>    tags_supplier  (&data->tables[0].tag, table_count, sizeof (data->tables[0]));
-  OT::Supplier<hb_blob_t *> blobs_supplier (&data->tables[0].blob, table_count, sizeof (data->tables[0]));
+  Supplier<hb_tag_t>    tags_supplier  (&data->tables[0].tag, table_count, sizeof (data->tables[0]));
+  Supplier<hb_blob_t *> blobs_supplier (&data->tables[0].blob, table_count, sizeof (data->tables[0]));
   bool ret = f->serialize_single (&c,
 				  sfnt_tag,
 				  tags_supplier,
diff --git a/src/hb-ucdn.cc b/src/hb-ucdn.cc
index 2c08718..56d13e7 100644
--- a/src/hb-ucdn.cc
+++ b/src/hb-ucdn.cc
@@ -17,6 +17,7 @@
 #include "hb-private.hh"
 
 #include "hb-unicode-private.hh"
+#include "hb-machinery-private.hh"
 
 #include "ucdn.h"
 
@@ -238,31 +239,14 @@
     return ucdn_compat_decompose(u, decomposed);
 }
 
-static hb_unicode_funcs_t *static_ucdn_funcs = nullptr;
 
-#ifdef HB_USE_ATEXIT
-static
-void free_static_ucdn_funcs (void)
+static void free_static_ucdn_funcs (void);
+
+static struct hb_ucdn_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t<hb_ucdn_unicode_funcs_lazy_loader_t>
 {
-retry:
-  hb_unicode_funcs_t *ucdn_funcs = (hb_unicode_funcs_t *) hb_atomic_ptr_get (&static_ucdn_funcs);
-  if (!hb_atomic_ptr_cmpexch (&static_ucdn_funcs, ucdn_funcs, nullptr))
-    goto retry;
-
-  hb_unicode_funcs_destroy (ucdn_funcs);
-}
-#endif
-
-extern "C" HB_INTERNAL
-hb_unicode_funcs_t *
-hb_ucdn_get_unicode_funcs (void)
-{
-retry:
-  hb_unicode_funcs_t *funcs = (hb_unicode_funcs_t *) hb_atomic_ptr_get (&static_ucdn_funcs);
-
-  if (unlikely (!funcs))
+  static inline hb_unicode_funcs_t *create (void)
   {
-    funcs = hb_unicode_funcs_create (nullptr);
+    hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr);
 
 #define HB_UNICODE_FUNC_IMPLEMENT(name) \
     hb_unicode_funcs_set_##name##_func (funcs, hb_ucdn_##name, nullptr, nullptr);
@@ -271,15 +255,25 @@
 
     hb_unicode_funcs_make_immutable (funcs);
 
-    if (!hb_atomic_ptr_cmpexch (&static_ucdn_funcs, nullptr, funcs)) {
-      hb_unicode_funcs_destroy (funcs);
-      goto retry;
-    }
+#ifdef HB_USE_ATEXIT
+    atexit (free_static_ucdn_funcs);
+#endif
+
+    return funcs;
+  }
+} static_ucdn_funcs;
 
 #ifdef HB_USE_ATEXIT
-    atexit (free_static_ucdn_funcs); /* First person registers atexit() callback. */
+static
+void free_static_ucdn_funcs (void)
+{
+  static_ucdn_funcs.free_instance ();
+}
 #endif
-  };
 
-  return hb_unicode_funcs_reference (funcs);
+extern "C" HB_INTERNAL
+hb_unicode_funcs_t *
+hb_ucdn_get_unicode_funcs (void)
+{
+  return static_ucdn_funcs.get_unconst ();
 }
diff --git a/src/hb-unicode-private.hh b/src/hb-unicode-private.hh
index b2c203d..fb16ba4 100644
--- a/src/hb-unicode-private.hh
+++ b/src/hb-unicode-private.hh
@@ -60,7 +60,8 @@
   HB_UNICODE_FUNC_IMPLEMENT (hb_script_t, script) \
   /* ^--- Add new simple callbacks here */
 
-struct hb_unicode_funcs_t {
+struct hb_unicode_funcs_t
+{
   hb_object_header_t header;
   ASSERT_POD ();
 
@@ -263,9 +264,7 @@
 #undef HB_UNICODE_FUNC_IMPLEMENT
   } destroy;
 };
-
-
-extern HB_INTERNAL const hb_unicode_funcs_t _hb_unicode_funcs_nil;
+DECLARE_NULL_INSTANCE (hb_unicode_funcs_t);
 
 
 /* Modified combining marks */
diff --git a/src/hb-unicode.cc b/src/hb-unicode.cc
index 2d16c2e..8cb4172 100644
--- a/src/hb-unicode.cc
+++ b/src/hb-unicode.cc
@@ -185,7 +185,8 @@
 }
 
 
-const hb_unicode_funcs_t _hb_unicode_funcs_nil = {
+DEFINE_NULL_INSTANCE (hb_unicode_funcs_t) =
+{
   HB_OBJECT_HEADER_STATIC,
 
   nullptr, /* parent */
@@ -209,7 +210,7 @@
 hb_unicode_funcs_t *
 hb_unicode_funcs_get_empty (void)
 {
-  return const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil);
+  return const_cast<hb_unicode_funcs_t *> (&Null(hb_unicode_funcs_t));
 }
 
 /**
diff --git a/src/hb-uniscribe.cc b/src/hb-uniscribe.cc
index b17fa31..11b7384 100644
--- a/src/hb-uniscribe.cc
+++ b/src/hb-uniscribe.cc
@@ -190,7 +190,8 @@
 }
 
 
-struct hb_uniscribe_shaper_funcs_t {
+struct hb_uniscribe_shaper_funcs_t
+{
   SIOT ScriptItemizeOpenType;
   SSOT ScriptShapeOpenType;
   SPOT ScriptPlaceOpenType;
@@ -220,47 +221,49 @@
     }
   }
 };
-static hb_uniscribe_shaper_funcs_t *uniscribe_funcs;
+
+
+static void free_static_uniscribe_shaper_funcs (void);
+
+static struct hb_uniscribe_shaper_funcs_lazy_loader_t : hb_lazy_loader_t<hb_uniscribe_shaper_funcs_t,
+									 hb_uniscribe_shaper_funcs_lazy_loader_t>
+{
+  static inline hb_uniscribe_shaper_funcs_t *create (void)
+  {
+    hb_uniscribe_shaper_funcs_t *funcs = (hb_uniscribe_shaper_funcs_t *) calloc (1, sizeof (hb_uniscribe_shaper_funcs_t));
+    if (unlikely (!funcs))
+      return nullptr;
+
+    funcs->init ();
 
 #ifdef HB_USE_ATEXIT
-static inline void
-free_uniscribe_funcs (void)
-{
-retry:
-  hb_uniscribe_shaper_funcs_t *local_uniscribe_funcs =
-    (hb_uniscribe_shaper_funcs_t *) hb_atomic_ptr_get (&uniscribe_funcs);
-  if (!hb_atomic_ptr_cmpexch (&uniscribe_funcs, local_uniscribe_funcs, nullptr))
-    goto retry;
+    atexit (free_static_uniscribe_shaper_funcs);
+#endif
 
-  free (uniscribe_funcs);
+    return funcs;
+  }
+  static inline void destroy (hb_uniscribe_shaper_funcs_t *p)
+  {
+    free ((void *) p);
+  }
+  static inline hb_uniscribe_shaper_funcs_t *get_null (void)
+  {
+    return nullptr;
+  }
+} static_uniscribe_shaper_funcs;
+
+#ifdef HB_USE_ATEXIT
+static
+void free_static_uniscribe_shaper_funcs (void)
+{
+  static_uniscribe_shaper_funcs.free_instance ();
 }
 #endif
 
 static hb_uniscribe_shaper_funcs_t *
 hb_uniscribe_shaper_get_funcs (void)
 {
-retry:
-  hb_uniscribe_shaper_funcs_t *funcs = (hb_uniscribe_shaper_funcs_t *) hb_atomic_ptr_get (&uniscribe_funcs);
-
-  if (unlikely (!funcs))
-  {
-    funcs = (hb_uniscribe_shaper_funcs_t *) calloc (1, sizeof (hb_uniscribe_shaper_funcs_t));
-    if (unlikely (!funcs))
-      return nullptr;
-
-    funcs->init ();
-
-    if (!hb_atomic_ptr_cmpexch (&uniscribe_funcs, nullptr, funcs)) {
-      free (funcs);
-      goto retry;
-    }
-
-#ifdef HB_USE_ATEXIT
-    atexit (free_uniscribe_funcs); /* First person registers atexit() callback. */
-#endif
-  }
-
-  return funcs;
+  return static_uniscribe_shaper_funcs.get_unconst ();
 }
 
 
@@ -309,7 +312,7 @@
  * shaper face data
  */
 
-struct hb_uniscribe_shaper_face_data_t {
+struct hb_uniscribe_face_data_t {
   HANDLE fh;
   hb_uniscribe_shaper_funcs_t *funcs;
   wchar_t face_name[LF_FACESIZE];
@@ -358,7 +361,7 @@
    * full, PS. All of them point to the same name data with our unique name.
    */
 
-  blob = OT::Sanitizer<OT::OpenTypeFontFile>().sanitize (blob);
+  blob = hb_sanitize_context_t ().sanitize_blob<OT::OpenTypeFontFile> (blob);
 
   unsigned int length, new_length, name_str_len;
   const char *orig_sfnt_data = hb_blob_get_data (blob, &length);
@@ -383,7 +386,7 @@
 
   memcpy(new_sfnt_data, orig_sfnt_data, length);
 
-  OT::name &name = OT::StructAtOffset<OT::name> (new_sfnt_data, name_table_offset);
+  OT::name &name = StructAtOffset<OT::name> (new_sfnt_data, name_table_offset);
   name.format.set (0);
   name.count.set (ARRAY_LENGTH (name_IDs));
   name.stringOffset.set (name.get_size ());
@@ -399,7 +402,7 @@
   }
 
   /* Copy string data from new_name, converting wchar_t to UTF16BE. */
-  unsigned char *p = &OT::StructAfter<unsigned char> (name);
+  unsigned char *p = &StructAfter<unsigned char> (name);
   for (unsigned int i = 0; i < name_str_len; i++)
   {
     *p++ = new_name[i] >> 8;
@@ -439,10 +442,10 @@
 			 HB_MEMORY_MODE_WRITABLE, nullptr, free);
 }
 
-hb_uniscribe_shaper_face_data_t *
+hb_uniscribe_face_data_t *
 _hb_uniscribe_shaper_face_data_create (hb_face_t *face)
 {
-  hb_uniscribe_shaper_face_data_t *data = (hb_uniscribe_shaper_face_data_t *) calloc (1, sizeof (hb_uniscribe_shaper_face_data_t));
+  hb_uniscribe_face_data_t *data = (hb_uniscribe_face_data_t *) calloc (1, sizeof (hb_uniscribe_face_data_t));
   if (unlikely (!data))
     return nullptr;
 
@@ -479,7 +482,7 @@
 }
 
 void
-_hb_uniscribe_shaper_face_data_destroy (hb_uniscribe_shaper_face_data_t *data)
+_hb_uniscribe_shaper_face_data_destroy (hb_uniscribe_face_data_t *data)
 {
   RemoveFontMemResourceEx (data->fh);
   free (data);
@@ -490,7 +493,7 @@
  * shaper font data
  */
 
-struct hb_uniscribe_shaper_font_data_t {
+struct hb_uniscribe_font_data_t {
   HDC hdc;
   LOGFONTW log_font;
   HFONT hfont;
@@ -508,19 +511,19 @@
   lf->lfCharSet = DEFAULT_CHARSET;
 
   hb_face_t *face = font->face;
-  hb_uniscribe_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
+  hb_uniscribe_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
 
   memcpy (lf->lfFaceName, face_data->face_name, sizeof (lf->lfFaceName));
 
   return true;
 }
 
-hb_uniscribe_shaper_font_data_t *
+hb_uniscribe_font_data_t *
 _hb_uniscribe_shaper_font_data_create (hb_font_t *font)
 {
   if (unlikely (!hb_uniscribe_shaper_face_data_ensure (font->face))) return nullptr;
 
-  hb_uniscribe_shaper_font_data_t *data = (hb_uniscribe_shaper_font_data_t *) calloc (1, sizeof (hb_uniscribe_shaper_font_data_t));
+  hb_uniscribe_font_data_t *data = (hb_uniscribe_font_data_t *) calloc (1, sizeof (hb_uniscribe_font_data_t));
   if (unlikely (!data))
     return nullptr;
 
@@ -559,7 +562,7 @@
 }
 
 void
-_hb_uniscribe_shaper_font_data_destroy (hb_uniscribe_shaper_font_data_t *data)
+_hb_uniscribe_shaper_font_data_destroy (hb_uniscribe_font_data_t *data)
 {
   if (data->hdc)
     ReleaseDC (nullptr, data->hdc);
@@ -574,7 +577,7 @@
 hb_uniscribe_font_get_logfontw (hb_font_t *font)
 {
   if (unlikely (!hb_uniscribe_shaper_font_data_ensure (font))) return nullptr;
-  hb_uniscribe_shaper_font_data_t *font_data =  HB_SHAPER_DATA_GET (font);
+  hb_uniscribe_font_data_t *font_data =  HB_SHAPER_DATA_GET (font);
   return &font_data->log_font;
 }
 
@@ -582,7 +585,7 @@
 hb_uniscribe_font_get_hfont (hb_font_t *font)
 {
   if (unlikely (!hb_uniscribe_shaper_font_data_ensure (font))) return nullptr;
-  hb_uniscribe_shaper_font_data_t *font_data =  HB_SHAPER_DATA_GET (font);
+  hb_uniscribe_font_data_t *font_data =  HB_SHAPER_DATA_GET (font);
   return font_data->hfont;
 }
 
@@ -591,20 +594,20 @@
  * shaper shape_plan data
  */
 
-struct hb_uniscribe_shaper_shape_plan_data_t {};
+struct hb_uniscribe_shape_plan_data_t {};
 
-hb_uniscribe_shaper_shape_plan_data_t *
+hb_uniscribe_shape_plan_data_t *
 _hb_uniscribe_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
 					     const hb_feature_t *user_features HB_UNUSED,
 					     unsigned int        num_user_features HB_UNUSED,
 					     const int          *coords HB_UNUSED,
 					     unsigned int        num_coords HB_UNUSED)
 {
-  return (hb_uniscribe_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_uniscribe_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_uniscribe_shaper_shape_plan_data_destroy (hb_uniscribe_shaper_shape_plan_data_t *data HB_UNUSED)
+_hb_uniscribe_shaper_shape_plan_data_destroy (hb_uniscribe_shape_plan_data_t *data HB_UNUSED)
 {
 }
 
@@ -622,8 +625,8 @@
 		     unsigned int        num_features)
 {
   hb_face_t *face = font->face;
-  hb_uniscribe_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
-  hb_uniscribe_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
+  hb_uniscribe_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
+  hb_uniscribe_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
   hb_uniscribe_shaper_funcs_t *funcs = face_data->funcs;
 
   /*
diff --git a/src/hb-vector-private.hh b/src/hb-vector-private.hh
new file mode 100644
index 0000000..1ef20d4
--- /dev/null
+++ b/src/hb-vector-private.hh
@@ -0,0 +1,238 @@
+/*
+ * Copyright © 2017,2018  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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_VECTOR_PRIVATE_HH
+#define HB_VECTOR_PRIVATE_HH
+
+#include "hb-private.hh"
+
+
+template <typename Type, unsigned int StaticSize=8>
+struct hb_vector_t
+{
+  unsigned int len;
+  unsigned int allocated; /* == 0 means allocation failed. */
+  Type *arrayZ;
+  Type static_array[StaticSize];
+
+  void init (void)
+  {
+    len = 0;
+    allocated = ARRAY_LENGTH (static_array);
+    arrayZ = static_array;
+  }
+
+  inline Type& operator [] (unsigned int i)
+  {
+    if (unlikely (i >= len))
+      return Crap (Type);
+    return arrayZ[i];
+  }
+  inline const Type& operator [] (unsigned int i) const
+  {
+    if (unlikely (i >= len))
+      return Null(Type);
+    return arrayZ[i];
+  }
+
+  inline Type *push (void)
+  {
+    if (unlikely (!resize (len + 1)))
+      return &Crap(Type);
+    return &arrayZ[len - 1];
+  }
+  inline Type *push (const Type& v)
+  {
+    Type *p = push ();
+    *p = v;
+    return p;
+  }
+
+  /* Allocate for size but don't adjust len. */
+  inline bool alloc (unsigned int size)
+  {
+    if (unlikely (!allocated))
+      return false;
+
+    if (likely (size <= allocated))
+      return true;
+
+    /* Reallocate */
+
+    unsigned int new_allocated = allocated;
+    while (size >= new_allocated)
+      new_allocated += (new_allocated >> 1) + 8;
+
+    Type *new_array = nullptr;
+
+    if (arrayZ == static_array)
+    {
+      new_array = (Type *) calloc (new_allocated, sizeof (Type));
+      if (new_array)
+        memcpy (new_array, arrayZ, len * sizeof (Type));
+    }
+    else
+    {
+      bool overflows = (new_allocated < allocated) || hb_unsigned_mul_overflows (new_allocated, sizeof (Type));
+      if (likely (!overflows))
+        new_array = (Type *) realloc (arrayZ, new_allocated * sizeof (Type));
+    }
+
+    if (unlikely (!new_array))
+    {
+      allocated = 0;
+      return false;
+    }
+
+    arrayZ = new_array;
+    allocated = new_allocated;
+
+    return true;
+  }
+
+  inline bool resize (int size_)
+  {
+    unsigned int size = size_ < 0 ? 0u : (unsigned int) size_;
+    if (!alloc (size))
+      return false;
+
+    if (size > len)
+      memset (arrayZ + len, 0, (size - len) * sizeof (*arrayZ));
+
+    len = size;
+    return true;
+  }
+
+  inline void pop (void)
+  {
+    if (!len) return;
+    len--;
+  }
+
+  inline void remove (unsigned int i)
+  {
+     if (unlikely (i >= len))
+       return;
+     memmove (static_cast<void *> (&arrayZ[i]),
+	      static_cast<void *> (&arrayZ[i + 1]),
+	      (len - i - 1) * sizeof (Type));
+     len--;
+  }
+
+  inline void shrink (int size_)
+  {
+    unsigned int size = size_ < 0 ? 0u : (unsigned int) size_;
+     if (size < len)
+       len = size;
+  }
+
+  template <typename T>
+  inline Type *find (T v) {
+    for (unsigned int i = 0; i < len; i++)
+      if (arrayZ[i] == v)
+	return &arrayZ[i];
+    return nullptr;
+  }
+  template <typename T>
+  inline const Type *find (T v) const {
+    for (unsigned int i = 0; i < len; i++)
+      if (arrayZ[i] == v)
+	return &arrayZ[i];
+    return nullptr;
+  }
+
+  inline void qsort (int (*cmp)(const void*, const void*))
+  {
+    ::qsort (arrayZ, len, sizeof (Type), cmp);
+  }
+
+  inline void qsort (void)
+  {
+    ::qsort (arrayZ, len, sizeof (Type), Type::cmp);
+  }
+
+  inline void qsort (unsigned int start, unsigned int end)
+  {
+    ::qsort (arrayZ + start, end - start, sizeof (Type), Type::cmp);
+  }
+
+  template <typename T>
+  inline Type *lsearch (const T &x)
+  {
+    for (unsigned int i = 0; i < len; i++)
+      if (0 == this->arrayZ[i].cmp (&x))
+	return &arrayZ[i];
+    return nullptr;
+  }
+
+  template <typename T>
+  inline Type *bsearch (const T &x)
+  {
+    unsigned int i;
+    return bfind (x, &i) ? &arrayZ[i] : nullptr;
+  }
+  template <typename T>
+  inline const Type *bsearch (const T &x) const
+  {
+    unsigned int i;
+    return bfind (x, &i) ? &arrayZ[i] : nullptr;
+  }
+  template <typename T>
+  inline bool bfind (const T &x, unsigned int *i) const
+  {
+    int min = 0, max = (int) this->len - 1;
+    while (min <= max)
+    {
+      int mid = (min + max) / 2;
+      int c = this->arrayZ[mid].cmp (&x);
+      if (c < 0)
+        max = mid - 1;
+      else if (c > 0)
+        min = mid + 1;
+      else
+      {
+        *i = mid;
+	return true;
+      }
+    }
+    if (max < 0 || (max < (int) this->len && this->arrayZ[max].cmp (&x) > 0))
+      max++;
+    *i = max;
+    return false;
+  }
+
+  inline void fini (void)
+  {
+    if (arrayZ != static_array)
+      free (arrayZ);
+    arrayZ = nullptr;
+    allocated = len = 0;
+  }
+};
+
+
+#endif /* HB_VECTOR_PRIVATE_HH */
diff --git a/src/hb-version.h b/src/hb-version.h
index c3c38de..e0816d1 100644
--- a/src/hb-version.h
+++ b/src/hb-version.h
@@ -38,9 +38,9 @@
 
 #define HB_VERSION_MAJOR 1
 #define HB_VERSION_MINOR 8
-#define HB_VERSION_MICRO 3
+#define HB_VERSION_MICRO 8
 
-#define HB_VERSION_STRING "1.8.3"
+#define HB_VERSION_STRING "1.8.8"
 
 #define HB_VERSION_ATLEAST(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) <= \
diff --git a/src/main.cc b/src/main.cc
index 873d240..98a1320 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -51,10 +51,9 @@
   const char *font_data = hb_blob_get_data (blob, &len);
   printf ("Opened font file %s: %d bytes long\n", argv[1], len);
 
-  Sanitizer<OpenTypeFontFile> sanitizer;
-  hb_blob_t *font_blob = sanitizer.sanitize (blob);
+  hb_blob_t *font_blob = hb_sanitize_context_t().sanitize_blob<OpenTypeFontFile> (blob);
   const OpenTypeFontFile* sanitized = font_blob->as<OpenTypeFontFile> ();
-  if (sanitized == &Null(OpenTypeFontFile))
+  if (!font_blob->data)
   {
     printf ("Sanitization of the file wasn't successful. Exit");
     return 1;
diff --git a/test/api/Makefile.am b/test/api/Makefile.am
index 65ceeda..a203344 100644
--- a/test/api/Makefile.am
+++ b/test/api/Makefile.am
@@ -9,6 +9,8 @@
 # Convenience targets:
 lib:
 	@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib
+libs:
+	@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src libs
 
 EXTRA_DIST += CMakeLists.txt
 
diff --git a/test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5542653037903872 b/test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5542653037903872
new file mode 100644
index 0000000..6307ddd
--- /dev/null
+++ b/test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5542653037903872
Binary files differ
diff --git a/test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5670861909524480 b/test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5670861909524480
new file mode 100644
index 0000000..49bcb30
--- /dev/null
+++ b/test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5670861909524480
Binary files differ
diff --git a/test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5973295416475648 b/test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5973295416475648
new file mode 100644
index 0000000..b506d2a
--- /dev/null
+++ b/test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5973295416475648
Binary files differ
diff --git a/test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-6136125075750912 b/test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-6136125075750912
new file mode 100644
index 0000000..ffcea64
--- /dev/null
+++ b/test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-6136125075750912
Binary files differ
diff --git a/test/api/test-ot-math.c b/test/api/test-ot-math.c
index 0ca5566..d071c88 100644
--- a/test/api/test-ot-math.c
+++ b/test/api/test-ot-math.c
@@ -396,8 +396,8 @@
   initFreeType();
 
   openFont("fonts/MathTestFontEmpty.otf");
-  g_assert_cmpint(hb_ot_math_get_min_connector_overlap(hb_font, FALSE), ==, 0); // MathVariants not available
-  g_assert_cmpint(hb_ot_math_get_min_connector_overlap(hb_font, TRUE), ==, 0); // MathVariants not available
+  g_assert_cmpint(hb_ot_math_get_min_connector_overlap(hb_font, HB_DIRECTION_LTR), ==, 0); // MathVariants not available
+  g_assert_cmpint(hb_ot_math_get_min_connector_overlap(hb_font, HB_DIRECTION_TTB), ==, 0); // MathVariants not available
   closeFont();
 
   openFont("fonts/MathTestFontPartial1.otf");
diff --git a/test/shaping/data/in-house/fonts/2a670df15b73a5dc75a5cc491bde5ac93c5077dc.ttf b/test/shaping/data/in-house/fonts/2a670df15b73a5dc75a5cc491bde5ac93c5077dc.ttf
new file mode 100644
index 0000000..a1fef49
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/2a670df15b73a5dc75a5cc491bde5ac93c5077dc.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf b/test/shaping/data/in-house/fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf
new file mode 100644
index 0000000..3dd30ed
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/b722a7d09e60421f3efbc706ad348ab47b88567b.ttf b/test/shaping/data/in-house/fonts/b722a7d09e60421f3efbc706ad348ab47b88567b.ttf
new file mode 100644
index 0000000..ca23ef8
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/b722a7d09e60421f3efbc706ad348ab47b88567b.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/tests/indic-old-spec.tests b/test/shaping/data/in-house/tests/indic-old-spec.tests
index cd56319..eb967ad 100644
--- a/test/shaping/data/in-house/tests/indic-old-spec.tests
+++ b/test/shaping/data/in-house/tests/indic-old-spec.tests
@@ -1,2 +1,4 @@
 ../fonts/57a9d9f83020155cbb1d2be1f43d82388cbecc88.ttf::U+0C9A,U+0CCD,U+0C9A,U+0CCD:[U0C9A_U0CCD.haln=0+1066|U0C9A_0CCD.blwf=0+0]
 ../fonts/270b89df543a7e48e206a2d830c0e10e5265c630.ttf::U+0D38,U+0D4D,U+0D31,U+0D4D,U+0D31,U+0D4D:[glyph201=0+1183|U0D4D=0+0]
+../fonts/b722a7d09e60421f3efbc706ad348ab47b88567b.ttf::U+091F,U+094D,U+0930,U+094D,U+0020:[Tra=0+550|virAma=0@-73,-110+0|space=4+500]
+../fonts/b722a7d09e60421f3efbc706ad348ab47b88567b.ttf::U+091F,U+094D,U+0930,U+0942:[Tra=0+550|UT=0@42,-150+0]
diff --git a/test/shaping/data/in-house/tests/khmer-misc.tests b/test/shaping/data/in-house/tests/khmer-misc.tests
new file mode 100644
index 0000000..a7a1c6d
--- /dev/null
+++ b/test/shaping/data/in-house/tests/khmer-misc.tests
@@ -0,0 +1,89 @@
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1781,U+17D2,U+1798,U+17C2:[uni17C2=0+288|uni1781=0+635|uni17D21798=0@22,-26+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1787,U+17B6:[uni178717B6=0+923]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1790,U+17D2,U+1784,U+17C3:[uni17C3=0+288|uni1790=0+635|uni17D21784=0@-1,-26+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1798,U+17B6:[uni179817B6=0+923]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1798,U+17D2,U+1796,U+17BB:[uni1798=0+635|uni17D21796=0@-1,-26+0|uni17BB=0@-22,-296+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179A:[uni179A=0+288]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179A,U+17B8:[uni179A=0+288|uni17B8.r=0@76,-29+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179A,U+17CD:[uni179A=0+288|uni17CD.r=0@18,-29+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17C5:[uni17C1=0+288|uni179F17C5=0+1216]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179A,U+17D2,U+17A5:[uni179A=0+288|uni17D2=0+0|uni17A5=2+635]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1784,U+17B9,U+17D2,U+1788:[uni1784=0+635|uni17B9=0@-46,30+0|uni17D21788=0+234]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1784,U+17D2,U+1788,U+17B9:[uni1784=0+635|uni17D21788=0+234|uni17B9=0@8,30+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1784,U+17D2,U+1782,U+17D2,U+179A:[uni17D2179A.low=0+287|uni1784=0+635|uni17D21782=0@0,-26+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1784,U+17D2,U+179A,U+17D2,U+1782:[uni17D2179A.low=0+287|uni1784=0+635|uni17D21782=0@0,-26+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1798,U+17C9,U+17D2,U+179B,U+17C1,U+17C7:[uni17C1=0+288|uni1798=0+635|uni17C9=0@-46,-29+0|uni17D2179B=0@-1,-26+0|uni17C7=0+386]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1798,U+200C,U+17C9,U+17D2,U+179B,U+17C1,U+17C7:[uni17C1=0+288|uni1798=0+635|space=0+0|uni17C9=0@-46,-29+0|uni17D2179B=0@-1,-26+0|uni17C7=0+386]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1794,U+17CA,U+17D0:[uni1794=0+635|uni17CA=0@-46,-29+0|uni17D0=0@-46,113+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1793,U+17C2,U+17CE:[uni17C2=0+288|uni1793=0+635|uni17CE=0@-36,-29+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1780,U+17C1,U+17D2,U+179A:[uni17D2179A=0+287|uni17C1=0+288|uni1780=0+636]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1780,U+17C0,U+17D2,U+179A:[uni17D2179A=0+287|uni17C1=0+288|uni1780=0+636|uni17C0.right1=0+288]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1780,U+17C4,U+17D2,U+179A:[uni17D2179A=0+287|uni17C1=0+288|uni178017B6=0+924]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1780,U+17C5,U+17D2,U+179A:[uni17D2179A=0+287|uni17C1=0+288|uni178017C5=0+924]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1796,U+17D1,U+17B6:[uni179617B6=0+923|uni17D1=0@-311,-19+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1780,U+1793,U+17D2,U+178F,U+17D2,U+179A,U+17B6,U+1780,U+17CB:[uni1780=0+636|uni17D2179A.low=1+287|uni179317B6=1+924|uni17D2178F=1@-290,-26+0|uni1780=7+636|uni17CB=7@-23,-29+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1780,U+1793,U+17D2,U+179A,U+17D2,U+179F,U+17B7,U+1780,U+17CB:[uni1780=0+636|uni17D2179A=1+287|uni1793=1+635|uni17D2179F=1+302|uni17B7=1@-4,30+0|uni1780=7+636|uni17CB=7@-23,-29+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1780,U+1793,U+17D2,U+179A,U+17D2,U+179F,U+17B8,U+1780,U+17CB:[uni1780=0+636|uni17D2179A=1+287|uni1793=1+635|uni17D2179F=1+302|uni17B8=1@-4,30+0|uni1780=7+636|uni17CB=7@-23,-29+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1780,U+17D2,U+179F,U+17B6,U+1793,U+17D2,U+178F:[uni1780=0+636|uni17D2179F17B6=0+584|uni1793=4+635|uni17D2178F=4@-1,-26+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1780,U+17D2,U+179F,U+17B7,U+1793,U+17D2,U+178F:[uni1780=0+636|uni17D2179F=0+302|uni17B7=0@-4,30+0|uni1793=4+635|uni17D2178F=4@-1,-26+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1780,U+17D2,U+179F,U+17B8,U+1793,U+17D2,U+178F:[uni1780=0+636|uni17D2179F=0+302|uni17B8=0@-4,30+0|uni1793=4+635|uni17D2178F=4@-1,-26+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1780,U+17D2,U+179F,U+17B9,U+1793,U+17D2,U+178F:[uni1780=0+636|uni17D2179F=0+302|uni17B9=0@-4,30+0|uni1793=4+635|uni17D2178F=4@-1,-26+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1780,U+17D2,U+179F,U+17BA,U+1793,U+17D2,U+178F:[uni1780=0+636|uni17D2179F=0+302|uni17BA=0@-4,30+0|uni1793=4+635|uni17D2178F=4@-1,-26+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1780,U+17D2,U+179F,U+17BB,U+1793,U+17D2,U+178F:[uni1780=0+636|uni17D2179F=0+302|uni17BB=0@1,-260+0|uni1793=4+635|uni17D2178F=4@-1,-26+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1780,U+17D2,U+179F,U+17BC,U+1793,U+17D2,U+178F:[uni1780=0+636|uni17D2179F=0+302|uni17BC=0@1,-260+0|uni1793=4+635|uni17D2178F=4@-1,-26+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1780,U+17D2,U+179F,U+17BD,U+1793,U+17D2,U+178F:[uni1780=0+636|uni17D2179F=0+302|uni17BD=0@1,-260+0|uni1793=4+635|uni17D2178F=4@-1,-26+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1780,U+17D2,U+179F,U+17BF,U+1793,U+17D2,U+178F:[uni17C1=0+288|uni1780=0+636|uni17D2179F=0+302|uni17BF.right2=0+288|uni1793=4+635|uni17D2178F=4@-1,-26+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1783,U+17D2,U+179B,U+17D2,U+179F,U+17B6,U+17C6:[uni1783=0+928|uni17D2179B=0@15,-26+0|uni17D2179F17B6.low=0+584|uni17C6=0@39,-29+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1783,U+17D2,U+179B,U+17D2,U+179F,U+17B7,U+17C7:[uni1783=0+928|uni17D2179B=0@15,-26+0|uni17D2179F.low=0+302|uni17B7=0@-4,30+0|uni17C7=0+386]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1783,U+17D2,U+179B,U+17D2,U+179F,U+17BB,U+17C6:[uni1783=0+928|uni17D2179B=0@15,-26+0|uni17D2179F.low=0+302|uni17BB=0+0|uni17C6=0@-4,30+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1783,U+17D2,U+179B,U+17D2,U+179F,U+17BB,U+17C7:[uni1783=0+928|uni17D2179B=0@15,-26+0|uni17D2179F.low=0+302|uni17BB=0+0|uni17C7=0+386]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1783,U+17D2,U+179B,U+17D2,U+179F,U+17C1,U+17C7:[uni17C1=0+288|uni1783=0+928|uni17D2179B=0@15,-26+0|uni17D2179F.low=0+302|uni17C7=0+386]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1783,U+17D2,U+179B,U+17D2,U+179F,U+17C4,U+17C7:[uni17C1=0+288|uni1783=0+928|uni17D2179B=0@15,-26+0|uni17D2179F17B6.low=0+584|uni17C7=0+386]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1783,U+17D2,U+179B,U+17D2,U+179F,U+17C6:[uni1783=0+928|uni17D2179B=0@15,-26+0|uni17D2179F.low=0+302|uni17C6=0@-4,30+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1783,U+17D2,U+179B,U+17D2,U+179F,U+17C7:[uni1783=0+928|uni17D2179B=0@15,-26+0|uni17D2179F.low=0+302|uni17C7=0+386]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1789,U+17BC:[uni1789=0+952|uni17BC=0@-173,-260+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1789,U+17D2,U+1789:[uni1789.a=0+952|uni17D21789.a=0@19,-22+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1789,U+17D2,U+1789,U+17BB:[uni1789.a=0+952|uni17D21789.a=0@19,-22+0|uni17BB=0@-160,-296+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1789,U+17D2,U+1789,U+17BC:[uni1789.a=0+952|uni17D21789.a=0@19,-22+0|uni17BC=0@-160,-296+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1789,U+17D2,U+1789,U+17BD:[uni1789.a=0+952|uni17D21789.a=0@19,-22+0|uni17BD=0@-160,-296+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+178F,U+17D2,U+179A,U+17D2,U+179F,U+17C0:[uni17C1=0+288|uni17D2179A=0+287|uni178F=0+635|uni17D2179F=0+302|uni17C0.right2=0+288]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+178F,U+17D2,U+179A,U+17D2,U+179F,U+17C1:[uni17C1=0+288|uni17D2179A=0+287|uni178F=0+635|uni17D2179F=0+302]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+178F,U+17D2,U+179A,U+17D2,U+179F,U+17C2:[uni17C2=0+288|uni17D2179A=0+287|uni178F=0+635|uni17D2179F=0+302]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+178F,U+17D2,U+179A,U+17D2,U+179F,U+17C3:[uni17C3=0+288|uni17D2179A=0+287|uni178F=0+635|uni17D2179F=0+302]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+178F,U+17D2,U+179A,U+17D2,U+179F,U+17C4:[uni17C1=0+288|uni17D2179A=0+287|uni178F=0+635|uni17D2179F17B6=0+584]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+178F,U+17D2,U+179A,U+17D2,U+179F,U+17C5:[uni17C1=0+288|uni17D2179A=0+287|uni178F=0+635|uni17D2179F17C5=0+584]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1792,U+17D2,U+179B,U+17BB,U+17C6,U+1780,U+17CB:[uni1792=0+635|uni17D2179B=0@-2,-26+0|uni17BB=0@-19,-296+0|uni17C6=0@-46,-29+0|uni1780=5+636|uni17CB=5@-23,-29+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1792,U+17D2,U+179B,U+17C4,U+1780,U+17CB:[uni17C1=0+288|uni179217B6=0+923|uni17D2179B=0@-290,-26+0|uni1780=4+636|uni17CB=4@-23,-29+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1792,U+17D2,U+179B,U+17C5,U+1780,U+17CB:[uni17C1=0+288|uni179217C5=0+923|uni17D2179B=0@-290,-26+0|uni1780=4+636|uni17CB=4@-23,-29+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1792,U+17D2,U+179B,U+17C6,U+1780,U+17CB:[uni1792=0+635|uni17D2179B=0@-2,-26+0|uni17C6=0@-46,-29+0|uni1780=4+636|uni17CB=4@-23,-29+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1798,U+17D2,U+178F,U+17D2,U+179B,U+17C9,U+17B6:[uni179817B6=0+923|uni17D2178F=0@-289,-26+0|uni17D2179B=0@-289,-296+0|uni17C9=0@-334,-29+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1798,U+17D2,U+178F,U+17D2,U+179B,U+17C9,U+17BB:[uni1798=0+635|uni17D2178F=0@-1,-26+0|uni17D2179B=0@-1,-296+0|uni17C9=0@-46,-29+0|uni17BB=0@-18,-566+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1798,U+17D2,U+178F,U+17D2,U+179B,U+17C9,U+17BF:[uni17C1=0+288|uni1798=0+635|uni17D2178F=0@-1,-26+0|uni17D2179B=0@-1,-296+0|uni17C9=0@-46,-29+0|uni17BF.right1=0+288]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1798,U+17D2,U+178F,U+17D2,U+179B,U+17C9,U+17C0:[uni17C1=0+288|uni1798=0+635|uni17D2178F=0@-1,-26+0|uni17D2179B=0@-1,-296+0|uni17C9=0@-46,-29+0|uni17C0.right1=0+288]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+1799,U+17D2,U+1799,U+17BE,U+17A0,U+17D2,U+179C,U+17D2,U+179B,U+17C3:[uni17C1=0+288|uni1799=0+953|uni17D21799=0+298|uni17B8=0@1,30+0|uni17C3=4+288|uni17A0=4+928|uni17D2179C=4@20,-26+0|uni17D2179B=4@19,-296+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179A,U+17D2,U+179A:[uni17D2179A=0+287|uni179A=0+288]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17B6,U+179F,U+17D2,U+178F,U+17D2,U+179A,U+1783,U+17D2,U+1788,U+1784,U+17B6:[uni179F17B6=0+1216|uni17D2179A=2+287|uni179F=2+928|uni17D2178F=2@14,-26+0|uni1783=7+928|uni17D21788=7+234|uni178417B6=10+923]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17B6,U+179F,U+17D2,U+178F,U+17D2,U+179A,U+1783,U+17D2,U+1788,U+1784,U+17B7:[uni179F17B6=0+1216|uni17D2179A=2+287|uni179F=2+928|uni17D2178F=2@14,-26+0|uni1783=7+928|uni17D21788=7+234|uni1784=10+635|uni17B7=10@-46,30+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17B6,U+179F,U+17D2,U+178F,U+17D2,U+179A,U+1783,U+17D2,U+1788,U+1784,U+17B8:[uni179F17B6=0+1216|uni17D2179A=2+287|uni179F=2+928|uni17D2178F=2@14,-26+0|uni1783=7+928|uni17D21788=7+234|uni1784=10+635|uni17B8=10@-46,30+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17D2,U+178F,U+17D2,U+179A,U+17B8,U+179C,U+17D0,U+1781,U+17D2,U+1789,U+17D2,U+179F,U+17B6:[uni17D2179A=0+287|uni179F=0+928|uni17D2178F=0@14,-26+0|uni17B8=0@-32,-29+0|uni179C=6+326|uni17D0=6@139,40+0|uni1781=8+635|uni17D21789=8@-4,-26+0|uni17D2179F17B6.low=8+584]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17D2,U+179A,U+17D2,U+178F,U+17B6:[uni17D2179A=0+287|uni179F17B6=0+1216|uni17D2178F=0@-274,-26+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17D2,U+179A,U+17D2,U+178F,U+17B7:[uni17D2179A=0+287|uni179F=0+928|uni17D2178F=0@14,-26+0|uni17B7=0@-32,-29+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17D2,U+179A,U+17D2,U+178F,U+17B8:[uni17D2179A=0+287|uni179F=0+928|uni17D2178F=0@14,-26+0|uni17B8=0@-32,-29+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17D2,U+179A,U+17D2,U+178F,U+17B9:[uni17D2179A=0+287|uni179F=0+928|uni17D2178F=0@14,-26+0|uni17B9=0@-32,-29+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17D2,U+179A,U+17D2,U+178F,U+17BA:[uni17D2179A=0+287|uni179F=0+928|uni17D2178F=0@14,-26+0|uni17BA=0@-32,-29+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17D2,U+179A,U+17D2,U+178F,U+17BB:[uni17D2179A=0+287|uni179F=0+928|uni17D2178F=0@14,-26+0|uni17BB=0@-6,-296+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17D2,U+179A,U+17D2,U+178F,U+17BC:[uni17D2179A=0+287|uni179F=0+928|uni17D2178F=0@14,-26+0|uni17BC=0@-6,-296+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17D2,U+179A,U+17D2,U+178F,U+17BD:[uni17D2179A=0+287|uni179F=0+928|uni17D2178F=0@14,-26+0|uni17BD=0@-6,-296+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17D2,U+179A,U+17D2,U+178F,U+17BE:[uni17C1=0+288|uni17D2179A=0+287|uni179F=0+928|uni17D2178F=0@14,-26+0|uni17B8=0@-32,-29+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17D2,U+179A,U+17D2,U+178F,U+17BF:[uni17C1=0+288|uni17D2179A=0+287|uni179F=0+928|uni17D2178F=0@14,-26+0|uni17BF.right2=0+288]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17D2,U+179A,U+17D2,U+178F,U+17CA,U+17BF:[uni17C1=0+288|uni17D2179A=0+287|uni179F=0+928|uni17D2178F=0@14,-26+0|uni17CA=0@-32,-29+0|uni17BF.right1=0+288]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17D2,U+179A,U+17D2,U+178F,U+17CA,U+17C0:[uni17C1=0+288|uni17D2179A=0+287|uni179F=0+928|uni17D2178F=0@14,-26+0|uni17CA=0@-32,-29+0|uni17C0.right1.high=0+288]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17D2,U+179A,U+17D2,U+178F,U+17CA,U+17C1:[uni17C1=0+288|uni17D2179A=0+287|uni179F=0+928|uni17D2178F=0@14,-26+0|uni17CA=0@-32,-29+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17D2,U+179A,U+17D2,U+178F,U+17CA,U+17C2:[uni17C2=0+288|uni17D2179A=0+287|uni179F=0+928|uni17D2178F=0@14,-26+0|uni17CA=0@-32,-29+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17D2,U+179A,U+17D2,U+178F,U+17CA,U+17C3:[uni17C3=0+288|uni17D2179A=0+287|uni179F=0+928|uni17D2178F=0@14,-26+0|uni17CA=0@-32,-29+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+179F,U+17D2,U+179A,U+17D2,U+178F,U+17CA,U+17C6:[uni17D2179A=0+287|uni179F=0+928|uni17D2178F=0@14,-26+0|uni17CA=0@-32,-29+0|uni17C6=0@-32,113+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+17A0,U+17D2,U+1782,U+17D2,U+179F,U+17CA,U+17BE:[uni17C1=0+288|uni17A0=0+928|uni17D21782=0@20,-26+0|uni17D2179F.low=0+302|uni17BB=0+0|uni17B8=0@-4,30+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+17A0,U+17D2,U+1782,U+17D2,U+179F,U+17CA,U+17BF:[uni17C1=0+288|uni17A0=0+928|uni17D21782=0@20,-26+0|uni17D2179F.low=0+302|uni17CA=0@-4,30+0|uni17BF.right1=0+288]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+17A0,U+17D2,U+1782,U+17D2,U+179F,U+17CA,U+17C0:[uni17C1=0+288|uni17A0=0+928|uni17D21782=0@20,-26+0|uni17D2179F.low=0+302|uni17CA=0@-4,30+0|uni17C0.right1.high=0+288]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+17A0,U+17D2,U+179A,U+17D2,U+179C,U+1784,U+17D2,U+1780:[uni17D2179A=0+287|uni17A0=0+928|uni17D2179C=0@20,-26+0|uni1784=5+635|uni17D21780=5@0,-26+0]
+../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+17A0,U+17D2,U+179A,U+17D2,U+179C,U+17B6,U+17C6,U+1784:[uni17D2179A=0+287|uni17A017B6=0+1216|uni17D2179C=0@-268,-26+0|uni17C6=0@47,-29+0|uni1784=7+635]
diff --git a/test/shaping/data/in-house/tests/use.tests b/test/shaping/data/in-house/tests/use.tests
index 172946c..4b46620 100644
--- a/test/shaping/data/in-house/tests/use.tests
+++ b/test/shaping/data/in-house/tests/use.tests
@@ -2,4 +2,9 @@
 ../fonts/4cce528e99f600ed9c25a2b69e32eb94a03b4ae8.ttf::U+1A48,U+1A58,U+1A25,U+1A48,U+1A58,U+1A25,U+1A6E,U+1A63:[uni1A48=0+1212|uni1A25=0+1912|uni1A58=0+0|uni1A48=3+1212|uni1A6E=3+0|uni1A25=3+1912|uni1A58=3+0|uni1A63=3+1212]
 ../fonts/f518eb6f6b5eec2946c9fbbbde44e45d46f5e2ac.ttf::U+1A48,U+1A58,U+1A25,U+1A48,U+1A58,U+1A25,U+1A6E,U+1A63:[uni1A48=0+1212|uni1A25=0+1912|uni1A58=0+0|uni1A48=3+1212|uni1A6E=3+1211|uni1A25=3+1912|uni1A58=3+0|uni1A63=3+1212]
 ../fonts/6ff0fbead4462d9f229167b4e6839eceb8465058.ttf:--font-funcs=ot:U+11103,U+11128:[u11103=0+837|u11128=0+0]
+../fonts/2a670df15b73a5dc75a5cc491bde5ac93c5077dc.ttf::U+11124,U+1112E:[u11124=0+514|u11131=0+0|u11127=0+0]
+../fonts/2a670df15b73a5dc75a5cc491bde5ac93c5077dc.ttf::U+11124,U+11131,U+11127:[u11124=0+514|u11131=0+0|u11127=0+0]
+../fonts/2a670df15b73a5dc75a5cc491bde5ac93c5077dc.ttf::U+11124,U+11127,U+11131:[u11124=0+514|u11127=0+0|uni25CC=0+547|u11131=0+0]
+../fonts/2a670df15b73a5dc75a5cc491bde5ac93c5077dc.ttf::U+11124,U+11134,U+11131:[u11124=0+514|u11134=0+0|u11131=0+0]
+../fonts/2a670df15b73a5dc75a5cc491bde5ac93c5077dc.ttf::U+11124,U+11131,U+11134:[u11124=0+514|u11131=0+0|uni25CC=0+547|u11134=0+0]
 ../fonts/573d3a3177c9a8646e94c8a0d7b224334340946a.ttf:--font-funcs=ft:U+11410,U+11442,U+11411,U+11440,U+11443,U+11410,U+11442,U+11411,U+11441,U+11443:[E_dv.alt=0+275|Ga.icd=0+367|Gha.diag=0@100,0+386|AA_dv.alt=0+208|Candrabindu=0@17,-8+0|E_dv.alt=5+275|Ga.icd=5+367|Gha.diag=5@100,0+386|AU_dv_part.alt=5+213|Candrabindu.sm=5@-52,179+0]
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/LICENSE
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/README b/test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/README
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/SOURCES
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt b/test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
rename to test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt b/test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
rename to test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt b/test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
rename to test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt b/test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
rename to test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/script-assamese/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/misc/misc.txt b/test/shaping/texts/in-house/shaper-indic/script-bengali/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-bengali/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-indic/script-bengali/misc/misc.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/misc/reph.txt b/test/shaping/texts/in-house/shaper-indic/script-bengali/misc/reph.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-bengali/misc/reph.txt
rename to test/shaping/texts/in-house/shaper-indic/script-bengali/misc/reph.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/LICENSE
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/README b/test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/README
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/SOURCES
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt b/test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
rename to test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt b/test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
rename to test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt b/test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
rename to test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt b/test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
rename to test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/script-bengali/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/dottedcircle.txt b/test/shaping/texts/in-house/shaper-indic/script-devanagari/misc/dottedcircle.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/dottedcircle.txt
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/misc/dottedcircle.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/eyelash.txt b/test/shaping/texts/in-house/shaper-indic/script-devanagari/misc/eyelash.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/eyelash.txt
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/misc/eyelash.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/joiners.txt b/test/shaping/texts/in-house/shaper-indic/script-devanagari/misc/joiners.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/joiners.txt
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/misc/joiners.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/misc.txt b/test/shaping/texts/in-house/shaper-indic/script-devanagari/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/misc/misc.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/spec-deviations.txt b/test/shaping/texts/in-house/shaper-indic/script-devanagari/misc/spec-deviations.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/spec-deviations.txt
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/misc/spec-deviations.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/tricky-reordering.txt b/test/shaping/texts/in-house/shaper-indic/script-devanagari/misc/tricky-reordering.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/tricky-reordering.txt
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/misc/tricky-reordering.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/LICENSE
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/README b/test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/README
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/SOURCES
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt b/test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-DevnagariSpecificAddition.txt b/test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-DevnagariSpecificAddition.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-DevnagariSpecificAddition.txt
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-DevnagariSpecificAddition.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt b/test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-GenericPunctuation.txt b/test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-GenericPunctuation.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-GenericPunctuation.txt
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-GenericPunctuation.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt b/test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt b/test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/script-devanagari/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/LICENSE
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/README b/test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/README
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/SOURCES
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt b/test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt b/test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt b/test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt b/test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gujarati/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/misc/misc.txt b/test/shaping/texts/in-house/shaper-indic/script-gurmukhi/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gurmukhi/misc/misc.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/LICENSE
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/README b/test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/README
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/SOURCES
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt b/test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-GurmukhiSpecific.txt b/test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-GurmukhiSpecific.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-GurmukhiSpecific.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-GurmukhiSpecific.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt b/test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt b/test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt b/test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/script-gurmukhi/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/misc/misc.txt b/test/shaping/texts/in-house/shaper-indic/script-kannada/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-kannada/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-indic/script-kannada/misc/misc.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/misc/right-matras.txt b/test/shaping/texts/in-house/shaper-indic/script-kannada/misc/right-matras.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-kannada/misc/right-matras.txt
rename to test/shaping/texts/in-house/shaper-indic/script-kannada/misc/right-matras.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/LICENSE
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/README b/test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/README
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/SOURCES
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt b/test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt
rename to test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt b/test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
rename to test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt b/test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
rename to test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt b/test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
rename to test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/script-kannada/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/misc/cibu.txt b/test/shaping/texts/in-house/shaper-indic/script-malayalam/misc/cibu.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/misc/cibu.txt
rename to test/shaping/texts/in-house/shaper-indic/script-malayalam/misc/cibu.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/misc/dot-reph.txt b/test/shaping/texts/in-house/shaper-indic/script-malayalam/misc/dot-reph.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/misc/dot-reph.txt
rename to test/shaping/texts/in-house/shaper-indic/script-malayalam/misc/dot-reph.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/misc/misc.txt b/test/shaping/texts/in-house/shaper-indic/script-malayalam/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-indic/script-malayalam/misc/misc.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/LICENSE
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/README b/test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/README
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/SOURCES
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt b/test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
rename to test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt b/test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
rename to test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/script-malayalam/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/misc/bindu.txt b/test/shaping/texts/in-house/shaper-indic/script-oriya/misc/bindu.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-oriya/misc/bindu.txt
rename to test/shaping/texts/in-house/shaper-indic/script-oriya/misc/bindu.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/misc/misc.txt b/test/shaping/texts/in-house/shaper-indic/script-oriya/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-oriya/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-indic/script-oriya/misc/misc.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/LICENSE
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/README b/test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/README
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/SOURCES
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt b/test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt
rename to test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt b/test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
rename to test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-OriyaSpecific.txt b/test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-OriyaSpecific.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-OriyaSpecific.txt
rename to test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-OriyaSpecific.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt b/test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
rename to test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/script-oriya/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/misc/extensive.txt b/test/shaping/texts/in-house/shaper-indic/script-sinhala/misc/extensive.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/misc/extensive.txt
rename to test/shaping/texts/in-house/shaper-indic/script-sinhala/misc/extensive.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/misc/misc.txt b/test/shaping/texts/in-house/shaper-indic/script-sinhala/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-indic/script-sinhala/misc/misc.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/misc/reph.txt b/test/shaping/texts/in-house/shaper-indic/script-sinhala/misc/reph.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/misc/reph.txt
rename to test/shaping/texts/in-house/shaper-indic/script-sinhala/misc/reph.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/misc/split-matras.txt b/test/shaping/texts/in-house/shaper-indic/script-sinhala/misc/split-matras.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/misc/split-matras.txt
rename to test/shaping/texts/in-house/shaper-indic/script-sinhala/misc/split-matras.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/LICENSE
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/README b/test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/README
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/SOURCES
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-Punctuation.txt b/test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-Punctuation.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-Punctuation.txt
rename to test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-Punctuation.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gpos/IndicFontFeatureGPOS.txt b/test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/gpos/IndicFontFeatureGPOS.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gpos/IndicFontFeatureGPOS.txt
rename to test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/gpos/IndicFontFeatureGPOS.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Conjunct.txt b/test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Conjunct.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Conjunct.txt
rename to test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Conjunct.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Rakaaraansaya.txt b/test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Rakaaraansaya.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Rakaaraansaya.txt
rename to test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Rakaaraansaya.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Repaya.txt b/test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Repaya.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Repaya.txt
rename to test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Repaya.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Special-Cases.txt b/test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Special-Cases.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Special-Cases.txt
rename to test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Special-Cases.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-TouchingLetters.txt b/test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-TouchingLetters.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-TouchingLetters.txt
rename to test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-TouchingLetters.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Yansaya.txt b/test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Yansaya.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Yansaya.txt
rename to test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Yansaya.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/misc/misc.txt b/test/shaping/texts/in-house/shaper-indic/script-tamil/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-tamil/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-indic/script-tamil/misc/misc.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/LICENSE
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/README b/test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/README
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/SOURCES
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-CurrencySymbols.txt b/test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-CurrencySymbols.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-CurrencySymbols.txt
rename to test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-CurrencySymbols.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt b/test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
rename to test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Numerics.txt b/test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Numerics.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Numerics.txt
rename to test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Numerics.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt b/test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
rename to test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Symbols.txt b/test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Symbols.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Symbols.txt
rename to test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Symbols.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-TamilSymbol.txt b/test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-TamilSymbol.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-TamilSymbol.txt
rename to test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-TamilSymbol.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt b/test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
rename to test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt b/test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
rename to test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/script-tamil/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/misc/misc.txt b/test/shaping/texts/in-house/shaper-indic/script-telugu/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-telugu/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-indic/script-telugu/misc/misc.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/LICENSE
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/README b/test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/README
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/SOURCES
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt b/test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
rename to test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt b/test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
rename to test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt b/test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
rename to test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/script-telugu/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/south-east-asian/script-khmer/misc/misc.txt b/test/shaping/texts/in-house/shaper-indic/south-east-asian/script-khmer/misc/misc.txt
deleted file mode 100644
index 945dd1d..0000000
--- a/test/shaping/texts/in-house/shaper-indic/south-east-asian/script-khmer/misc/misc.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-ខ្មែ
-ជា
-ថ្ងៃ
-មា
-ម្ពុ
-រ
-រី
-រ៍
-សៅ
-រ្ឥ
-ងឹ្ឈ
-ង្ឈឹ
-ង្គ្រ
-ង្រ្គ
-ម៉្លេះ
-ម‌៉្លេះ
-ប៊័
-នែ៎
-កេ្រ
-កៀ្រ
-កោ្រ
-កៅ្រ
-ព៑ា
diff --git a/test/shaping/texts/in-house/shaper-khmer/misc.txt b/test/shaping/texts/in-house/shaper-khmer/misc.txt
new file mode 100644
index 0000000..3bbffe7
--- /dev/null
+++ b/test/shaping/texts/in-house/shaper-khmer/misc.txt
@@ -0,0 +1,89 @@
+ខ្មែ
+ជា
+ថ្ងៃ
+មា
+ម្ពុ
+រ
+រី
+រ៍
+សៅ
+រ្ឥ
+ងឹ្ឈ
+ង្ឈឹ
+ង្គ្រ
+ង្រ្គ
+ម៉្លេះ
+ម‌៉្លេះ
+ប៊័
+នែ៎
+កេ្រ
+កៀ្រ
+កោ្រ
+កៅ្រ
+ព៑ា
+កន្ត្រាក់
+កន្រ្សិក់
+កន្រ្សីក់
+ក្សាន្ត
+ក្សិន្ត
+ក្សីន្ត
+ក្សឹន្ត
+ក្សឺន្ត
+ក្សុន្ត
+ក្សូន្ត
+ក្សួន្ត
+ក្សឿន្ត
+ឃ្ល្សាំ
+ឃ្ល្សិះ
+ឃ្ល្សុំ
+ឃ្ល្សុះ
+ឃ្ល្សេះ
+ឃ្ល្សោះ
+ឃ្ល្សំ
+ឃ្ល្សះ
+ញូ
+ញ្ញ
+ញ្ញុ
+ញ្ញូ
+ញ្ញួ
+ត្រ្សៀ
+ត្រ្សេ
+ត្រ្សែ
+ត្រ្សៃ
+ត្រ្សោ
+ត្រ្សៅ
+ធ្លុំក់
+ធ្លោក់
+ធ្លៅក់
+ធ្លំក់
+ម្ត្ល៉ា
+ម្ត្ល៉ុ
+ម្ត្ល៉ឿ
+ម្ត្ល៉ៀ
+យ្យើហ្វ្លៃ
+រ្រ
+សាស្ត្រឃ្ឈងា
+សាស្ត្រឃ្ឈងិ
+សាស្ត្រឃ្ឈងី
+ស្ត្រីវ័ខ្ញ្សា
+ស្រ្តា
+ស្រ្តិ
+ស្រ្តី
+ស្រ្តឹ
+ស្រ្តឺ
+ស្រ្តុ
+ស្រ្តូ
+ស្រ្តួ
+ស្រ្តើ
+ស្រ្តឿ
+ស្រ្ត៊ឿ
+ស្រ្ត៊ៀ
+ស្រ្ត៊េ
+ស្រ្ត៊ែ
+ស្រ្ត៊ៃ
+ស្រ្ត៊ំ
+ហ្គ្ស៊ើ
+ហ្គ្ស៊ឿ
+ហ្គ្ស៊ៀ
+ហ្រ្វង្ក
+ហ្រ្វាំង
diff --git a/test/shaping/texts/in-house/shaper-indic/south-east-asian/script-khmer/misc/other-marks-invalid.txt b/test/shaping/texts/in-house/shaper-khmer/other-marks-invalid.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/south-east-asian/script-khmer/misc/other-marks-invalid.txt
rename to test/shaping/texts/in-house/shaper-khmer/other-marks-invalid.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/south-east-asian/script-khmer/misc/other-marks.txt b/test/shaping/texts/in-house/shaper-khmer/other-marks.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/south-east-asian/script-khmer/misc/other-marks.txt
rename to test/shaping/texts/in-house/shaper-khmer/other-marks.txt
diff --git a/test/shaping/texts/in-house/shaper-indic/south-east-asian/script-javanese/misc.txt b/test/shaping/texts/in-house/shaper-use/script-javanese/misc.txt
similarity index 100%
rename from test/shaping/texts/in-house/shaper-indic/south-east-asian/script-javanese/misc.txt
rename to test/shaping/texts/in-house/shaper-use/script-javanese/misc.txt