Merge pull request #1018 from googlefonts/cmap4

[subset] Add cmap format 4 subsetting.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 55366c0..9ed7e56 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,4 @@
 cmake_minimum_required(VERSION 2.8.0)
-cmake_policy(SET CMP0063 NEW)
-
 project(harfbuzz)
 
 enable_testing()
@@ -526,17 +524,17 @@
 
 ## Define harfbuzz library
 add_library(harfbuzz ${project_sources} ${project_extra_sources} ${project_headers})
-set_target_properties(harfbuzz PROPERTIES
-  VISIBILITY_INLINES_HIDDEN TRUE)
 target_link_libraries(harfbuzz ${THIRD_PARTY_LIBS})
 
 ## Define harfbuzz-subset library
 add_library(harfbuzz-subset ${subset_project_sources} ${subset_project_headers})
 add_dependencies(harfbuzz-subset harfbuzz)
-set_target_properties(harfbuzz-subset PROPERTIES
-  VISIBILITY_INLINES_HIDDEN TRUE)
 target_link_libraries(harfbuzz-subset harfbuzz ${THIRD_PARTY_LIBS})
 
+if (BUILD_SHARED_LIBS)
+  set_target_properties(harfbuzz harfbuzz-subset PROPERTIES VISIBILITY_INLINES_HIDDEN TRUE)
+endif ()
+
 if (UNIX OR MINGW)
   # Make symbols link locally
   include (CheckCXXCompilerFlag)
@@ -566,11 +564,13 @@
     ${hb_gobject_headers}
     ${hb_gobject_gen_headers}
   )
-  set_target_properties(harfbuzz-gobject PROPERTIES
-    VISIBILITY_INLINES_HIDDEN TRUE)
   include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}/src)
   add_dependencies(harfbuzz-gobject harfbuzz)
   target_link_libraries(harfbuzz-gobject harfbuzz ${GOBJECT_LIBRARIES} ${THIRD_PARTY_LIBS})
+
+  if (BUILD_SHARED_LIBS)
+    set_target_properties(harfbuzz-gobject PROPERTIES VISIBILITY_INLINES_HIDDEN TRUE)
+  endif ()
 endif ()
 
 if (BUILD_SHARED_LIBS AND WIN32 AND NOT MINGW)
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 1ed64fe..9fbb71e 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -2,6 +2,7 @@
 
 HB_BASE_sources = \
 	hb-atomic-private.hh \
+	hb-blob-private.hh \
 	hb-blob.cc \
 	hb-buffer-private.hh \
 	hb-buffer-serialize.cc \
diff --git a/src/dump-emoji.cc b/src/dump-emoji.cc
index e173a5e..63af4a6 100644
--- a/src/dump-emoji.cc
+++ b/src/dump-emoji.cc
@@ -47,7 +47,7 @@
 #include <stdio.h>
 
 #ifndef HB_NO_VISIBILITY
-const void * const OT::_hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {};
+const void * const _hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {};
 #endif
 
 void cbdt_callback (const uint8_t* data, unsigned int length,
@@ -91,7 +91,7 @@
 void colr_cpal_rendering (cairo_font_face_t *cairo_face, unsigned int upem, unsigned int num_glyphs,
 			  const OT::COLR *colr, const OT::CPAL *cpal)
 {
-  for (int i = 0; i < num_glyphs; ++i)
+  for (unsigned int i = 0; i < num_glyphs; ++i)
   {
     unsigned int first_layer_index, num_layers;
     if (colr->get_base_glyph_record (i, &first_layer_index, &num_layers))
@@ -171,7 +171,7 @@
 {
   // Dump every glyph available on the font
   return; // disabled for now
-  for (int i = 0; i < num_glyphs; ++i)
+  for (unsigned int i = 0; i < num_glyphs; ++i)
   {
     cairo_text_extents_t extents;
     cairo_glyph_t glyph = {0};
@@ -240,11 +240,11 @@
 
   OT::Sanitizer<OT::COLR> sanitizerCOLR;
   hb_blob_t* colr_blob = sanitizerCOLR.sanitize (face->reference_table (HB_OT_TAG_COLR));
-  const OT::COLR *colr = OT::Sanitizer<OT::COLR>::lock_instance (colr_blob);
+  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));
-  const OT::CPAL *cpal = OT::Sanitizer<OT::CPAL>::lock_instance (cpal_blob);
+  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
index 1d18983..81525f4 100644
--- a/src/dump-fon.cc
+++ b/src/dump-fon.cc
@@ -27,7 +27,7 @@
 #include "hb-open-type-private.hh"
 
 #ifndef HB_NO_VISIBILITY
-const void * const OT::_hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {};
+const void * const _hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {};
 #endif
 
 template <typename Type, int Bytes> struct LEInt;
@@ -344,7 +344,7 @@
   {
     const NE_TYPEINFO& next = OT::StructAfter<NE_TYPEINFO> (*this);
     if (type_id == 0)
-      return OT::Null (NE_TYPEINFO);
+      return Null (NE_TYPEINFO);
     return next;
   }
 
@@ -352,7 +352,7 @@
   {
     if (idx < count)
       return resources[idx].get_font (base, shift);
-    return OT::Null (LE_FONTINFO16);
+    return Null (LE_FONTINFO16);
   }
 
   inline unsigned int get_count () const
@@ -401,7 +401,7 @@
       return_trace (false);
 
     const NE_TYPEINFO* n = &chain;
-    while (n != &OT::Null (NE_TYPEINFO) && c->check_struct (n) && n->get_type_id () != 0)
+    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));
@@ -418,13 +418,13 @@
   inline const NE_TYPEINFO& get_fonts_entry () const
   {
     const NE_TYPEINFO* n = &chain;
-    while (n != &OT::Null (NE_TYPEINFO) && n->get_type_id () != 0)
+    while (n != &Null (NE_TYPEINFO) && n->get_type_id () != 0)
     {
       if (n->get_type_id () == NE_TYPEINFO::FONT)
 	return *n;
       n = &n->next();
     }
-    return OT::Null (NE_TYPEINFO);
+    return Null (NE_TYPEINFO);
   }
 
   protected:
@@ -447,7 +447,7 @@
   inline const NE_RESOURCE_TABLE& get_resource_table () const
   {
     if (magic != 0x454E) // Only NE containers are support for now, NE == 0x454E
-      return OT::Null (NE_RESOURCE_TABLE);
+      return Null (NE_RESOURCE_TABLE);
     return this+rsrctab;
   }
 
@@ -542,8 +542,7 @@
 
   OT::Sanitizer<LE_IMAGE_DOS_HEADER> sanitizer;
   hb_blob_t *font_blob = sanitizer.sanitize (blob);
-  const LE_IMAGE_DOS_HEADER* dos_header =
-    OT::Sanitizer<LE_IMAGE_DOS_HEADER>::lock_instance (font_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 ();
diff --git a/src/hb-aat-layout-common-private.hh b/src/hb-aat-layout-common-private.hh
index f7a7b86..cf30822 100644
--- a/src/hb-aat-layout-common-private.hh
+++ b/src/hb-aat-layout-common-private.hh
@@ -134,7 +134,7 @@
 
   protected:
   BinSearchHeader	header;
-  HBUINT8			bytes[VAR];
+  HBUINT8		bytes[VAR];
   public:
   DEFINE_SIZE_ARRAY (10, bytes);
 };
diff --git a/src/hb-aat-layout.cc b/src/hb-aat-layout.cc
index ad85849..7784fae 100644
--- a/src/hb-aat-layout.cc
+++ b/src/hb-aat-layout.cc
@@ -44,6 +44,7 @@
  * morx/kerx/trak
  */
 
+#if 0
 static inline const AAT::ankr&
 _get_ankr (hb_face_t *face, hb_blob_t **blob = nullptr)
 {
@@ -51,7 +52,7 @@
   {
     if (blob)
       *blob = hb_blob_get_empty ();
-    return OT::Null(AAT::ankr);
+    return Null(AAT::ankr);
   }
   hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
   const AAT::ankr& ankr = *(layout->ankr.get ());
@@ -67,7 +68,7 @@
   {
     if (blob)
       *blob = hb_blob_get_empty ();
-    return OT::Null(AAT::kerx);
+    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. */
@@ -84,7 +85,7 @@
   {
     if (blob)
       *blob = hb_blob_get_empty ();
-    return OT::Null(AAT::morx);
+    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. */
@@ -101,7 +102,7 @@
   {
     if (blob)
       *blob = hb_blob_get_empty ();
-    return OT::Null(AAT::trak);
+    return Null(AAT::trak);
   }
   hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
   const AAT::trak& trak = *(layout->trak.get ());
@@ -109,6 +110,7 @@
     *blob = layout->trak.blob;
   return trak;
 }
+#endif
 
 // static inline void
 // _hb_aat_layout_create (hb_face_t *face)
@@ -116,27 +118,30 @@
 //   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));
-//   OT::Sanitizer<AAT::morx>::lock_instance (morx_blob);
+//   morx_blob->as<AAT::morx> ();
 
 //   if (0)
 //   {
-//     OT::Sanitizer<AAT::Lookup<OT::GlyphID> >::lock_instance (morx_blob)->get_value (1, face->get_num_glyphs ());
+//     morx_blob->as<AAT::Lookup<OT::GlyphID> > ()->get_value (1, face->get_num_glyphs ());
 //   }
 // }
 
 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
 hb_aat_layout_position (hb_font_t *font, hb_buffer_t *buffer)
 {
+#if 0
   hb_blob_t *blob;
   const AAT::ankr& ankr = _get_ankr (font->face, &blob);
   const AAT::kerx& kerx = _get_kerx (font->face, &blob);
@@ -145,4 +150,5 @@
   AAT::hb_aat_apply_context_t c (font, buffer, blob);
   kerx.apply (&c, &ankr);
   trak.apply (&c);
+#endif
 }
diff --git a/src/hb-blob-private.hh b/src/hb-blob-private.hh
new file mode 100644
index 0000000..b72fa72
--- /dev/null
+++ b/src/hb-blob-private.hh
@@ -0,0 +1,88 @@
+/*
+ * Copyright © 2009  Red Hat, Inc.
+ * 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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_BLOB_PRIVATE_HH
+#define HB_BLOB_PRIVATE_HH
+
+#include "hb-private.hh"
+
+#include "hb-object-private.hh"
+
+
+/*
+ * hb_blob_t
+ */
+
+struct hb_blob_t
+{
+  inline void fini_shallow (void)
+  {
+    destroy_user_data ();
+  }
+
+  inline void destroy_user_data (void)
+  {
+    if (destroy)
+    {
+      destroy (user_data);
+      user_data = nullptr;
+      destroy = nullptr;
+    }
+  }
+
+  HB_INTERNAL bool try_make_writable (void);
+  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
+  {
+    return unlikely (!data) ? &Null(Type) : reinterpret_cast<const Type *> (data);
+  }
+
+  public:
+  hb_object_header_t header;
+  ASSERT_POD ();
+
+  bool immutable;
+
+  const char *data;
+  unsigned int length;
+  hb_memory_mode_t mode;
+
+  void *user_data;
+  hb_destroy_func_t destroy;
+};
+
+
+#endif /* HB_BLOB_PRIVATE_HH */
diff --git a/src/hb-blob.cc b/src/hb-blob.cc
index 710765d..b131970 100644
--- a/src/hb-blob.cc
+++ b/src/hb-blob.cc
@@ -31,8 +31,7 @@
 
 #include "hb-private.hh"
 #include "hb-debug.hh"
-
-#include "hb-object-private.hh"
+#include "hb-blob-private.hh"
 
 #ifdef HAVE_SYS_MMAN_H
 #ifdef HAVE_UNISTD_H
@@ -49,32 +48,6 @@
 #include <fcntl.h>
 
 
-struct hb_blob_t {
-  hb_object_header_t header;
-  ASSERT_POD ();
-
-  bool immutable;
-
-  const char *data;
-  unsigned int length;
-  hb_memory_mode_t mode;
-
-  void *user_data;
-  hb_destroy_func_t destroy;
-};
-
-
-static bool _try_writable (hb_blob_t *blob);
-
-static void
-_hb_blob_destroy_user_data (hb_blob_t *blob)
-{
-  if (blob->destroy) {
-    blob->destroy (blob->user_data);
-    blob->user_data = nullptr;
-    blob->destroy = nullptr;
-  }
-}
 
 /**
  * hb_blob_create: (skip)
@@ -118,7 +91,7 @@
 
   if (blob->mode == HB_MEMORY_MODE_DUPLICATE) {
     blob->mode = HB_MEMORY_MODE_READONLY;
-    if (!_try_writable (blob)) {
+    if (!blob->try_make_writable ()) {
       hb_blob_destroy (blob);
       return hb_blob_get_empty ();
     }
@@ -264,7 +237,7 @@
 {
   if (!hb_object_destroy (blob)) return;
 
-  _hb_blob_destroy_user_data (blob);
+  blob->fini_shallow ();
 
   free (blob);
 }
@@ -399,7 +372,7 @@
 char *
 hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length)
 {
-  if (!_try_writable (blob)) {
+  if (!blob->try_make_writable ()) {
     if (length)
       *length = 0;
 
@@ -413,8 +386,8 @@
 }
 
 
-static hb_bool_t
-_try_make_writable_inplace_unix (hb_blob_t *blob)
+bool
+hb_blob_t::try_make_writable_inplace_unix (void)
 {
 #if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT)
   uintptr_t pagesize = -1, mask, length;
@@ -429,25 +402,25 @@
 #endif
 
   if ((uintptr_t) -1L == pagesize) {
-    DEBUG_MSG_FUNC (BLOB, blob, "failed to get pagesize: %s", strerror (errno));
+    DEBUG_MSG_FUNC (BLOB, this, "failed to get pagesize: %s", strerror (errno));
     return false;
   }
-  DEBUG_MSG_FUNC (BLOB, blob, "pagesize is %lu", (unsigned long) pagesize);
+  DEBUG_MSG_FUNC (BLOB, this, "pagesize is %lu", (unsigned long) pagesize);
 
   mask = ~(pagesize-1);
-  addr = (const char *) (((uintptr_t) blob->data) & mask);
-  length = (const char *) (((uintptr_t) blob->data + blob->length + pagesize-1) & mask)  - addr;
-  DEBUG_MSG_FUNC (BLOB, blob,
+  addr = (const char *) (((uintptr_t) this->data) & mask);
+  length = (const char *) (((uintptr_t) this->data + this->length + pagesize-1) & mask)  - addr;
+  DEBUG_MSG_FUNC (BLOB, this,
 		  "calling mprotect on [%p..%p] (%lu bytes)",
 		  addr, addr+length, (unsigned long) length);
   if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) {
-    DEBUG_MSG_FUNC (BLOB, blob, "mprotect failed: %s", strerror (errno));
+    DEBUG_MSG_FUNC (BLOB, this, "mprotect failed: %s", strerror (errno));
     return false;
   }
 
-  blob->mode = HB_MEMORY_MODE_WRITABLE;
+  this->mode = HB_MEMORY_MODE_WRITABLE;
 
-  DEBUG_MSG_FUNC (BLOB, blob,
+  DEBUG_MSG_FUNC (BLOB, this,
 		  "successfully made [%p..%p] (%lu bytes) writable\n",
 		  addr, addr+length, (unsigned long) length);
   return true;
@@ -456,57 +429,61 @@
 #endif
 }
 
-static bool
-_try_writable_inplace (hb_blob_t *blob)
+bool
+hb_blob_t::try_make_writable_inplace (void)
 {
-  DEBUG_MSG_FUNC (BLOB, blob, "making writable inplace\n");
+  DEBUG_MSG_FUNC (BLOB, this, "making writable inplace\n");
 
-  if (_try_make_writable_inplace_unix (blob))
+  if (this->try_make_writable_inplace_unix ())
     return true;
 
-  DEBUG_MSG_FUNC (BLOB, blob, "making writable -> FAILED\n");
+  DEBUG_MSG_FUNC (BLOB, this, "making writable -> FAILED\n");
 
   /* Failed to make writable inplace, mark that */
-  blob->mode = HB_MEMORY_MODE_READONLY;
+  this->mode = HB_MEMORY_MODE_READONLY;
   return false;
 }
 
-static bool
-_try_writable (hb_blob_t *blob)
+bool
+hb_blob_t::try_make_writable (void)
 {
-  if (blob->immutable)
+  if (this->immutable)
     return false;
 
-  if (blob->mode == HB_MEMORY_MODE_WRITABLE)
+  if (this->mode == HB_MEMORY_MODE_WRITABLE)
     return true;
 
-  if (blob->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && _try_writable_inplace (blob))
+  if (this->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && this->try_make_writable_inplace ())
     return true;
 
-  if (blob->mode == HB_MEMORY_MODE_WRITABLE)
+  if (this->mode == HB_MEMORY_MODE_WRITABLE)
     return true;
 
 
-  DEBUG_MSG_FUNC (BLOB, blob, "current data is -> %p\n", blob->data);
+  DEBUG_MSG_FUNC (BLOB, this, "current data is -> %p\n", this->data);
 
   char *new_data;
 
-  new_data = (char *) malloc (blob->length);
+  new_data = (char *) malloc (this->length);
   if (unlikely (!new_data))
     return false;
 
-  DEBUG_MSG_FUNC (BLOB, blob, "dupped successfully -> %p\n", blob->data);
+  DEBUG_MSG_FUNC (BLOB, this, "dupped successfully -> %p\n", this->data);
 
-  memcpy (new_data, blob->data, blob->length);
-  _hb_blob_destroy_user_data (blob);
-  blob->mode = HB_MEMORY_MODE_WRITABLE;
-  blob->data = new_data;
-  blob->user_data = new_data;
-  blob->destroy = free;
+  memcpy (new_data, this->data, this->length);
+  this->destroy_user_data ();
+  this->mode = HB_MEMORY_MODE_WRITABLE;
+  this->data = new_data;
+  this->user_data = new_data;
+  this->destroy = free;
 
   return true;
 }
 
+/*
+ * Mmap
+ */
+
 #if defined(_WIN32) || defined(__CYGWIN__)
 #include <windows.h>
 #include <io.h>
diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
index dc0639f..f7a0495 100644
--- a/src/hb-buffer.cc
+++ b/src/hb-buffer.cc
@@ -687,6 +687,8 @@
   /* If direction is set to INVALID, guess from script */
   if (props.direction == HB_DIRECTION_INVALID) {
     props.direction = hb_script_get_horizontal_direction (props.script);
+    if (props.direction == HB_DIRECTION_INVALID)
+      props.direction = HB_DIRECTION_LTR;
   }
 
   /* If language is not set, use default language from locale */
@@ -1489,6 +1491,8 @@
  * Next, if buffer direction is not set (ie. is %HB_DIRECTION_INVALID),
  * it will be set to the natural horizontal direction of the
  * buffer script as returned by hb_script_get_horizontal_direction().
+ * If hb_script_get_horizontal_direction() returns %HB_DIRECTION_INVALID,
+ * then %HB_DIRECTION_LTR is used.
  *
  * Finally, if buffer language is not set (ie. is %HB_LANGUAGE_INVALID),
  * it will be set to the process's default language as returned by
diff --git a/src/hb-common.cc b/src/hb-common.cc
index f38ebb0..956855c 100644
--- a/src/hb-common.cc
+++ b/src/hb-common.cc
@@ -536,6 +536,12 @@
     case HB_SCRIPT_ADLAM:
 
       return HB_DIRECTION_RTL;
+
+
+    /* https://github.com/harfbuzz/harfbuzz/issues/1000 */
+    case HB_SCRIPT_OLD_ITALIC:
+
+      return HB_DIRECTION_INVALID;
   }
 
   return HB_DIRECTION_LTR;
diff --git a/src/hb-face.cc b/src/hb-face.cc
index d8af8c1..0127141 100644
--- a/src/hb-face.cc
+++ b/src/hb-face.cc
@@ -29,6 +29,7 @@
 #include "hb-private.hh"
 
 #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"
@@ -134,7 +135,7 @@
   if (tag == HB_TAG_NONE)
     return hb_blob_reference (data->blob);
 
-  const OT::OpenTypeFontFile &ot_file = *OT::Sanitizer<OT::OpenTypeFontFile>::lock_instance (data->blob);
+  const OT::OpenTypeFontFile &ot_file = *data->blob->as<OT::OpenTypeFontFile> ();
   const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index);
 
   const OT::OpenTypeTable &table = ot_face.get_table_by_tag (tag);
@@ -425,7 +426,7 @@
 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 = OT::Sanitizer<OT::head>::lock_instance (head_blob);
+  const OT::head *head_table = head_blob->as<OT::head> ();
   upem = head_table->get_upem ();
   hb_blob_destroy (head_blob);
 }
@@ -469,7 +470,7 @@
 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 = OT::Sanitizer<OT::maxp>::lock_instance (maxp_blob);
+  const OT::maxp *maxp_table = maxp_blob->as<OT::maxp> ();
   num_glyphs = maxp_table->get_num_glyphs ();
   hb_blob_destroy (maxp_blob);
 }
@@ -499,7 +500,7 @@
 
   hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) face->user_data;
 
-  const OT::OpenTypeFontFile &ot_file = *OT::Sanitizer<OT::OpenTypeFontFile>::lock_instance (data->blob);
+  const OT::OpenTypeFontFile &ot_file = *data->blob->as<OT::OpenTypeFontFile> ();
   const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index);
 
   return ot_face.get_table_tags (start_offset, table_count, table_tags);
diff --git a/src/hb-object-private.hh b/src/hb-object-private.hh
index 75a72f4..1f61dce 100644
--- a/src/hb-object-private.hh
+++ b/src/hb-object-private.hh
@@ -62,7 +62,6 @@
 
 /* user_data */
 
-#define HB_USER_DATA_ARRAY_INIT {HB_MUTEX_INIT, HB_LOCKABLE_SET_INIT}
 struct hb_user_data_array_t
 {
   struct hb_user_data_item_t {
@@ -97,9 +96,9 @@
 struct hb_object_header_t
 {
   hb_reference_count_t ref_count;
-  hb_user_data_array_t user_data;
+  hb_user_data_array_t *user_data;
 
-#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INIT, HB_USER_DATA_ARRAY_INIT}
+#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INIT, nullptr}
 
   private:
   ASSERT_POD ();
@@ -133,7 +132,7 @@
 static inline void hb_object_init (Type *obj)
 {
   obj->header.ref_count.init (1);
-  obj->header.user_data.init ();
+  obj->header.user_data = nullptr;
 }
 template <typename Type>
 static inline bool hb_object_is_inert (const Type *obj)
@@ -172,7 +171,11 @@
 static inline void hb_object_fini (Type *obj)
 {
   obj->header.ref_count.fini (); /* Do this before user_data */
-  obj->header.user_data.fini ();
+  if (obj->header.user_data)
+  {
+    obj->header.user_data->fini ();
+    free (obj->header.user_data);
+  }
 }
 template <typename Type>
 static inline bool hb_object_set_user_data (Type               *obj,
@@ -184,17 +187,34 @@
   if (unlikely (!obj || hb_object_is_inert (obj)))
     return false;
   assert (hb_object_is_valid (obj));
-  return obj->header.user_data.set (key, data, destroy, replace);
+
+retry:
+  hb_user_data_array_t *user_data = (hb_user_data_array_t *) hb_atomic_ptr_get (&obj->header.user_data);
+  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)))
+    {
+      user_data->fini ();
+      free (user_data);
+      goto retry;
+    }
+  }
+
+  return user_data->set (key, data, destroy, replace);
 }
 
 template <typename Type>
 static inline void *hb_object_get_user_data (Type               *obj,
 					     hb_user_data_key_t *key)
 {
-  if (unlikely (!obj || hb_object_is_inert (obj)))
+  if (unlikely (!obj || hb_object_is_inert (obj) || !obj->header.user_data))
     return nullptr;
   assert (hb_object_is_valid (obj));
-  return obj->header.user_data.get (key);
+  return obj->header.user_data->get (key);
 }
 
 
diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh
index 1f22b18..775cb39 100644
--- a/src/hb-open-type-private.hh
+++ b/src/hb-open-type-private.hh
@@ -31,6 +31,7 @@
 
 #include "hb-private.hh"
 #include "hb-debug.hh"
+#include "hb-blob-private.hh"
 #include "hb-face-private.hh"
 
 
@@ -127,46 +128,6 @@
 
 
 /*
- * Null objects
- */
-
-/* Global nul-content Null pool.  Enlarge as necessary. */
-
-#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
-#else
-extern HB_INTERNAL
-#endif
-const 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 const Type& Null (void) {
-  static_assert (sizeof (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
-  return *CastP<Type> (_hb_NullPool);
-}
-
-/* Specializaiton for arbitrary-content arbitrary-sized Null objects. */
-#define DEFINE_NULL_DATA(Type, data) \
-static const char _Null##Type[sizeof (Type) + 1] = data; /* +1 is for nul-termination in data */ \
-template <> \
-/*static*/ inline const Type& Null<Type> (void) { \
-  return *CastP<Type> (_Null##Type); \
-} /* The following line really exists such that we end in a place needing semicolon */ \
-static_assert (Type::min_size + 1 <= sizeof (_Null##Type), "Null pool too small.  Enlarge.")
-
-/* Accessor macro. */
-#define Null(Type) Null<Type>()
-
-
-/*
  * Dispatch
  */
 
@@ -225,7 +186,7 @@
   inline void start_processing (void)
   {
     this->start = hb_blob_get_data (this->blob, nullptr);
-    this->end = this->start + hb_blob_get_length (this->blob);
+    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);
@@ -368,7 +329,7 @@
       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 + hb_blob_get_length (blob);
+	c->end = c->start + blob->length;
 
 	if (c->start) {
 	  c->writable = true;
@@ -383,19 +344,17 @@
 
     DEBUG_MSG_FUNC (SANITIZE, c->start, sane ? "PASSED" : "FAILED");
     if (sane)
+    {
+      blob->lock ();
       return blob;
-    else {
+    }
+    else
+    {
       hb_blob_destroy (blob);
       return hb_blob_get_empty ();
     }
   }
 
-  static const Type* lock_instance (hb_blob_t *blob) {
-    hb_blob_make_immutable (blob);
-    const char *base = hb_blob_get_data (blob, nullptr);
-    return unlikely (!base) ? &Null(Type) : CastP<Type> (base);
-  }
-
   inline void set_num_glyphs (unsigned int num_glyphs) { c->num_glyphs = num_glyphs; }
 
   private:
@@ -726,7 +685,6 @@
   public:
   DEFINE_SIZE_STATIC (4);
 };
-DEFINE_NULL_DATA (Tag, "    ");
 
 /* Glyph index number, same as uint16 (length = 16 bits) */
 typedef HBUINT16 GlyphID;
@@ -738,7 +696,6 @@
 struct Index : HBUINT16 {
   static const unsigned int NOT_FOUND_INDEX = 0xFFFFu;
 };
-DEFINE_NULL_DATA (Index, "\xff\xff");
 
 /* Offset, Null offset = 0 */
 template <typename Type>
@@ -1224,7 +1181,7 @@
 
 /* Lazy struct and blob loaders. */
 
-/* Logic is shared between hb_lazy_loader_t and hb_lazy_table_loader_t */
+/* Logic is shared between hb_lazy_loader_t and hb_table_lazy_loader_t */
 template <typename T>
 struct hb_lazy_loader_t
 {
@@ -1236,7 +1193,7 @@
 
   inline void fini (void)
   {
-    if (instance && instance != &OT::Null(T))
+    if (instance && instance != &Null(T))
     {
       instance->fini();
       free (instance);
@@ -1251,12 +1208,12 @@
     {
       p = (T *) calloc (1, sizeof (T));
       if (unlikely (!p))
-        p = const_cast<T *> (&OT::Null(T));
+        p = const_cast<T *> (&Null(T));
       else
 	p->init (face);
       if (unlikely (!hb_atomic_ptr_cmpexch (const_cast<T **>(&instance), nullptr, p)))
       {
-	if (p != &OT::Null(T))
+	if (p != &Null(T))
 	  p->fini ();
 	goto retry;
       }
@@ -1274,15 +1231,14 @@
   T *instance;
 };
 
-/* Logic is shared between hb_lazy_loader_t and hb_lazy_table_loader_t */
+/* Logic is shared between hb_lazy_loader_t and hb_table_lazy_loader_t */
 template <typename T>
-struct hb_lazy_table_loader_t
+struct hb_table_lazy_loader_t
 {
   inline void init (hb_face_t *face_)
   {
     face = face_;
     blob = nullptr;
-    instance = nullptr;
   }
 
   inline void fini (void)
@@ -1293,19 +1249,18 @@
   inline const T* get (void) const
   {
   retry:
-    T *p = (T *) hb_atomic_ptr_get (&instance);
-    if (unlikely (!p))
+    hb_blob_t *blob_ = (hb_blob_t *) hb_atomic_ptr_get (&blob);
+    if (unlikely (!blob_))
     {
-      hb_blob_t *blob_ = OT::Sanitizer<T>().sanitize (face->reference_table (T::tableTag));
-      p = const_cast<T *>(OT::Sanitizer<T>::lock_instance (blob_));
-      if (!hb_atomic_ptr_cmpexch (const_cast<T **>(&instance), nullptr, p))
+      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 p;
+    return blob_->as<T> ();
   }
 
   inline const T* operator-> (void) const
@@ -1313,10 +1268,9 @@
     return get();
   }
 
+  private:
   hb_face_t *face;
   mutable hb_blob_t *blob;
-  private:
-  mutable T *instance;
 };
 
 
diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh
index dc55c0d..5e215a4 100644
--- a/src/hb-ot-cmap-table.hh
+++ b/src/hb-ot-cmap-table.hh
@@ -892,7 +892,7 @@
     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 = OT::Sanitizer<OT::cmap>::lock_instance (this->blob);
+      const OT::cmap *cmap = this->blob->as<OT::cmap> ();
       const OT::CmapSubtable *subtable = nullptr;
       const OT::CmapSubtableFormat14 *subtable_uvs = nullptr;
 
@@ -913,7 +913,7 @@
 	if (subtable) symbol = true;
       }
       /* Meh. */
-      if (!subtable) subtable = &OT::Null(OT::CmapSubtable);
+      if (!subtable) subtable = &Null(OT::CmapSubtable);
 
       /* UVS subtable. */
       if (!subtable_uvs)
@@ -923,7 +923,7 @@
 	  subtable_uvs = &st->u.format14;
       }
       /* Meh. */
-      if (!subtable_uvs) subtable_uvs = &OT::Null(OT::CmapSubtableFormat14);
+      if (!subtable_uvs) subtable_uvs = &Null(OT::CmapSubtableFormat14);
 
       this->uvs_table = subtable_uvs;
 
diff --git a/src/hb-ot-color-cbdt-table.hh b/src/hb-ot-color-cbdt-table.hh
index 528d144..f4207f2 100644
--- a/src/hb-ot-color-cbdt-table.hh
+++ b/src/hb-ot-color-cbdt-table.hh
@@ -403,8 +403,8 @@
 	cbdt = nullptr;
 	return;  /* Not a bitmap font. */
       }
-      cblc = Sanitizer<CBLC>::lock_instance (cblc_blob);
-      cbdt = Sanitizer<CBDT>::lock_instance (cbdt_blob);
+      cblc = cblc_blob->as<CBLC> ();
+      cbdt = cbdt_blob->as<CBDT> ();
 
     }
 
diff --git a/src/hb-ot-color-sbix-table.hh b/src/hb-ot-color-sbix-table.hh
index 09d0cc5..09a9517 100644
--- a/src/hb-ot-color-sbix-table.hh
+++ b/src/hb-ot-color-sbix-table.hh
@@ -102,7 +102,7 @@
       sanitizer.set_num_glyphs (num_glyphs);
       sbix_blob = sanitizer.sanitize (face->reference_table (HB_OT_TAG_sbix));
       sbix_len = hb_blob_get_length (sbix_blob);
-      sbix_table = OT::Sanitizer<OT::sbix>::lock_instance (sbix_blob);
+      sbix_table = sbix_blob->as<OT::sbix> ();
 
     }
 
diff --git a/src/hb-ot-color-svg-table.hh b/src/hb-ot-color-svg-table.hh
index 47b3292..ed6cf97 100644
--- a/src/hb-ot-color-svg-table.hh
+++ b/src/hb-ot-color-svg-table.hh
@@ -99,7 +99,7 @@
       OT::Sanitizer<OT::SVG> sanitizer;
       svg_blob = sanitizer.sanitize (face->reference_table (HB_OT_TAG_SVG));
       svg_len = hb_blob_get_length (svg_blob);
-      svg = OT::Sanitizer<OT::SVG>::lock_instance (svg_blob);
+      svg = svg_blob->as<OT::SVG> ();
 
     }
 
diff --git a/src/hb-ot-color.cc b/src/hb-ot-color.cc
index ceebe0b..86171c6 100644
--- a/src/hb-ot-color.cc
+++ b/src/hb-ot-color.cc
@@ -44,7 +44,7 @@
 static inline const OT::COLR&
 _get_colr (hb_face_t *face)
 {
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::COLR);
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::COLR);
   hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
   return *(layout->colr.get ());
 }
@@ -52,7 +52,7 @@
 static inline const OT::CPAL&
 _get_cpal (hb_face_t *face)
 {
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::CPAL);
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::CPAL);
   hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
   return *(layout->cpal.get ());
 }
diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh
index 4b02153..8c2bfd0 100644
--- a/src/hb-ot-glyf-table.hh
+++ b/src/hb-ot-glyf-table.hh
@@ -237,7 +237,7 @@
       memset (this, 0, sizeof (accelerator_t));
 
       hb_blob_t *head_blob = Sanitizer<head>().sanitize (face->reference_table (HB_OT_TAG_head));
-      const head *head_table = Sanitizer<head>::lock_instance (head_blob);
+      const head *head_table = head_blob->as<head> ();
       if (head_table == &Null(head) || (unsigned int) 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. */
@@ -248,9 +248,9 @@
       hb_blob_destroy (head_blob);
 
       loca_blob = Sanitizer<loca>().sanitize (face->reference_table (HB_OT_TAG_loca));
-      loca_table = Sanitizer<loca>::lock_instance (loca_blob);
+      loca_table = loca_blob->as<loca> ();
       glyf_blob = Sanitizer<glyf>().sanitize (face->reference_table (HB_OT_TAG_glyf));
-      glyf_table = Sanitizer<glyf>::lock_instance (glyf_blob);
+      glyf_table = glyf_blob->as<glyf> ();
 
       num_glyphs = MAX (1u, hb_blob_get_length (loca_blob) / (short_offset ? 2 : 4)) - 1;
       glyf_len = hb_blob_get_length (glyf_blob);
diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh
index 5944688..c07763e 100644
--- a/src/hb-ot-hmtx-table.hh
+++ b/src/hb-ot-hmtx-table.hh
@@ -196,7 +196,7 @@
       if (T::os2Tag)
       {
 	hb_blob_t *os2_blob = Sanitizer<os2> ().sanitize (face->reference_table (T::os2Tag));
-	const os2 *os2_table = Sanitizer<os2>::lock_instance (os2_blob);
+	const os2 *os2_table = os2_blob->as<os2> ();
 #define USE_TYPO_METRICS (1u<<7)
 	if (0 != (os2_table->fsSelection & USE_TYPO_METRICS))
 	{
@@ -209,7 +209,7 @@
       }
 
       hb_blob_t *_hea_blob = Sanitizer<H> ().sanitize (face->reference_table (H::tableTag));
-      const H *_hea_table = Sanitizer<H>::lock_instance (_hea_blob);
+      const H *_hea_table = _hea_blob->as<H> ();
       num_advances = _hea_table->numberOfLongMetrics;
       if (!got_font_extents)
       {
@@ -238,10 +238,10 @@
 	hb_blob_destroy (blob);
 	blob = hb_blob_get_empty ();
       }
-      table = Sanitizer<hmtxvmtx>::lock_instance (blob);
+      table = blob->as<hmtxvmtx> ();
 
       var_blob = Sanitizer<HVARVVAR> ().sanitize (face->reference_table (T::variationsTag));
-      var_table = Sanitizer<HVARVVAR>::lock_instance (var_blob);
+      var_table = var_blob->as<HVARVVAR> ();
     }
 
     inline void fini (void)
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index ec33169..b0fdea4 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -363,8 +363,8 @@
     inline void init (hb_face_t *face)
     {
       blob = Sanitizer<kern>().sanitize (face->reference_table (HB_OT_TAG_kern));
-      table = Sanitizer<kern>::lock_instance (blob);
-      table_length = hb_blob_get_length (blob);
+      table = blob->as<kern> ();
+      table_length = blob->length;
     }
     inline void fini (void)
     {
diff --git a/src/hb-ot-layout-common-private.hh b/src/hb-ot-layout-common-private.hh
index bd193f9..bec694e 100644
--- a/src/hb-ot-layout-common-private.hh
+++ b/src/hb-ot-layout-common-private.hh
@@ -165,7 +165,6 @@
   public:
   DEFINE_SIZE_STATIC (6);
 };
-DEFINE_NULL_DATA (RangeRecord, "\000\001");
 
 
 struct IndexArray : ArrayOf<Index>
@@ -225,7 +224,6 @@
   public:
   DEFINE_SIZE_ARRAY (6, featureIndex);
 };
-DEFINE_NULL_DATA (LangSys, "\0\0\xFF\xFF");
 
 
 struct Script
@@ -247,7 +245,16 @@
   { return langSys.find_index (tag, index); }
 
   inline bool has_default_lang_sys (void) const { return defaultLangSys != 0; }
-  inline const LangSys& get_default_lang_sys (void) const { return this+defaultLangSys; }
+  inline const LangSys& get_default_lang_sys (void) const
+  {
+    if (!defaultLangSys)
+    {
+      /* This is the ONLY place where our null data is not all zeros.
+       * So, return special data instead of using the null pool. */
+      return *reinterpret_cast<const LangSys *> ("\0\0\xFF\xFF");
+    }
+    return this+defaultLangSys;
+  }
 
   inline bool sanitize (hb_sanitize_context_t *c,
 			const Record<Script>::sanitize_closure_t * = nullptr) const
diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh
index 2454920..b2f974b 100644
--- a/src/hb-ot-layout-private.hh
+++ b/src/hb-ot-layout-private.hh
@@ -172,16 +172,10 @@
   const struct OT::GPOS *gpos;
 
   /* TODO Move the following out of this struct. */
-  OT::hb_lazy_table_loader_t<struct OT::BASE> base;
-  OT::hb_lazy_table_loader_t<struct OT::COLR> colr;
-  OT::hb_lazy_table_loader_t<struct OT::CPAL> cpal;
-  OT::hb_lazy_table_loader_t<struct OT::MATH> math;
-  OT::hb_lazy_table_loader_t<struct OT::fvar> fvar;
-  OT::hb_lazy_table_loader_t<struct OT::avar> avar;
-  OT::hb_lazy_table_loader_t<struct AAT::ankr> ankr;
-  OT::hb_lazy_table_loader_t<struct AAT::kerx> kerx;
-  OT::hb_lazy_table_loader_t<struct AAT::morx> morx;
-  OT::hb_lazy_table_loader_t<struct AAT::trak> trak;
+  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;
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index 42d29aa..368a846 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -30,23 +30,24 @@
 
 #include "hb-open-type-private.hh"
 #include "hb-ot-layout-private.hh"
+#include "hb-ot-map-private.hh"
 
-#include "hb-ot-layout-base-table.hh"
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-ot-layout-gsub-table.hh"
 #include "hb-ot-layout-gpos-table.hh"
-#include "hb-ot-layout-jstf-table.hh" // Just so we compile it; unused otherwise.
-#include "hb-ot-name-table.hh" // Just so we compile it; unused otherwise.
+
+// Just so we compile them; unused otherwise:
+#include "hb-ot-layout-base-table.hh"
+#include "hb-ot-layout-jstf-table.hh"
 #include "hb-ot-color-colr-table.hh"
 #include "hb-ot-color-cpal-table.hh"
-#include "hb-ot-color-sbix-table.hh" // Just so we compile it; unused otherwise.
-#include "hb-ot-color-svg-table.hh" // Just so we compile it; unused otherwise.
-
-#include "hb-ot-map-private.hh"
+#include "hb-ot-color-sbix-table.hh"
+#include "hb-ot-color-svg-table.hh"
+#include "hb-ot-name-table.hh"
 
 
 #ifndef HB_NO_VISIBILITY
-const void * const OT::_hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {};
+const void * const _hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {};
 #endif
 
 
@@ -58,24 +59,17 @@
     return nullptr;
 
   layout->gdef_blob = OT::Sanitizer<OT::GDEF>().sanitize (face->reference_table (HB_OT_TAG_GDEF));
-  layout->gdef = OT::Sanitizer<OT::GDEF>::lock_instance (layout->gdef_blob);
+  layout->gdef = layout->gdef_blob->as<OT::GDEF> ();
 
   layout->gsub_blob = OT::Sanitizer<OT::GSUB>().sanitize (face->reference_table (HB_OT_TAG_GSUB));
-  layout->gsub = OT::Sanitizer<OT::GSUB>::lock_instance (layout->gsub_blob);
+  layout->gsub = layout->gsub_blob->as<OT::GSUB> ();
 
   layout->gpos_blob = OT::Sanitizer<OT::GPOS>().sanitize (face->reference_table (HB_OT_TAG_GPOS));
-  layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob);
+  layout->gpos = layout->gpos_blob->as<OT::GPOS> ();
 
-  layout->base.init (face);
-  layout->colr.init (face);
-  layout->cpal.init (face);
   layout->math.init (face);
   layout->fvar.init (face);
   layout->avar.init (face);
-  layout->ankr.init (face);
-  layout->kerx.init (face);
-  layout->morx.init (face);
-  layout->trak.init (face);
 
   {
     /*
@@ -83,9 +77,9 @@
      * 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 = hb_blob_get_length (layout->gdef_blob);
-    unsigned int gsub_len = hb_blob_get_length (layout->gsub_blob);
-    unsigned int gpos_len = hb_blob_get_length (layout->gpos_blob);
+    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)
@@ -108,7 +102,7 @@
        * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html
        */
      if (3 == layout->gdef->get_glyph_class (5))
-       layout->gdef = &OT::Null(OT::GDEF);
+       layout->gdef = &Null(OT::GDEF);
     }
     else if (0
       /* sha1sum:96eda93f7d33e79962451c6c39a6b51ee893ce8c  tahoma.ttf from Windows 8 */
@@ -180,7 +174,7 @@
        *     https://bugzilla.mozilla.org/show_bug.cgi?id=1279693
        *     https://bugzilla.mozilla.org/show_bug.cgi?id=1279875
        */
-      layout->gdef = &OT::Null(OT::GDEF);
+      layout->gdef = &Null(OT::GDEF);
     }
   }
 
@@ -222,16 +216,9 @@
   hb_blob_destroy (layout->gsub_blob);
   hb_blob_destroy (layout->gpos_blob);
 
-  layout->base.fini ();
-  layout->colr.fini ();
-  layout->cpal.fini ();
   layout->math.fini ();
   layout->fvar.fini ();
   layout->avar.fini ();
-  layout->ankr.fini ();
-  layout->kerx.fini ();
-  layout->morx.fini ();
-  layout->trak.fini ();
 
   free (layout);
 }
@@ -239,7 +226,7 @@
 // static inline const OT::BASE&
 // _get_base (hb_face_t *face)
 // {
-//   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::BASE);
+//   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::BASE);
 //   hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
 //   return *(layout->base.get ());
 // }
@@ -247,19 +234,19 @@
 static inline const OT::GDEF&
 _get_gdef (hb_face_t *face)
 {
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GDEF);
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::GDEF);
   return *hb_ot_layout_from_face (face)->gdef;
 }
 static inline const OT::GSUB&
 _get_gsub (hb_face_t *face)
 {
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GSUB);
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::GSUB);
   return *hb_ot_layout_from_face (face)->gsub;
 }
 static inline const OT::GPOS&
 _get_gpos (hb_face_t *face)
 {
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GPOS);
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::GPOS);
   return *hb_ot_layout_from_face (face)->gpos;
 }
 
@@ -331,7 +318,7 @@
   switch (table_tag) {
     case HB_OT_TAG_GSUB: return _get_gsub (face);
     case HB_OT_TAG_GPOS: return _get_gpos (face);
-    default:             return OT::Null(OT::GSUBGPOS);
+    default:             return Null(OT::GSUBGPOS);
   }
 }
 
@@ -911,7 +898,7 @@
 hb_bool_t
 hb_ot_layout_has_substitution (hb_face_t *face)
 {
-  return &_get_gsub (face) != &OT::Null(OT::GSUB);
+  return &_get_gsub (face) != &Null(OT::GSUB);
 }
 
 /**
@@ -975,7 +962,7 @@
 hb_bool_t
 hb_ot_layout_has_positioning (hb_face_t *face)
 {
-  return &_get_gpos (face) != &OT::Null(OT::GPOS);
+  return &_get_gpos (face) != &Null(OT::GPOS);
 }
 
 void
@@ -1311,5 +1298,5 @@
 // hb_bool_t
 // hb_ot_base_has_data (hb_face_t *face)
 // {
-//   return &_get_base (face) != &OT::Null(OT::BASE);
+//   return &_get_base (face) != &Null(OT::BASE);
 // }
diff --git a/src/hb-ot-map.cc b/src/hb-ot-map.cc
index 54b0ce3..b7cbafa 100644
--- a/src/hb-ot-map.cc
+++ b/src/hb-ot-map.cc
@@ -164,9 +164,6 @@
 						&required_feature_tag[table_index]);
   }
 
-  if (!feature_infos.len)
-    return;
-
   /* Sort features and merge duplicates */
   {
     feature_infos.qsort ();
diff --git a/src/hb-ot-math.cc b/src/hb-ot-math.cc
index f82a073..1667a7d 100644
--- a/src/hb-ot-math.cc
+++ b/src/hb-ot-math.cc
@@ -32,7 +32,7 @@
 static inline const OT::MATH&
 _get_math (hb_face_t *face)
 {
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::MATH);
+  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 ());
 }
@@ -55,7 +55,7 @@
 hb_bool_t
 hb_ot_math_has_data (hb_face_t *face)
 {
-  return &_get_math (face) != &OT::Null(OT::MATH);
+  return &_get_math (face) != &Null(OT::MATH);
 }
 
 /**
diff --git a/src/hb-ot-post-table.hh b/src/hb-ot-post-table.hh
index 0cd97fa..8ba5ee1 100644
--- a/src/hb-ot-post-table.hh
+++ b/src/hb-ot-post-table.hh
@@ -110,8 +110,8 @@
     inline void init (hb_face_t *face)
     {
       blob = Sanitizer<post>().sanitize (face->reference_table (HB_OT_TAG_post));
-      const post *table = Sanitizer<post>::lock_instance (blob);
-      unsigned int table_length = hb_blob_get_length (blob);
+      const post *table = blob->as<post> ();
+      unsigned int table_length = blob->length;
 
       version = table->version.to_int ();
       index_to_offset.init ();
@@ -141,7 +141,7 @@
     inline bool get_glyph_name (hb_codepoint_t glyph,
 				char *buf, unsigned int buf_len) const
     {
-      hb_string_t s = find_glyph_name (glyph);
+      hb_bytes_t s = find_glyph_name (glyph);
       if (!s.len)
         return false;
       if (!buf_len)
@@ -185,7 +185,7 @@
 	}
       }
 
-      hb_string_t st (name, len);
+      hb_bytes_t st (name, len);
       const uint16_t *gid = (const uint16_t *) hb_bsearch_r (&st, gids, count, sizeof (gids[0]), cmp_key, (void *) this);
       if (gid)
       {
@@ -220,23 +220,23 @@
     static inline int cmp_key (const void *pk, const void *po, void *arg)
     {
       const accelerator_t *thiz = (const accelerator_t *) arg;
-      const hb_string_t *key = (const hb_string_t *) pk;
+      const hb_bytes_t *key = (const hb_bytes_t *) pk;
       uint16_t o = * (const uint16_t *) po;
       return thiz->find_glyph_name (o).cmp (*key);
     }
 
-    inline hb_string_t find_glyph_name (hb_codepoint_t glyph) const
+    inline hb_bytes_t find_glyph_name (hb_codepoint_t glyph) const
     {
       if (version == 0x00010000)
       {
 	if (glyph >= NUM_FORMAT1_NAMES)
-	  return hb_string_t ();
+	  return hb_bytes_t ();
 
 	return format1_names (glyph);
       }
 
       if (version != 0x00020000 || glyph >= glyphNameIndex->len)
-	return hb_string_t ();
+	return hb_bytes_t ();
 
       unsigned int index = glyphNameIndex->array[glyph];
       if (index < NUM_FORMAT1_NAMES)
@@ -244,14 +244,14 @@
       index -= NUM_FORMAT1_NAMES;
 
       if (index >= index_to_offset.len)
-	return hb_string_t ();
+	return hb_bytes_t ();
       unsigned int offset = index_to_offset.array[index];
 
       const uint8_t *data = pool + offset;
       unsigned int name_length = *data;
       data++;
 
-      return hb_string_t ((const char *) data, name_length);
+      return hb_bytes_t ((const char *) data, name_length);
     }
 
     private:
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index dd10e34..5dc5784 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -306,13 +306,16 @@
 hb_ensure_native_direction (hb_buffer_t *buffer)
 {
   hb_direction_t direction = buffer->props.direction;
+  hb_direction_t horiz_dir = hb_script_get_horizontal_direction (buffer->props.script);
 
   /* TODO vertical:
    * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType
    * Ogham fonts are supposed to be implemented BTT or not.  Need to research that
    * first. */
-  if ((HB_DIRECTION_IS_HORIZONTAL (direction) && direction != hb_script_get_horizontal_direction (buffer->props.script)) ||
-      (HB_DIRECTION_IS_VERTICAL   (direction) && direction != HB_DIRECTION_TTB))
+  if ((HB_DIRECTION_IS_HORIZONTAL (direction) &&
+       direction != horiz_dir && horiz_dir != HB_DIRECTION_INVALID) ||
+      (HB_DIRECTION_IS_VERTICAL   (direction) &&
+       direction != HB_DIRECTION_TTB))
   {
     /* Same loop as hb_form_clusters().
      * Since form_clusters() merged clusters already, we don't merge. */
diff --git a/src/hb-ot-var.cc b/src/hb-ot-var.cc
index 90ba0bd..f0612a6 100644
--- a/src/hb-ot-var.cc
+++ b/src/hb-ot-var.cc
@@ -39,14 +39,14 @@
 static inline const OT::fvar&
 _get_fvar (hb_face_t *face)
 {
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::fvar);
+  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 ());
 }
 static inline const OT::avar&
 _get_avar (hb_face_t *face)
 {
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::avar);
+  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 ());
 }
@@ -65,7 +65,7 @@
 hb_bool_t
 hb_ot_var_has_data (hb_face_t *face)
 {
-  return &_get_fvar (face) != &OT::Null(OT::fvar);
+  return &_get_fvar (face) != &Null(OT::fvar);
 }
 
 /**
diff --git a/src/hb-private.hh b/src/hb-private.hh
index d7c8fef..9e075a3 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -829,7 +829,7 @@
  * light-weight) to be enabled, then HB_DEBUG can be defined to disable
  * the costlier checks. */
 #ifdef NDEBUG
-#define HB_NDEBUG
+#define HB_NDEBUG 1
 #endif
 
 
@@ -1052,12 +1052,12 @@
 
 /* String type. */
 
-struct hb_string_t
+struct hb_bytes_t
 {
-  inline hb_string_t (void) : bytes (nullptr), len (0) {}
-  inline hb_string_t (const char *bytes_, unsigned int len_) : bytes (bytes_), len (len_) {}
+  inline hb_bytes_t (void) : bytes (nullptr), len (0) {}
+  inline hb_bytes_t (const char *bytes_, unsigned int len_) : bytes (bytes_), len (len_) {}
 
-  inline int cmp (const hb_string_t &a) const
+  inline int cmp (const hb_bytes_t &a) const
   {
     if (len != a.len)
       return (int) a.len - (int) len;
@@ -1066,8 +1066,8 @@
   }
   static inline int cmp (const void *pa, const void *pb)
   {
-    hb_string_t *a = (hb_string_t *) pa;
-    hb_string_t *b = (hb_string_t *) pb;
+    hb_bytes_t *a = (hb_bytes_t *) pa;
+    hb_bytes_t *b = (hb_bytes_t *) pb;
     return b->cmp (*a);
   }
 
@@ -1089,4 +1089,33 @@
 #endif
 
 
+/*
+ * Null objects
+ */
+
+/* Global nul-content Null pool.  Enlarge as necessary. */
+
+#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
+#else
+extern HB_INTERNAL
+#endif
+const 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 const Type& Null (void) {
+  static_assert (sizeof (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
+  return *reinterpret_cast<const Type *> (_hb_NullPool);
+}
+#define Null(Type) Null<Type>()
+
+
 #endif /* HB_PRIVATE_HH */
diff --git a/src/hb-string-array.hh b/src/hb-string-array.hh
index d83553d..679841c 100644
--- a/src/hb-string-array.hh
+++ b/src/hb-string-array.hh
@@ -66,12 +66,12 @@
   sizeof (HB_STRING_ARRAY_TYPE_NAME)
 };
 
-static inline hb_string_t
+static inline hb_bytes_t
 HB_STRING_ARRAY_NAME (unsigned int i)
 {
   assert (i < ARRAY_LENGTH (HB_STRING_ARRAY_OFFS_NAME) - 1);
-  return hb_string_t (HB_STRING_ARRAY_POOL_NAME.str + HB_STRING_ARRAY_OFFS_NAME[i],
-		      HB_STRING_ARRAY_OFFS_NAME[i + 1] - HB_STRING_ARRAY_OFFS_NAME[i] - 1);
+  return hb_bytes_t (HB_STRING_ARRAY_POOL_NAME.str + HB_STRING_ARRAY_OFFS_NAME[i],
+		     HB_STRING_ARRAY_OFFS_NAME[i + 1] - HB_STRING_ARRAY_OFFS_NAME[i] - 1);
 }
 
 #undef HB_STRING_ARRAY_TYPE_NAME
diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index 35fe0ef..d6295a4 100644
--- a/src/hb-subset.cc
+++ b/src/hb-subset.cc
@@ -45,7 +45,7 @@
 
 
 #if !defined(HB_NO_VISIBILITY) && !defined(HB_SUBSET_BUILTIN)
-const void * const OT::_hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {};
+const void * const _hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {};
 #endif
 
 
@@ -87,11 +87,11 @@
   OT::Sanitizer<TableType> sanitizer;
 
   hb_blob_t *source_blob = sanitizer.sanitize (plan->source->reference_table (TableType::tableTag));
-  const TableType *table = OT::Sanitizer<TableType>::lock_instance (source_blob);
+  const TableType *table = source_blob->as<TableType> ();
 
   hb_tag_t tag = TableType::tableTag;
   hb_bool_t result = false;
-  if (table != &OT::Null(TableType))
+  if (table != &Null(TableType))
   {
     result = table->subset(plan);
   } else {
diff --git a/src/main.cc b/src/main.cc
index 935fa39..9a18736 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -38,7 +38,7 @@
 using namespace OT;
 
 #ifndef HB_NO_VISIBILITY
-const void * const OT::_hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {};
+const void * const _hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {};
 #endif
 
 int
@@ -56,7 +56,7 @@
 
   Sanitizer<OpenTypeFontFile> sanitizer;
   hb_blob_t *font_blob = sanitizer.sanitize (blob);
-  const OpenTypeFontFile* sanitized = Sanitizer<OpenTypeFontFile>::lock_instance (font_blob);
+  const OpenTypeFontFile* sanitized = font_blob->as<OpenTypeFontFile> ();
   if (sanitized == &Null (OpenTypeFontFile))
   {
     printf ("Sanitization of the file wasn't successful. Exit");
diff --git a/test/api/test-common.c b/test/api/test-common.c
index 74b50be..f6f0d48 100644
--- a/test/api/test-common.c
+++ b/test/api/test-common.c
@@ -173,6 +173,7 @@
 
   g_assert_cmpint (hb_script_get_horizontal_direction (HB_SCRIPT_LATIN), ==, HB_DIRECTION_LTR);
   g_assert_cmpint (hb_script_get_horizontal_direction (HB_SCRIPT_ARABIC), ==, HB_DIRECTION_RTL);
+  g_assert_cmpint (hb_script_get_horizontal_direction (HB_SCRIPT_OLD_ITALIC), ==, HB_DIRECTION_INVALID);
   g_assert_cmpint (hb_script_get_horizontal_direction (hb_script_from_iso15924_tag (wWyZ)), ==, HB_DIRECTION_LTR);
 }
 
diff --git a/test/shaping/data/in-house/Makefile.sources b/test/shaping/data/in-house/Makefile.sources
index 9a1434e..bf4df20 100644
--- a/test/shaping/data/in-house/Makefile.sources
+++ b/test/shaping/data/in-house/Makefile.sources
@@ -31,6 +31,7 @@
 	tests/mark-filtering-sets.tests \
 	tests/mongolian-variation-selector.tests \
 	tests/myanmar-syllable.tests \
+	tests/none-directional.tests \
 	tests/spaces.tests \
 	tests/simple.tests \
 	tests/tibetan-contractions-1.tests \
diff --git a/test/shaping/data/in-house/fonts/73e84dac2fc6a2d1bc9250d1414353661088937d.ttf b/test/shaping/data/in-house/fonts/73e84dac2fc6a2d1bc9250d1414353661088937d.ttf
new file mode 100644
index 0000000..46e7c1d
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/73e84dac2fc6a2d1bc9250d1414353661088937d.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/tests/none-directional.tests b/test/shaping/data/in-house/tests/none-directional.tests
new file mode 100644
index 0000000..e59946d
--- /dev/null
+++ b/test/shaping/data/in-house/tests/none-directional.tests
@@ -0,0 +1,3 @@
+../fonts/73e84dac2fc6a2d1bc9250d1414353661088937d.ttf::U+10300,U+10301:[u10300=0+1470|u10301=1+1284]
+../fonts/73e84dac2fc6a2d1bc9250d1414353661088937d.ttf:--direction=ltr:U+10300,U+10301:[u10300=0+1470|u10301=1+1284]
+../fonts/73e84dac2fc6a2d1bc9250d1414353661088937d.ttf:--direction=rtl:U+10300,U+10301:[u10301_r=1+1284|u10300_r=0+1470]