Bug 1473064 - Update HarfBuzz to version 1.8.3. r=jfkthame
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 11 Jul 2018 10:47:57 -0400
changeset 426549 294fc3bbabc3a3bd7d717a46b93b36538e90abae
parent 426548 9d4c8ab1fa61392f2c7449ed0cc5cafa091bd1cc
child 426550 7f49209f821f52bf5df8ae0b9eee7d4145034576
push id34275
push usernerli@mozilla.com
push dateFri, 13 Jul 2018 21:53:18 +0000
treeherdermozilla-central@b79457b703d9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs1473064
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1473064 - Update HarfBuzz to version 1.8.3. r=jfkthame
gfx/harfbuzz/NEWS
gfx/harfbuzz/README-mozilla
gfx/harfbuzz/configure.ac
gfx/harfbuzz/src/Makefile.am
gfx/harfbuzz/src/gen-def.py
gfx/harfbuzz/src/gen-use-table.py
gfx/harfbuzz/src/hb-aat-layout-trak-table.hh
gfx/harfbuzz/src/hb-blob-private.hh
gfx/harfbuzz/src/hb-blob.cc
gfx/harfbuzz/src/hb-buffer-private.hh
gfx/harfbuzz/src/hb-buffer.cc
gfx/harfbuzz/src/hb-common.cc
gfx/harfbuzz/src/hb-coretext.cc
gfx/harfbuzz/src/hb-debug.hh
gfx/harfbuzz/src/hb-directwrite.cc
gfx/harfbuzz/src/hb-dsalgs.hh
gfx/harfbuzz/src/hb-face-private.hh
gfx/harfbuzz/src/hb-font-private.hh
gfx/harfbuzz/src/hb-ft.cc
gfx/harfbuzz/src/hb-map-private.hh
gfx/harfbuzz/src/hb-object-private.hh
gfx/harfbuzz/src/hb-open-file-private.hh
gfx/harfbuzz/src/hb-open-type-private.hh
gfx/harfbuzz/src/hb-ot-cmap-table.hh
gfx/harfbuzz/src/hb-ot-hdmx-table.hh
gfx/harfbuzz/src/hb-ot-layout-common-private.hh
gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
gfx/harfbuzz/src/hb-ot-map.cc
gfx/harfbuzz/src/hb-ot-os2-unicode-ranges.hh
gfx/harfbuzz/src/hb-ot-post-table.hh
gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
gfx/harfbuzz/src/hb-ot-shape-complex-khmer.cc
gfx/harfbuzz/src/hb-ot-shape-complex-use-table.cc
gfx/harfbuzz/src/hb-ot-shape.cc
gfx/harfbuzz/src/hb-private.hh
gfx/harfbuzz/src/hb-set-private.hh
gfx/harfbuzz/src/hb-shape-plan-private.hh
gfx/harfbuzz/src/hb-shape-plan.cc
gfx/harfbuzz/src/hb-subset-input.cc
gfx/harfbuzz/src/hb-subset-plan.hh
gfx/harfbuzz/src/hb-subset.cc
gfx/harfbuzz/src/hb-unicode-private.hh
gfx/harfbuzz/src/hb-uniscribe.cc
gfx/harfbuzz/src/hb-version.h
gfx/harfbuzz/src/hb-warning.cc
gfx/harfbuzz/src/main.cc
gfx/harfbuzz/update.sh
--- a/gfx/harfbuzz/NEWS
+++ b/gfx/harfbuzz/NEWS
@@ -1,15 +1,31 @@
+Overview of changes leading to 1.8.3
+Wednesday, July 11, 2018
+====================================
+- A couple of Indic / USE bug fixes.
+- Disable vectorization, as it was causing unaligned access bus error on
+  certain 32bit architectures.
+
+
+Overview of changes leading to 1.8.2
+Tuesday, July 3, 2018
+====================================
+- Fix infinite loop in Khmer shaper.
+- Improve hb_blob_create_from_file() for streams.
+
+
 Overview of changes leading to 1.8.1
 Tuesday, June 12, 2018
 ====================================
 - Fix hb-version.h file generation; last two releases went out with wrong ones.
 - Add correctness bug in hb_set_t operations, introduced in 1.7.7.
 - Remove HB_SUBSET_BUILTIN build option.  Not necessary.
 
+
 Overview of changes leading to 1.8.0
 Tuesday, June 5, 2018
 ====================================
 - Update to Unicode 11.0.0.
 
 
 Overview of changes leading to 1.7.7
 Tuesday, June 5, 2018
--- a/gfx/harfbuzz/README-mozilla
+++ b/gfx/harfbuzz/README-mozilla
@@ -1,15 +1,17 @@
-gfx/harfbuzz status as of 2018-06-12:
+This directory contains the HarfBuzz source from the upstream repo:
+https://github.com/harfbuzz/harfbuzz
 
-This directory contains the HarfBuzz source from the 'master' branch of
-https://github.com/behdad/harfbuzz.
-
-Current version: 1.8.1
+Current version: 1.8.3 [commit 2b76767bf572364d3d647cdd139f2044a7ad06b2]
 
 UPDATING:
 
 Our in-tree copy of HarfBuzz does not depend on any generated files from the
 upstream build system. Therefore, it should be sufficient to simply overwrite
 the in-tree files one the updated ones from upstream to perform updates.
 
+To simplify this, the in-tree copy can be updated by running
+  sh update.sh
+from within the gfx/harfbuzz directory.
+
 If the collection of source files changes, manual updates to moz.build may be
 needed as we don't use the upstream makefiles.
--- a/gfx/harfbuzz/configure.ac
+++ b/gfx/harfbuzz/configure.ac
@@ -1,11 +1,11 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [1.8.1],
+        [1.8.3],
         [https://github.com/harfbuzz/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
 
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_SRCDIR([src/harfbuzz.pc.in])
 AC_CONFIG_HEADERS([config.h])
 
@@ -73,17 +73,17 @@ GTK_DOC_CHECK([1.15],[--flavour no-tmpl]
 	if test "x$enable_gtk_doc" = xyes; then
 		have_gtk_doc=true
 	fi
 ], [
 	AM_CONDITIONAL([ENABLE_GTK_DOC], false)
 ])
 
 # Functions and headers
-AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l)
+AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l posix_memalign)
 
 save_libs="$LIBS"
 LIBS="$LIBS -lm"
 AC_CHECK_FUNCS([round], ,[AC_CHECK_DECLS([round], , ,[#include <math.h>])])
 LIBS="$save_libs"
 
 AC_CHECK_HEADERS(unistd.h sys/mman.h xlocale.h stdbool.h)
 
--- a/gfx/harfbuzz/src/Makefile.am
+++ b/gfx/harfbuzz/src/Makefile.am
@@ -272,23 +272,23 @@ CLEANFILES += $(pkgconfig_DATA)
 
 DEF_FILES = harfbuzz.def harfbuzz-subset.def harfbuzz-icu.def
 if HAVE_GOBJECT
 DEF_FILES += harfbuzz-gobject.def
 endif
 check: $(DEF_FILES) # For check-symbols.sh
 CLEANFILES += $(DEF_FILES)
 harfbuzz.def: $(HBHEADERS) $(HBNODISTHEADERS)
-	$(AM_V_GEN) headers="$^" $(srcdir)/gen-def.py "$@"
+	$(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^
 harfbuzz-subset.def: $(HB_SUBSET_headers)
-	$(AM_V_GEN) headers="$^" $(srcdir)/gen-def.py "$@"
+	$(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^
 harfbuzz-icu.def: $(HB_ICU_headers)
-	$(AM_V_GEN) headers="$^" $(srcdir)/gen-def.py "$@"
+	$(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^
 harfbuzz-gobject.def: $(HB_GOBJECT_headers)
-	$(AM_V_GEN) headers="$^" $(srcdir)/gen-def.py "$@"
+	$(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^
 
 
 GENERATORS = \
 	gen-arabic-table.py \
 	gen-indic-table.py \
 	gen-use-table.py \
 	gen-def.py \
 	$(NULL)
--- a/gfx/harfbuzz/src/gen-def.py
+++ b/gfx/harfbuzz/src/gen-def.py
@@ -1,19 +1,25 @@
 #!/usr/bin/env python
 
 from __future__ import print_function, division, absolute_import
 
 import io, os, re, sys
 
+if len (sys.argv) < 3:
+	sys.exit("usage: gen-def.py harfbuzz.def hb.h [hb-blob.h hb-buffer.h ...]")
+
+output_file = sys.argv[1]
+header_paths = sys.argv[2:]
+
 headers_content = []
-for h in os.environ["headers"].split (' '):
+for h in header_paths:
 	if h.endswith (".h"):
 		with io.open (h, encoding='utf-8') as f: headers_content.append (f.read ())
 
 result = """EXPORTS
 %s
 LIBRARY lib%s-0.dll""" % (
 	"\n".join (sorted (re.findall (r"^hb_\w+(?= \()", "\n".join (headers_content), re.M))),
-	sys.argv[1].replace ('.def', '')
+	output_file.replace ('.def', '')
 )
 
-with open (sys.argv[1], "w") as f: f.write (result)
+with open (output_file, "w") as f: f.write (result)
--- a/gfx/harfbuzz/src/gen-use-table.py
+++ b/gfx/harfbuzz/src/gen-use-table.py
@@ -42,16 +42,19 @@ for i, f in enumerate (files):
 		values[i][t] = values[i].get (t, 0) + end - start + 1
 
 defaults = ('Other', 'Not_Applicable', 'Cn', 'No_Block')
 
 # TODO Characters that are not in Unicode Indic files, but used in USE
 data[0][0x034F] = defaults[0]
 data[0][0x2060] = defaults[0]
 data[0][0x20F0] = defaults[0]
+# TODO https://github.com/roozbehp/unicode-data/issues/9
+data[0][0x11C44] = 'Consonant_Placeholder'
+data[0][0x11C45] = 'Consonant_Placeholder'
 for u in range (0xFE00, 0xFE0F + 1):
 	data[0][u] = defaults[0]
 
 # Merge data into one dict:
 for i,v in enumerate (defaults):
 	values[i][v] = values[i].get (v, 0) + 1
 combined = {}
 for i,d in enumerate (data):
@@ -160,17 +163,17 @@ def is_BASE(U, UISC, UGC):
 			Tone_Letter,
 			Vowel_Independent #SPEC-DRAFT
 			] or
 		(UGC == Lo and UISC in [Avagraha, Bindu, Consonant_Final, Consonant_Medial,
 					Consonant_Subjoined, Vowel, Vowel_Dependent]))
 def is_BASE_IND(U, UISC, UGC):
 	#SPEC-DRAFT return (UISC in [Consonant_Dead, Modifying_Letter] or UGC == Po)
 	return (UISC in [Consonant_Dead, Modifying_Letter] or
-		(UGC == Po and not U in [0x104B, 0x104E, 0x2022, 0x11A3F, 0x11A45]) or
+		(UGC == Po and not U in [0x104B, 0x104E, 0x2022, 0x11A3F, 0x11A45, 0x11C44, 0x11C45]) or
 		False # SPEC-DRAFT-OUTDATED! U == 0x002D
 		)
 def is_BASE_NUM(U, UISC, UGC):
 	return UISC == Brahmi_Joining_Number
 def is_BASE_OTHER(U, UISC, UGC):
 	if UISC == Consonant_Placeholder: return True #SPEC-DRAFT
 	#SPEC-DRAFT return U in [0x00A0, 0x00D7, 0x2015, 0x2022, 0x25CC, 0x25FB, 0x25FC, 0x25FD, 0x25FE]
 	return U in [0x2015, 0x2022, 0x25FB, 0x25FC, 0x25FD, 0x25FE]
@@ -339,16 +342,19 @@ def map_to_use(data):
 
 		# TODO: In USE's override list but not in Unicode 11.0
 		if U == 0x103C: UIPC = Left
 
 		# TODO: These are not in USE's override list that we have, nor are they in Unicode 11.0
 		if 0xA926 <= U <= 0xA92A: UIPC = Top
 		if U == 0x111CA: UIPC = Bottom
 		if U == 0x11300: UIPC = Top
+		# TODO: https://github.com/harfbuzz/harfbuzz/pull/1037
+		if U == 0x11302: UIPC = Top
+		if U == 0x1133C: UIPC = Bottom
 		if U == 0x1171E: UIPC = Left # Correct?!
 		if 0x1CF2 <= U <= 0x1CF3: UIPC = Right
 		if 0x1CF8 <= U <= 0x1CF9: UIPC = Top
 		# https://github.com/roozbehp/unicode-data/issues/8
 		if U == 0x0A51: UIPC = Bottom
 
 		assert (UIPC in [Not_Applicable, Visual_Order_Left] or
 			USE in use_positions), "%s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UGC)
--- a/gfx/harfbuzz/src/hb-aat-layout-trak-table.hh
+++ b/gfx/harfbuzz/src/hb-aat-layout-trak-table.hh
@@ -167,29 +167,29 @@ struct trak
     hb_buffer_t *buffer = c->buffer;
     if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
     {
       const TrackData &trackData = this+horizData;
       float tracking = trackData.get_tracking (this, ptem);
       hb_position_t advance_to_add = c->font->em_scalef_x (tracking / 2);
       foreach_grapheme (buffer, start, end)
       {
-	/* TODO This is wrong. */
+	buffer->pos[start].x_offset += advance_to_add;
 	buffer->pos[start].x_advance += advance_to_add;
 	buffer->pos[end].x_advance += advance_to_add;
       }
     }
     else
     {
       const TrackData &trackData = this+vertData;
       float tracking = trackData.get_tracking (this, ptem);
       hb_position_t advance_to_add = c->font->em_scalef_y (tracking / 2);
       foreach_grapheme (buffer, start, end)
       {
-	/* TODO This is wrong. */
+	buffer->pos[start].y_offset += advance_to_add;
 	buffer->pos[start].y_advance += advance_to_add;
 	buffer->pos[end].y_advance += advance_to_add;
       }
     }
 
     return_trace (true);
   }
 
--- a/gfx/harfbuzz/src/hb-blob-private.hh
+++ b/gfx/harfbuzz/src/hb-blob-private.hh
@@ -26,18 +26,16 @@
  * 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)
--- a/gfx/harfbuzz/src/hb-blob.cc
+++ b/gfx/harfbuzz/src/hb-blob.cc
@@ -26,17 +26,16 @@
  */
 
 /* http://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html */
 #ifndef _POSIX_C_SOURCE
 #define _POSIX_C_SOURCE 200809L
 #endif
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
 #include "hb-blob-private.hh"
 
 #ifdef HAVE_SYS_MMAN_H
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif /* HAVE_UNISTD_H */
 #include <sys/mman.h>
 #endif /* HAVE_SYS_MMAN_H */
@@ -484,127 +483,161 @@ hb_blob_t::try_make_writable (void)
 #ifdef HAVE_MMAP
 # include <sys/types.h>
 # include <sys/stat.h>
 # include <fcntl.h>
 #endif
 
 #if defined(_WIN32) || defined(__CYGWIN__)
 # include <windows.h>
-#endif
-
-#ifndef _O_BINARY
-# define _O_BINARY 0
+#else
+# ifndef _O_BINARY
+#  define _O_BINARY 0
+# endif
 #endif
 
 #ifndef MAP_NORESERVE
 # define MAP_NORESERVE 0
 #endif
 
 struct hb_mapped_file_t
 {
   char *contents;
   unsigned long length;
 #if defined(_WIN32) || defined(__CYGWIN__)
   HANDLE mapping;
 #endif
 };
 
+#if (defined(HAVE_MMAP) || defined(_WIN32) || defined(__CYGWIN__)) && !defined(HB_NO_MMAP)
 static void
 _hb_mapped_file_destroy (hb_mapped_file_t *file)
 {
 #ifdef HAVE_MMAP
   munmap (file->contents, file->length);
 #elif defined(_WIN32) || defined(__CYGWIN__)
   UnmapViewOfFile (file->contents);
   CloseHandle (file->mapping);
 #else
-  free (file->contents);
+  assert (0); // If we don't have mmap we shouldn't reach here
 #endif
 
   free (file);
 }
+#endif
 
 /**
  * hb_blob_create_from_file:
  * @file_name: font filename.
  *
  * Returns: A hb_blob_t pointer with the content of the file
  *
  * Since: 1.7.7
  **/
 hb_blob_t *
 hb_blob_create_from_file (const char *file_name)
 {
-  // Adopted from glib's gmappedfile.c with Matthias Clasen and
-  // Allison Lortie permission but changed a lot to suit our need.
-  bool writable = false;
-  hb_memory_mode_t mm = HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE;
+  /* Adopted from glib's gmappedfile.c with Matthias Clasen and
+     Allison Lortie permission but changed a lot to suit our need. */
+#if defined(HAVE_MMAP) && !defined(HB_NO_MMAP)
   hb_mapped_file_t *file = (hb_mapped_file_t *) calloc (1, sizeof (hb_mapped_file_t));
   if (unlikely (!file)) return hb_blob_get_empty ();
 
-#ifdef HAVE_MMAP
-  int fd = open (file_name, (writable ? O_RDWR : O_RDONLY) | _O_BINARY, 0);
-# define CLOSE close
+  int fd = open (file_name, O_RDONLY | _O_BINARY, 0);
   if (unlikely (fd == -1)) goto fail_without_close;
 
   struct stat st;
   if (unlikely (fstat (fd, &st) == -1)) goto fail;
 
-  // See https://github.com/GNOME/glib/blob/f9faac7/glib/gmappedfile.c#L139-L142
-  if (unlikely (st.st_size == 0 && S_ISREG (st.st_mode))) goto fail;
-
   file->length = (unsigned long) st.st_size;
-  file->contents = (char *) mmap (nullptr, file->length,
-				  writable ? PROT_READ|PROT_WRITE : PROT_READ,
+  file->contents = (char *) mmap (nullptr, file->length, PROT_READ,
 				  MAP_PRIVATE | MAP_NORESERVE, fd, 0);
 
   if (unlikely (file->contents == MAP_FAILED)) goto fail;
 
-#elif defined(_WIN32) || defined(__CYGWIN__)
-  HANDLE fd = CreateFile (file_name,
-			  writable ? GENERIC_READ|GENERIC_WRITE : GENERIC_READ,
-			  FILE_SHARE_READ, nullptr, OPEN_EXISTING,
-			  FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, nullptr);
-# define CLOSE CloseHandle
+  close (fd);
+
+  return hb_blob_create (file->contents, file->length,
+			 HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file,
+			 (hb_destroy_func_t) _hb_mapped_file_destroy);
+
+fail:
+  close (fd);
+fail_without_close:
+  free (file);
+
+#elif (defined(_WIN32) || defined(__CYGWIN__)) && !defined(HB_NO_MMAP)
+  hb_mapped_file_t *file = (hb_mapped_file_t *) calloc (1, sizeof (hb_mapped_file_t));
+  if (unlikely (!file)) return hb_blob_get_empty ();
+
+  HANDLE fd;
+  unsigned int size = strlen (file_name) + 1;
+  wchar_t * wchar_file_name = (wchar_t *) malloc (sizeof (wchar_t) * size);
+  if (unlikely (wchar_file_name == nullptr)) goto fail_without_close;
+  mbstowcs (wchar_file_name, file_name, size);
+  fd = CreateFileW (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, nullptr,
+		    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
+		    nullptr);
+  free (wchar_file_name);
 
   if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close;
 
   file->length = (unsigned long) GetFileSize (fd, nullptr);
-  file->mapping = CreateFileMapping (fd, nullptr,
-				     writable ? PAGE_WRITECOPY : PAGE_READONLY,
-				     0, 0, nullptr);
+  file->mapping = CreateFileMapping (fd, nullptr, PAGE_READONLY, 0, 0, nullptr);
   if (unlikely (file->mapping == nullptr)) goto fail;
 
-  file->contents = (char *) MapViewOfFile (file->mapping,
-					   writable ? FILE_MAP_COPY : FILE_MAP_READ,
-					   0, 0, 0);
+  file->contents = (char *) MapViewOfFile (file->mapping, FILE_MAP_READ, 0, 0, 0);
   if (unlikely (file->contents == nullptr)) goto fail;
 
-#else
-  mm = HB_MEMORY_MODE_WRITABLE;
-
-  FILE *fd = fopen (file_name, "rb");
-# define CLOSE fclose
-  if (unlikely (!fd)) goto fail_without_close;
+  CloseHandle (fd);
+  return hb_blob_create (file->contents, file->length,
+			 HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file,
+			 (hb_destroy_func_t) _hb_mapped_file_destroy);
 
-  fseek (fd, 0, SEEK_END);
-  file->length = ftell (fd);
-  rewind (fd);
-  file->contents = (char *) malloc (file->length);
-  if (unlikely (!file->contents)) goto fail;
-
-  if (unlikely (fread (file->contents, 1, file->length, fd) != file->length))
-    goto fail;
+fail:
+  CloseHandle (fd);
+fail_without_close:
+  free (file);
 
 #endif
 
-  CLOSE (fd);
-  return hb_blob_create (file->contents, file->length, mm, (void *) file,
-			 (hb_destroy_func_t) _hb_mapped_file_destroy);
+  /* The following tries to read a file without knowing its size beforehand
+     It's used as a fallback for systems without mmap or to read from pipes */
+  unsigned long len = 0, allocated = BUFSIZ * 16;
+  char *data = (char *) malloc (allocated);
+  if (unlikely (data == nullptr)) return hb_blob_get_empty ();
+
+  FILE *fp = fopen (file_name, "rb");
+  if (unlikely (fp == nullptr)) goto fread_fail_without_close;
 
-fail:
-  CLOSE (fd);
-#undef CLOSE
-fail_without_close:
-  free (file);
+  while (!feof (fp))
+  {
+    if (allocated - len < BUFSIZ)
+    {
+      allocated *= 2;
+      /* Don't allocate and go more than ~536MB, our mmap reader still
+	 can cover files like that but lets limit our fallback reader */
+      if (unlikely (allocated > (2 << 28))) goto fread_fail;
+      char *new_data = (char *) realloc (data, allocated);
+      if (unlikely (new_data == nullptr)) goto fread_fail;
+      data = new_data;
+    }
+
+    unsigned long addition = fread (data + len, 1, allocated - len, fp);
+
+    int err = ferror (fp);
+#ifdef EINTR // armcc doesn't have it
+    if (unlikely (err == EINTR)) continue;
+#endif
+    if (unlikely (err)) goto fread_fail;
+
+    len += addition;
+  }
+
+  return hb_blob_create (data, len, HB_MEMORY_MODE_WRITABLE, data,
+                         (hb_destroy_func_t) free);
+
+fread_fail:
+  fclose (fp);
+fread_fail_without_close:
+  free (data);
   return hb_blob_get_empty ();
 }
--- a/gfx/harfbuzz/src/hb-buffer-private.hh
+++ b/gfx/harfbuzz/src/hb-buffer-private.hh
@@ -26,17 +26,16 @@
  * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_BUFFER_PRIVATE_HH
 #define HB_BUFFER_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-object-private.hh"
 #include "hb-unicode-private.hh"
 
 
 #ifndef HB_BUFFER_MAX_LEN_FACTOR
 #define HB_BUFFER_MAX_LEN_FACTOR 32
 #endif
 #ifndef HB_BUFFER_MAX_LEN_MIN
 #define HB_BUFFER_MAX_LEN_MIN 8192
--- a/gfx/harfbuzz/src/hb-buffer.cc
+++ b/gfx/harfbuzz/src/hb-buffer.cc
@@ -119,24 +119,24 @@ hb_buffer_t::enlarge (unsigned int size)
     return false;
   }
 
   unsigned int new_allocated = allocated;
   hb_glyph_position_t *new_pos = nullptr;
   hb_glyph_info_t *new_info = nullptr;
   bool separate_out = out_info != info;
 
-  if (unlikely (_hb_unsigned_int_mul_overflows (size, sizeof (info[0]))))
+  if (unlikely (hb_unsigned_mul_overflows (size, sizeof (info[0]))))
     goto done;
 
   while (size >= new_allocated)
     new_allocated += (new_allocated >> 1) + 32;
 
   static_assert ((sizeof (info[0]) == sizeof (pos[0])), "");
-  if (unlikely (_hb_unsigned_int_mul_overflows (new_allocated, sizeof (info[0]))))
+  if (unlikely (hb_unsigned_mul_overflows (new_allocated, sizeof (info[0]))))
     goto done;
 
   new_pos = (hb_glyph_position_t *) realloc (pos, new_allocated * sizeof (pos[0]));
   new_info = (hb_glyph_info_t *) realloc (info, new_allocated * sizeof (info[0]));
 
 done:
   if (unlikely (!new_pos || !new_info))
     successful = false;
--- a/gfx/harfbuzz/src/hb-common.cc
+++ b/gfx/harfbuzz/src/hb-common.cc
@@ -23,18 +23,16 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
 
-#include "hb-mutex-private.hh"
-#include "hb-object-private.hh"
 
 #include <locale.h>
 #ifdef HAVE_XLOCALE_H
 #include <xlocale.h>
 #endif
 
 
 /* hb_options_t */
@@ -540,16 +538,17 @@ hb_script_get_horizontal_direction (hb_s
     case HB_SCRIPT_OLD_SOGDIAN:
     case HB_SCRIPT_SOGDIAN:
 
       return HB_DIRECTION_RTL;
 
 
     /* https://github.com/harfbuzz/harfbuzz/issues/1000 */
     case HB_SCRIPT_OLD_ITALIC:
+    case HB_SCRIPT_RUNIC:
 
       return HB_DIRECTION_INVALID;
   }
 
   return HB_DIRECTION_LTR;
 }
 
 
--- a/gfx/harfbuzz/src/hb-coretext.cc
+++ b/gfx/harfbuzz/src/hb-coretext.cc
@@ -24,17 +24,16 @@
  *
  * Mozilla Author(s): Jonathan Kew
  * Google Author(s): Behdad Esfahbod
  */
 
 #define HB_SHAPER coretext
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
 #include "hb-shaper-impl-private.hh"
 
 #include "hb-coretext.h"
 #include <math.h>
 
 /* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */
 #define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f
 
--- a/gfx/harfbuzz/src/hb-debug.hh
+++ b/gfx/harfbuzz/src/hb-debug.hh
@@ -23,16 +23,17 @@
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_DEBUG_HH
 #define HB_DEBUG_HH
 
 #include "hb-private.hh"
+#include "hb-dsalgs.hh"
 
 
 #ifndef HB_DEBUG
 #define HB_DEBUG 0
 #endif
 
 static inline bool
 _hb_debug (unsigned int level,
--- a/gfx/harfbuzz/src/hb-directwrite.cc
+++ b/gfx/harfbuzz/src/hb-directwrite.cc
@@ -18,17 +18,16 @@
  * 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-private.hh"
-#include "hb-debug.hh"
 #define HB_SHAPER directwrite
 #include "hb-shaper-impl-private.hh"
 
 #include <DWrite_1.h>
 
 #include "hb-directwrite.h"
 
 
--- a/gfx/harfbuzz/src/hb-dsalgs.hh
+++ b/gfx/harfbuzz/src/hb-dsalgs.hh
@@ -25,16 +25,253 @@
  */
 
 #ifndef HB_DSALGS_HH
 #define HB_DSALGS_HH
 
 #include "hb-private.hh"
 
 
+/* Void! For when we need a expression-type of void. */
+typedef const struct _hb_void_t *hb_void_t;
+#define HB_VOID ((const _hb_void_t *) nullptr)
+
+
+/*
+ * Bithacks.
+ */
+
+/* Return the number of 1 bits in v. */
+template <typename T>
+static inline HB_CONST_FUNC unsigned int
+hb_popcount (T v)
+{
+#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) && defined(__OPTIMIZE__)
+  if (sizeof (T) <= sizeof (unsigned int))
+    return __builtin_popcount (v);
+
+  if (sizeof (T) <= sizeof (unsigned long))
+    return __builtin_popcountl (v);
+
+  if (sizeof (T) <= sizeof (unsigned long long))
+    return __builtin_popcountll (v);
+#endif
+
+  if (sizeof (T) <= 4)
+  {
+    /* "HACKMEM 169" */
+    uint32_t y;
+    y = (v >> 1) &033333333333;
+    y = v - y - ((y >>1) & 033333333333);
+    return (((y + (y >> 3)) & 030707070707) % 077);
+  }
+
+  if (sizeof (T) == 8)
+  {
+    unsigned int shift = 32;
+    return hb_popcount<uint32_t> ((uint32_t) v) + hb_popcount ((uint32_t) (v >> shift));
+  }
+
+  if (sizeof (T) == 16)
+  {
+    unsigned int shift = 64;
+    return hb_popcount<uint64_t> ((uint64_t) v) + hb_popcount ((uint64_t) (v >> shift));
+  }
+
+  assert (0);
+  return 0; /* Shut up stupid compiler. */
+}
+
+/* Returns the number of bits needed to store number */
+template <typename T>
+static inline HB_CONST_FUNC unsigned int
+hb_bit_storage (T v)
+{
+  if (unlikely (!v)) return 0;
+
+#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__)
+  if (sizeof (T) <= sizeof (unsigned int))
+    return sizeof (unsigned int) * 8 - __builtin_clz (v);
+
+  if (sizeof (T) <= sizeof (unsigned long))
+    return sizeof (unsigned long) * 8 - __builtin_clzl (v);
+
+  if (sizeof (T) <= sizeof (unsigned long long))
+    return sizeof (unsigned long long) * 8 - __builtin_clzll (v);
+#endif
+
+#if (defined(_MSC_VER) && _MSC_VER >= 1500) || defined(__MINGW32__)
+  if (sizeof (T) <= sizeof (unsigned int))
+  {
+    unsigned long where;
+    _BitScanReverse (&where, v);
+    return 1 + where;
+  }
+# if _WIN64
+  if (sizeof (T) <= 8)
+  {
+    unsigned long where;
+    _BitScanReverse64 (&where, v);
+    return 1 + where;
+  }
+# endif
+#endif
+
+  if (sizeof (T) <= 4)
+  {
+    /* "bithacks" */
+    const unsigned int b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000};
+    const unsigned int S[] = {1, 2, 4, 8, 16};
+    unsigned int r = 0;
+    for (int i = 4; i >= 0; i--)
+      if (v & b[i])
+      {
+	v >>= S[i];
+	r |= S[i];
+      }
+    return r + 1;
+  }
+  if (sizeof (T) <= 8)
+  {
+    /* "bithacks" */
+    const uint64_t b[] = {0x2ULL, 0xCULL, 0xF0ULL, 0xFF00ULL, 0xFFFF0000ULL, 0xFFFFFFFF00000000ULL};
+    const unsigned int S[] = {1, 2, 4, 8, 16, 32};
+    unsigned int r = 0;
+    for (int i = 5; i >= 0; i--)
+      if (v & b[i])
+      {
+	v >>= S[i];
+	r |= S[i];
+      }
+    return r + 1;
+  }
+  if (sizeof (T) == 16)
+  {
+    unsigned int shift = 64;
+    return (v >> shift) ? hb_bit_storage<uint64_t> ((uint64_t) (v >> shift)) + shift :
+			  hb_bit_storage<uint64_t> ((uint64_t) v);
+  }
+
+  assert (0);
+  return 0; /* Shut up stupid compiler. */
+}
+
+/* Returns the number of zero bits in the least significant side of v */
+template <typename T>
+static inline HB_CONST_FUNC unsigned int
+hb_ctz (T v)
+{
+  if (unlikely (!v)) return 0;
+
+#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__)
+  if (sizeof (T) <= sizeof (unsigned int))
+    return __builtin_ctz (v);
+
+  if (sizeof (T) <= sizeof (unsigned long))
+    return __builtin_ctzl (v);
+
+  if (sizeof (T) <= sizeof (unsigned long long))
+    return __builtin_ctzll (v);
+#endif
+
+#if (defined(_MSC_VER) && _MSC_VER >= 1500) || defined(__MINGW32__)
+  if (sizeof (T) <= sizeof (unsigned int))
+  {
+    unsigned long where;
+    _BitScanForward (&where, v);
+    return where;
+  }
+# if _WIN64
+  if (sizeof (T) <= 8)
+  {
+    unsigned long where;
+    _BitScanForward64 (&where, v);
+    return where;
+  }
+# endif
+#endif
+
+  if (sizeof (T) <= 4)
+  {
+    /* "bithacks" */
+    unsigned int c = 32;
+    v &= - (int32_t) v;
+    if (v) c--;
+    if (v & 0x0000FFFF) c -= 16;
+    if (v & 0x00FF00FF) c -= 8;
+    if (v & 0x0F0F0F0F) c -= 4;
+    if (v & 0x33333333) c -= 2;
+    if (v & 0x55555555) c -= 1;
+    return c;
+  }
+  if (sizeof (T) <= 8)
+  {
+    /* "bithacks" */
+    unsigned int c = 64;
+    v &= - (int64_t) (v);
+    if (v) c--;
+    if (v & 0x00000000FFFFFFFFULL) c -= 32;
+    if (v & 0x0000FFFF0000FFFFULL) c -= 16;
+    if (v & 0x00FF00FF00FF00FFULL) c -= 8;
+    if (v & 0x0F0F0F0F0F0F0F0FULL) c -= 4;
+    if (v & 0x3333333333333333ULL) c -= 2;
+    if (v & 0x5555555555555555ULL) c -= 1;
+    return c;
+  }
+  if (sizeof (T) == 16)
+  {
+    unsigned int shift = 64;
+    return (uint64_t) v ? hb_bit_storage<uint64_t> ((uint64_t) v) :
+			  hb_bit_storage<uint64_t> ((uint64_t) (v >> shift)) + shift;
+  }
+
+  assert (0);
+  return 0; /* Shut up stupid compiler. */
+}
+
+
+/*
+ * Tiny stuff.
+ */
+
+#undef MIN
+template <typename Type>
+static inline Type MIN (const Type &a, const Type &b) { return a < b ? a : b; }
+
+#undef MAX
+template <typename Type>
+static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; }
+
+static inline unsigned int DIV_CEIL (const unsigned int a, unsigned int b)
+{ return (a + (b - 1)) / b; }
+
+
+#undef  ARRAY_LENGTH
+template <typename Type, unsigned int n>
+static inline unsigned int ARRAY_LENGTH (const Type (&)[n]) { return n; }
+/* A const version, but does not detect erratically being called on pointers. */
+#define ARRAY_LENGTH_CONST(__array) ((signed int) (sizeof (__array) / sizeof (__array[0])))
+
+static inline bool
+hb_unsigned_mul_overflows (unsigned int count, unsigned int size)
+{
+  return (size > 0) && (count >= ((unsigned int) -1) / size);
+}
+
+static inline unsigned int
+hb_ceil_to_4 (unsigned int v)
+{
+  return ((v - 1) | 3) + 1;
+}
+
+
+/*
+ * Sort and search.
+ */
+
 static inline void *
 hb_bsearch_r (const void *key, const void *base,
 	      size_t nmemb, size_t size,
 	      int (*compar)(const void *_key, const void *_item, void *_arg),
 	      void *arg)
 {
   int min = 0, max = (int) nmemb - 1;
   while (min <= max)
@@ -48,17 +285,16 @@ hb_bsearch_r (const void *key, const voi
       min = mid + 1;
     else
       return (void *) p;
   }
   return nullptr;
 }
 
 
-
 /* From https://github.com/noporpoise/sort_r */
 
 /* Isaac Turner 29 April 2014 Public Domain */
 
 /*
 
 hb_sort_r function to be exported.
 
@@ -153,9 +389,476 @@ static inline void sort_r_simple(void *b
 
 static inline void hb_sort_r(void *base, size_t nel, size_t width,
 			     int (*compar)(const void *_a, const void *_b, void *_arg),
 			     void *arg)
 {
     sort_r_simple(base, nel, width, compar, arg);
 }
 
+
+template <typename T, typename T2> static inline void
+hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *), T2 *array2)
+{
+  for (unsigned int i = 1; i < len; i++)
+  {
+    unsigned int j = i;
+    while (j && compar (&array[j - 1], &array[i]) > 0)
+      j--;
+    if (i == j)
+      continue;
+    /* Move item i to occupy place for item j, shift what's in between. */
+    {
+      T t = array[i];
+      memmove (&array[j + 1], &array[j], (i - j) * sizeof (T));
+      array[j] = t;
+    }
+    if (array2)
+    {
+      T2 t = array2[i];
+      memmove (&array2[j + 1], &array2[j], (i - j) * sizeof (T2));
+      array2[j] = t;
+    }
+  }
+}
+
+template <typename T> static inline void
+hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *))
+{
+  hb_stable_sort (array, len, compar, (int *) nullptr);
+}
+
+static inline hb_bool_t
+hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out)
+{
+  /* Pain because we don't know whether s is nul-terminated. */
+  char buf[64];
+  len = MIN (ARRAY_LENGTH (buf) - 1, len);
+  strncpy (buf, s, len);
+  buf[len] = '\0';
+
+  char *end;
+  errno = 0;
+  unsigned long v = strtoul (buf, &end, base);
+  if (errno) return false;
+  if (*end) return false;
+  *out = v;
+  return true;
+}
+
+
+#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
+{
+  hb_auto_t (void) { Type::init (); }
+  ~hb_auto_t (void) { Type::fini (); }
+  private: /* Hide */
+  void init (void) {}
+  void fini (void) {}
+};
+
+struct hb_bytes_t
+{
+  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_bytes_t &a) const
+  {
+    if (len != a.len)
+      return (int) a.len - (int) len;
+
+    return memcmp (a.bytes, bytes, len);
+  }
+  static inline int cmp (const void *pa, const void *pb)
+  {
+    hb_bytes_t *a = (hb_bytes_t *) pa;
+    hb_bytes_t *b = (hb_bytes_t *) pb;
+    return b->cmp (*a);
+  }
+
+  const char *bytes;
+  unsigned int len;
+};
+
+
+struct HbOpOr
+{
+  static const bool passthru_left = true;
+  static const bool passthru_right = true;
+  template <typename T> static void process (T &o, const T &a, const T &b) { o = a | b; }
+};
+struct HbOpAnd
+{
+  static const bool passthru_left = false;
+  static const bool passthru_right = false;
+  template <typename T> static void process (T &o, const T &a, const T &b) { o = a & b; }
+};
+struct HbOpMinus
+{
+  static const bool passthru_left = true;
+  static const bool passthru_right = false;
+  template <typename T> static void process (T &o, const T &a, const T &b) { o = a & ~b; }
+};
+struct HbOpXor
+{
+  static const bool passthru_left = true;
+  static const bool passthru_right = true;
+  template <typename T> static void process (T &o, const T &a, const T &b) { o = a ^ b; }
+};
+
+
+/* Compiler-assisted vectorization. */
+
+/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))),
+ * using vectorized operations if HB_VECTOR_SIZE is set to **bit** numbers (eg 128).
+ * Define that to 0 to disable. */
+template <typename elt_t, unsigned int byte_size>
+struct hb_vector_size_t
+{
+  elt_t& operator [] (unsigned int i) { return u.v[i]; }
+  const elt_t& operator [] (unsigned int i) const { return u.v[i]; }
+
+  template <class Op>
+  inline hb_vector_size_t process (const hb_vector_size_t &o) const
+  {
+    hb_vector_size_t r;
+#if HB_VECTOR_SIZE
+    if (HB_VECTOR_SIZE && 0 == (byte_size * 8) % HB_VECTOR_SIZE)
+      for (unsigned int i = 0; i < ARRAY_LENGTH (u.vec); i++)
+	Op::process (r.u.vec[i], u.vec[i], o.u.vec[i]);
+    else
+#endif
+      for (unsigned int i = 0; i < ARRAY_LENGTH (u.v); i++)
+	Op::process (r.u.v[i], u.v[i], o.u.v[i]);
+    return r;
+  }
+  inline hb_vector_size_t operator | (const hb_vector_size_t &o) const
+  { return process<HbOpOr> (o); }
+  inline hb_vector_size_t operator & (const hb_vector_size_t &o) const
+  { return process<HbOpAnd> (o); }
+  inline hb_vector_size_t operator ^ (const hb_vector_size_t &o) const
+  { return process<HbOpXor> (o); }
+  inline hb_vector_size_t operator ~ () const
+  {
+    hb_vector_size_t r;
+#if HB_VECTOR_SIZE && 0
+    if (HB_VECTOR_SIZE && 0 == (byte_size * 8) % HB_VECTOR_SIZE)
+      for (unsigned int i = 0; i < ARRAY_LENGTH (u.vec); i++)
+	r.u.vec[i] = ~u.vec[i];
+    else
+#endif
+    for (unsigned int i = 0; i < ARRAY_LENGTH (u.v); i++)
+      r.u.v[i] = ~u.v[i];
+    return r;
+  }
+
+  private:
+  static_assert (byte_size / sizeof (elt_t) * sizeof (elt_t) == byte_size, "");
+  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)];
+#endif
+  } u;
+};
+
+
 #endif /* HB_DSALGS_HH */
--- a/gfx/harfbuzz/src/hb-face-private.hh
+++ b/gfx/harfbuzz/src/hb-face-private.hh
@@ -26,17 +26,16 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_FACE_PRIVATE_HH
 #define HB_FACE_PRIVATE_HH
 
 #include "hb-private.hh"
 
-#include "hb-object-private.hh"
 #include "hb-shaper-private.hh"
 #include "hb-shape-plan-private.hh"
 
 
 /*
  * hb_face_t
  */
 
--- a/gfx/harfbuzz/src/hb-font-private.hh
+++ b/gfx/harfbuzz/src/hb-font-private.hh
@@ -26,17 +26,16 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_FONT_PRIVATE_HH
 #define HB_FONT_PRIVATE_HH
 
 #include "hb-private.hh"
 
-#include "hb-object-private.hh"
 #include "hb-face-private.hh"
 #include "hb-shaper-private.hh"
 
 
 
 /*
  * hb_font_funcs_t
  */
--- a/gfx/harfbuzz/src/hb-ft.cc
+++ b/gfx/harfbuzz/src/hb-ft.cc
@@ -23,17 +23,16 @@
  * 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
  */
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
 
 #include "hb-ft.h"
 
 #include "hb-font-private.hh"
 
 #include FT_ADVANCES_H
 #include FT_MULTIPLE_MASTERS_H
 #include FT_TRUETYPE_TABLES_H
--- a/gfx/harfbuzz/src/hb-map-private.hh
+++ b/gfx/harfbuzz/src/hb-map-private.hh
@@ -23,17 +23,16 @@
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_MAP_PRIVATE_HH
 #define HB_MAP_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-object-private.hh"
 
 
 template <typename T>
 inline uint32_t Hash (const T &v)
 {
   /* Knuth's multiplicative method: */
   return (uint32_t) v * 2654435761u;
 }
@@ -84,17 +83,17 @@ struct hb_map_t
     hb_object_fini (this);
     fini_shallow ();
   }
 
   inline bool resize (void)
   {
     if (unlikely (!successful)) return false;
 
-    unsigned int power = _hb_bit_storage (population * 2 + 8);
+    unsigned int power = hb_bit_storage (population * 2 + 8);
     unsigned int new_size = 1u << power;
     item_t *new_items = (item_t *) malloc ((size_t) new_size * sizeof (item_t));
     if (unlikely (!new_items))
     {
       successful = false;
       return false;
     }
     memset (new_items, 0xFF, (size_t) new_size * sizeof (item_t));
--- a/gfx/harfbuzz/src/hb-object-private.hh
+++ b/gfx/harfbuzz/src/hb-object-private.hh
@@ -28,18 +28,16 @@
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OBJECT_PRIVATE_HH
 #define HB_OBJECT_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
-
 #include "hb-atomic-private.hh"
 #include "hb-mutex-private.hh"
 
 
 /* reference_count */
 
 #define HB_REFERENCE_COUNT_INERT_VALUE 0
 #define HB_REFERENCE_COUNT_POISON_VALUE -0x0000DEAD
--- a/gfx/harfbuzz/src/hb-open-file-private.hh
+++ b/gfx/harfbuzz/src/hb-open-file-private.hh
@@ -281,57 +281,251 @@ struct TTCHeader
   Tag		ttcTag;		/* TrueType Collection ID string: 'ttcf' */
   FixedVersion<>version;	/* Version of the TTC Header (1.0 or 2.0),
 				 * 0x00010000u or 0x00020000u */
   }			header;
   TTCHeaderVersion1	version1;
   } u;
 };
 
+/*
+ * Mac Resource Fork
+ */
+
+struct ResourceRefItem
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    // actual data sanitization is done on ResourceForkHeader sanitizer
+    return_trace (likely (c->check_struct (this)));
+  }
+
+  HBINT16	id;		/* Resource ID, is really should be signed? */
+  HBINT16	nameOffset;	/* Offset from beginning of resource name list
+				 * to resource name, minus means there is no */
+  HBUINT8	attr;		/* Resource attributes */
+  HBUINT24	dataOffset;	/* Offset from beginning of resource data to
+				 * data for this resource */
+  HBUINT32	reserved;	/* Reserved for handle to resource */
+  public:
+  DEFINE_SIZE_STATIC (12);
+};
+
+struct ResourceTypeItem
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    // RefList sanitization is done on ResourceMap sanitizer
+    return_trace (likely (c->check_struct (this)));
+  }
+
+  inline unsigned int get_resource_count () const
+  {
+    return numRes + 1;
+  }
+
+  inline bool is_sfnt () const
+  {
+    return type == HB_TAG ('s','f','n','t');
+  }
+
+  inline const ResourceRefItem& get_ref_item (const void *base,
+					      unsigned int i) const
+  {
+    return (base+refList)[i];
+  }
+
+  protected:
+  Tag		type;		/* Resource type */
+  HBUINT16	numRes;		/* Number of resource this type in map minus 1 */
+  OffsetTo<UnsizedArrayOf<ResourceRefItem> >
+		refList;	/* Offset from beginning of resource type list
+				 * to reference list for this type */
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+struct ResourceMap
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!c->check_struct (this)))
+      return_trace (false);
+    for (unsigned int i = 0; i < get_types_count (); ++i)
+    {
+      const ResourceTypeItem& type = get_type (i);
+      if (unlikely (!type.sanitize (c)))
+        return_trace (false);
+      for (unsigned int j = 0; j < type.get_resource_count (); ++j)
+	if (unlikely (!get_ref_item (type, j).sanitize (c)))
+	  return_trace (false);
+    }
+    return_trace (true);
+  }
+
+  inline const ResourceTypeItem& get_type (unsigned int i) const
+  {
+    // Why offset from the second byte of the object? I'm not sure
+    return ((&reserved[2])+typeList)[i];
+  }
+
+  inline unsigned int get_types_count () const
+  {
+    return nTypes + 1;
+  }
+
+  inline const ResourceRefItem &get_ref_item (const ResourceTypeItem &type,
+					      unsigned int i) const
+  {
+    return type.get_ref_item (&(this+typeList), i);
+  }
+
+  inline const PString& get_name (const ResourceRefItem &item,
+				  unsigned int i) const
+  {
+    if (item.nameOffset == -1)
+      return Null (PString);
+
+    return StructAtOffset<PString> (this, nameList + item.nameOffset);
+  }
+
+  protected:
+  HBUINT8	reserved[16];	/* Reserved for copy of resource header */
+  LOffsetTo<ResourceMap>
+		reserved1;	/* Reserved for handle to next resource map */
+  HBUINT16	reserved2;	/* Reserved for file reference number */
+  HBUINT16	attr;		/* Resource fork attribute */
+  OffsetTo<UnsizedArrayOf<ResourceTypeItem> >
+		typeList;	/* Offset from beginning of map to
+				 * resource type list */
+  HBUINT16	nameList;	/* Offset from beginning of map to
+				 * resource name list */
+  HBUINT16	nTypes;		/* Number of types in the map minus 1 */
+  public:
+  DEFINE_SIZE_STATIC (30);
+};
+
+struct ResourceForkHeader
+{
+  inline unsigned int get_face_count () const
+  {
+    const ResourceMap &resource_map = this+map;
+    for (unsigned int i = 0; i < resource_map.get_types_count (); ++i)
+    {
+      const ResourceTypeItem& type = resource_map.get_type (i);
+      if (type.is_sfnt ())
+	return type.get_resource_count ();
+    }
+    return 0;
+  }
+
+  inline const LArrayOf<HBUINT8>& get_data (const ResourceTypeItem& type,
+					    unsigned int idx) const
+  {
+    const ResourceMap &resource_map = this+map;
+    unsigned int offset = dataOffset;
+    offset += resource_map.get_ref_item (type, idx).dataOffset;
+    return StructAtOffset<LArrayOf<HBUINT8> > (this, offset);
+  }
+
+  inline const OpenTypeFontFace& get_face (unsigned int idx) const
+  {
+    const ResourceMap &resource_map = this+map;
+    for (unsigned int i = 0; i < resource_map.get_types_count (); ++i)
+    {
+      const ResourceTypeItem& type = resource_map.get_type (i);
+      if (type.is_sfnt () && idx < type.get_resource_count ())
+	return (OpenTypeFontFace&) get_data (type, idx).arrayZ;
+    }
+    return Null (OpenTypeFontFace);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!c->check_struct (this)))
+      return_trace (false);
+
+    const ResourceMap &resource_map = this+map;
+    if (unlikely (!resource_map.sanitize (c)))
+      return_trace (false);
+
+    for (unsigned int i = 0; i < resource_map.get_types_count (); ++i)
+    {
+      const ResourceTypeItem& type = resource_map.get_type (i);
+      for (unsigned int j = 0; j < type.get_resource_count (); ++j)
+      {
+        const LArrayOf<HBUINT8>& data = get_data (type, j);
+	if (unlikely (!(data.sanitize (c) &&
+			((OpenTypeFontFace&) data.arrayZ).sanitize (c))))
+	  return_trace (false);
+      }
+    }
+
+    return_trace (true);
+  }
+
+  protected:
+  HBUINT32	dataOffset;	/* Offset from beginning of resource fork
+				 * to resource data */
+  LOffsetTo<ResourceMap>
+		map;		/* Offset from beginning of resource fork
+				 * to resource map */
+  HBUINT32	dataLen;	/* Length of resource data */
+  HBUINT32	mapLen;		/* Length of resource map */
+  public:
+  DEFINE_SIZE_STATIC (16);
+};
 
 /*
  * OpenType Font File
  */
 
 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 */
     TTCTag		= HB_TAG ('t','t','c','f'), /* TrueType Collection */
+    DFontTag		= HB_TAG ( 0 , 0 , 1 , 0 ), /* DFont Mac Resource Fork */
     TrueTag		= HB_TAG ('t','r','u','e'), /* Obsolete Apple TrueType */
     Typ1Tag		= HB_TAG ('t','y','p','1')  /* Obsolete Apple Type1 font in SFNT container */
   };
 
   inline hb_tag_t get_tag (void) const { return u.tag; }
 
   inline unsigned int get_face_count (void) const
   {
     switch (u.tag) {
     case CFFTag:	/* All the non-collection tags */
     case TrueTag:
     case Typ1Tag:
     case TrueTypeTag:	return 1;
     case TTCTag:	return u.ttcHeader.get_face_count ();
+//    case DFontTag:	return u.rfHeader.get_face_count ();
     default:		return 0;
     }
   }
   inline const OpenTypeFontFace& get_face (unsigned int i) const
   {
     switch (u.tag) {
     /* Note: for non-collection SFNT data we ignore index.  This is because
      * Apple dfont container is a container of SFNT's.  So each SFNT is a
      * non-TTC, but the index is more than zero. */
     case CFFTag:	/* All the non-collection tags */
     case TrueTag:
     case Typ1Tag:
     case TrueTypeTag:	return u.fontFace;
     case TTCTag:	return u.ttcHeader.get_face (i);
+//    case DFontTag:	return u.rfHeader.get_face (i);
     default:		return Null(OpenTypeFontFace);
     }
   }
 
   inline bool serialize_single (hb_serialize_context_t *c,
 				hb_tag_t sfnt_tag,
 			        Supplier<hb_tag_t> &tags,
 			        Supplier<hb_blob_t *> &blobs,
@@ -348,25 +542,27 @@ struct OpenTypeFontFile
     TRACE_SANITIZE (this);
     if (unlikely (!u.tag.sanitize (c))) return_trace (false);
     switch (u.tag) {
     case CFFTag:	/* All the non-collection tags */
     case TrueTag:
     case Typ1Tag:
     case TrueTypeTag:	return_trace (u.fontFace.sanitize (c));
     case TTCTag:	return_trace (u.ttcHeader.sanitize (c));
+//    case DFontTag:	return_trace (u.rfHeader.sanitize (c));
     default:		return_trace (true);
     }
   }
 
   protected:
   union {
   Tag			tag;		/* 4-byte identifier. */
   OpenTypeFontFace	fontFace;
   TTCHeader		ttcHeader;
+  ResourceForkHeader	rfHeader;
   } u;
   public:
   DEFINE_SIZE_UNION (4, tag);
 };
 
 
 } /* namespace OT */
 
--- a/gfx/harfbuzz/src/hb-open-type-private.hh
+++ b/gfx/harfbuzz/src/hb-open-type-private.hh
@@ -25,17 +25,16 @@
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OPEN_TYPE_PRIVATE_HH
 #define HB_OPEN_TYPE_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
 #include "hb-blob-private.hh"
 #include "hb-face-private.hh"
 
 
 namespace OT {
 
 
 
@@ -225,17 +224,17 @@ struct hb_sanitize_context_t :
        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_int_mul_overflows (len, record_size);
+    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");
@@ -1028,16 +1027,17 @@ struct ArrayOf
 
   public:
   LenType len;
   Type arrayZ[VAR];
   public:
   DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ);
 };
 template <typename Type> struct LArrayOf : ArrayOf<Type, HBUINT32> {};
+typedef ArrayOf<HBUINT8, HBUINT8> PString;
 
 /* Array of Offset's */
 template <typename Type, typename OffsetType=HBUINT16>
 struct OffsetArrayOf : ArrayOf<OffsetTo<Type, OffsetType> > {};
 
 /* Array of offsets relative to the beginning of the array itself. */
 template <typename Type>
 struct OffsetListOf : OffsetArrayOf<Type>
@@ -1172,17 +1172,17 @@ struct BinSearchHeader
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
   }
 
   inline void set (unsigned int v)
   {
     len.set (v);
     assert (len == v);
-    entrySelector.set (MAX (1u, _hb_bit_storage (v)) - 1);
+    entrySelector.set (MAX (1u, hb_bit_storage (v)) - 1);
     searchRange.set (16 * (1u << entrySelector));
     rangeShift.set (v * 16 > searchRange
 		    ? 16 * v - searchRange
 		    : 0);
   }
 
   protected:
   HBUINT16	len;
--- a/gfx/harfbuzz/src/hb-ot-cmap-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-cmap-table.hh
@@ -84,17 +84,17 @@ struct CmapSubtableFormat4
     TRACE_SERIALIZE (this);
 
     if (unlikely (!c->extend_min (*this))) return_trace (false);
 
     this->format.set (4);
     this->length.set (get_sub_table_size (segments));
 
     this->segCountX2.set (segments.len * 2);
-    this->entrySelector.set (MAX (1u, _hb_bit_storage (segments.len)) - 1);
+    this->entrySelector.set (MAX (1u, hb_bit_storage (segments.len)) - 1);
     this->searchRange.set (2 * (1u << this->entrySelector));
     this->rangeShift.set (segments.len * 2 > this->searchRange
                           ? 2 * segments.len - this->searchRange
                           : 0);
 
     HBUINT16 *end_count = c->allocate_size<HBUINT16> (HBUINT16::static_size * segments.len);
     c->allocate_size<HBUINT16> (HBUINT16::static_size); // 2 bytes of padding.
     HBUINT16 *start_count = c->allocate_size<HBUINT16> (HBUINT16::static_size * segments.len);
--- a/gfx/harfbuzz/src/hb-ot-hdmx-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-hdmx-table.hh
@@ -194,17 +194,17 @@ struct hdmx
 
     return result;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && version == 0 &&
-		  !_hb_unsigned_int_mul_overflows (num_records, size_device_record) &&
+		  !hb_unsigned_mul_overflows (num_records, size_device_record) &&
 		  size_device_record >= DeviceRecord::min_size &&
 		  c->check_range (this, get_size()));
   }
 
   protected:
   HBUINT16	version;		/* Table version number (0) */
   HBUINT16	num_records;		/* Number of device records. */
   HBUINT32	size_device_record;	/* Size of a device record, 32-bit aligned. */
--- a/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
@@ -25,17 +25,16 @@
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OT_LAYOUT_COMMON_PRIVATE_HH
 #define HB_OT_LAYOUT_COMMON_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
 #include "hb-ot-layout-private.hh"
 #include "hb-open-type-private.hh"
 #include "hb-set-private.hh"
 
 
 #ifndef HB_MAX_NESTING_LEVEL
 #define HB_MAX_NESTING_LEVEL	6
 #endif
@@ -827,40 +826,53 @@ struct CoverageFormat2
   /* Older compilers need this to be public. */
   struct Iter
   {
     inline void init (const CoverageFormat2 &c_)
     {
       c = &c_;
       coverage = 0;
       i = 0;
-      j = c->rangeRecord.len ? c_.rangeRecord[0].start : 0;
+      j = c->rangeRecord.len ? c->rangeRecord[0].start : 0;
+      if (unlikely (c->rangeRecord[0].start > c->rangeRecord[0].end))
+      {
+        /* Broken table. Skip. */
+        i = c->rangeRecord.len;
+      }
     }
     inline bool more (void) { return i < c->rangeRecord.len; }
     inline void next (void)
     {
       if (j >= c->rangeRecord[i].end)
       {
         i++;
 	if (more ())
 	{
+	  hb_codepoint_t old = j;
 	  j = c->rangeRecord[i].start;
+	  if (unlikely (j <= old))
+	  {
+	    /* Broken table. Skip. Important to avoid DoS. */
+	   i = c->rangeRecord.len;
+	   return;
+	  }
 	  coverage = c->rangeRecord[i].value;
 	}
 	return;
       }
       coverage++;
       j++;
     }
     inline hb_codepoint_t get_glyph (void) { return j; }
     inline unsigned int get_coverage (void) { return coverage; }
 
     private:
     const struct CoverageFormat2 *c;
-    unsigned int i, j, coverage;
+    unsigned int i, coverage;
+    hb_codepoint_t j;
   };
   private:
 
   protected:
   HBUINT16	coverageFormat;	/* Format identifier--format = 2 */
   SortedArrayOf<RangeRecord>
 		rangeRecord;	/* Array of glyph ranges--ordered by
 				 * Start GlyphID. rangeCount entries
--- a/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
@@ -94,17 +94,17 @@ struct ValueFormat : HBUINT16
 					 * horizontal advance--measured from
 					 * beginning of PosTable (may be NULL) */
   Offset	yAdvDevice;		/* Offset to Device table for vertical
 					 * advance--measured from beginning of
 					 * PosTable (may be NULL) */
 #endif
 
   inline unsigned int get_len (void) const
-  { return _hb_popcount ((unsigned int) *this); }
+  { return hb_popcount ((unsigned int) *this); }
   inline unsigned int get_size (void) const
   { return get_len () * Value::static_size; }
 
   void apply_value (hb_ot_apply_context_t   *c,
 		    const void           *base,
 		    const Value          *values,
 		    hb_glyph_position_t  &glyph_pos) const
   {
@@ -369,17 +369,17 @@ struct AnchorMatrix
     *found = !matrixZ[row * cols + col].is_null ();
     return this+matrixZ[row * cols + col];
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const
   {
     TRACE_SANITIZE (this);
     if (!c->check_struct (this)) return_trace (false);
-    if (unlikely (_hb_unsigned_int_mul_overflows (rows, cols))) return_trace (false);
+    if (unlikely (hb_unsigned_mul_overflows (rows, cols))) return_trace (false);
     unsigned int count = rows * cols;
     if (!c->check_array (matrixZ, matrixZ[0].static_size, count)) return_trace (false);
     for (unsigned int i = 0; i < count; i++)
       if (!matrixZ[i].sanitize (c, this)) return_trace (false);
     return_trace (true);
   }
 
   HBUINT16	rows;			/* Number of rows */
@@ -1069,20 +1069,23 @@ struct MarkBasePosFormat1
     /* Now we search backwards for a non-mark glyph */
     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset (buffer->idx, 1);
     skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
     do {
       if (!skippy_iter.prev ()) return_trace (false);
       /* We only want to attach to the first of a MultipleSubst sequence.
        * https://github.com/harfbuzz/harfbuzz/issues/740
-       * Reject others. */
+       * Reject others...
+       * ...but stop if we find a mark in the MultipleSubst sequence:
+       * https://github.com/harfbuzz/harfbuzz/issues/1020 */
       if (!_hb_glyph_info_multiplied (&buffer->info[skippy_iter.idx]) ||
 	  0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) ||
 	  (skippy_iter.idx == 0 ||
+	   _hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx - 1]) ||
 	   _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]) !=
 	   _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx - 1]) ||
 	   _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) !=
 	   _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx - 1]) + 1
 	   ))
 	break;
       skippy_iter.reject ();
     } while (1);
--- a/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
@@ -506,17 +506,17 @@ struct AlternateSubstFormat1
     const AlternateSet &alt_set = this+alternateSet[index];
 
     if (unlikely (!alt_set.len)) return_trace (false);
 
     hb_mask_t glyph_mask = c->buffer->cur().mask;
     hb_mask_t lookup_mask = c->lookup_mask;
 
     /* Note: This breaks badly if two features enabled this lookup together. */
-    unsigned int shift = _hb_ctz (lookup_mask);
+    unsigned int shift = hb_ctz (lookup_mask);
     unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
 
     if (unlikely (alt_index > alt_set.len || alt_index == 0)) return_trace (false);
 
     glyph_id = alt_set[alt_index - 1];
 
     c->replace_glyph (glyph_id);
 
--- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
@@ -25,17 +25,16 @@
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
 #define HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
 #include "hb-buffer-private.hh"
 #include "hb-map-private.hh"
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-set-private.hh"
 
 
 namespace OT {
 
@@ -383,17 +382,17 @@ struct hb_ot_apply_context_t :
 	if (skip == matcher_t::SKIP_NO)
 	  return false;
       }
       return false;
     }
     inline bool prev (void)
     {
       assert (num_items > 0);
-      while (idx >= num_items)
+      while (idx > num_items - 1)
       {
 	idx--;
 	const hb_glyph_info_t &info = c->buffer->out_info[idx];
 
 	matcher_t::may_skip_t skip = matcher.may_skip (c, info);
 	if (unlikely (skip == matcher_t::SKIP_YES))
 	  continue;
 
--- a/gfx/harfbuzz/src/hb-ot-map.cc
+++ b/gfx/harfbuzz/src/hb-ot-map.cc
@@ -140,17 +140,17 @@ void hb_ot_map_builder_t::add_pause (uns
 
 void
 hb_ot_map_builder_t::compile (hb_ot_map_t  &m,
 			      const int    *coords,
 			      unsigned int  num_coords)
 {
   static_assert ((!(HB_GLYPH_FLAG_DEFINED & (HB_GLYPH_FLAG_DEFINED + 1))), "");
   unsigned int global_bit_mask = HB_GLYPH_FLAG_DEFINED + 1;
-  unsigned int global_bit_shift = _hb_popcount (HB_GLYPH_FLAG_DEFINED);
+  unsigned int global_bit_shift = hb_popcount (HB_GLYPH_FLAG_DEFINED);
 
   m.global_mask = global_bit_mask;
 
   unsigned int required_feature_index[2];
   hb_tag_t required_feature_tag[2];
   /* We default to applying required feature in stage 0.  If the required
    * feature has a tag that is known to the shaper, we apply required feature
    * in the stage for that tag.
@@ -204,17 +204,17 @@ hb_ot_map_builder_t::compile (hb_ot_map_
 
     unsigned int bits_needed;
 
     if ((info->flags & F_GLOBAL) && info->max_value == 1)
       /* Uses the global bit */
       bits_needed = 0;
     else
       /* Limit to 8 bits per feature. */
-      bits_needed = MIN(8u, _hb_bit_storage (info->max_value));
+      bits_needed = MIN(8u, hb_bit_storage (info->max_value));
 
     if (!info->max_value || next_bit + bits_needed > 8 * sizeof (hb_mask_t))
       continue; /* Feature disabled, or not enough bits. */
 
 
     hb_bool_t found = false;
     unsigned int feature_index[2];
     for (unsigned int table_index = 0; table_index < 2; table_index++)
--- a/gfx/harfbuzz/src/hb-ot-os2-unicode-ranges.hh
+++ b/gfx/harfbuzz/src/hb-ot-os2-unicode-ranges.hh
@@ -23,17 +23,16 @@
  *
  * Google Author(s): Garret Rieger
  */
 
 #ifndef HB_OT_OS2_UNICODE_RANGES_HH
 #define HB_OT_OS2_UNICODE_RANGES_HH
 
 #include "hb-private.hh"
-#include "hb-dsalgs.hh"
 
 namespace OT {
 
 struct Range {
   hb_codepoint_t start;
   hb_codepoint_t end;
   unsigned int bit;
 };
--- a/gfx/harfbuzz/src/hb-ot-post-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-post-table.hh
@@ -23,17 +23,16 @@
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OT_POST_TABLE_HH
 #define HB_OT_POST_TABLE_HH
 
 #include "hb-open-type-private.hh"
-#include "hb-dsalgs.hh"
 #include "hb-subset-plan.hh"
 
 #define HB_STRING_ARRAY_NAME format1_names
 #define HB_STRING_ARRAY_LIST "hb-ot-post-macroman.hh"
 #include "hb-string-array.hh"
 #undef HB_STRING_ARRAY_LIST
 #undef HB_STRING_ARRAY_NAME
 
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
@@ -20,17 +20,16 @@
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
 #include "hb-ot-shape-complex-arabic-private.hh"
 #include "hb-ot-shape-private.hh"
 
 
 /* buffer var allocations */
 #define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */
 
 #define HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH HB_BUFFER_SCRATCH_FLAG_COMPLEX0
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
@@ -663,31 +663,38 @@ initial_reordering_consonant_syllable (c
   if (has_reph)
     info[start].indic_position() = POS_RA_TO_BECOME_REPH;
 
   /* For old-style Indic script tags, move the first post-base Halant after
    * 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).  We don't
-   * currently know about other scripts, so we single out Malayalam for now.
+   * 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.
    *
    * Kannada test case:
    * U+0C9A,U+0CCD,U+0C9A,U+0CCD
    * With some versions of Lohit Kannada.
    * https://bugs.freedesktop.org/show_bug.cgi?id=59118
    *
    * Malayalam test case:
    * U+0D38,U+0D4D,U+0D31,U+0D4D,U+0D31,U+0D4D
    * With lohit-ttf-20121122/Lohit-Malayalam.ttf
+   *
+   * Bengali test case
+   * U+0998,U+09CD,U+09AF,U+09CD
+   * With Windows XP vrinda.ttf
+   * https://github.com/harfbuzz/harfbuzz/issues/1073
    */
   if (indic_plan->is_old_spec)
   {
-    bool disallow_double_halants = buffer->props.script != HB_SCRIPT_MALAYALAM;
+    bool disallow_double_halants = buffer->props.script != HB_SCRIPT_MALAYALAM &&
+				   buffer->props.script != HB_SCRIPT_BENGALI;
     for (unsigned int i = base + 1; i < end; i++)
       if (info[i].indic_category() == OT_H)
       {
         unsigned int j;
         for (j = end - 1; j > i; j--)
 	  if (is_consonant (info[j]) ||
 	      (disallow_double_halants && info[j].indic_category() == OT_H))
 	    break;
@@ -1115,42 +1122,79 @@ final_reordering_syllable (const hb_ot_s
   /*   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.
+   *
+   * IMPLEMENTATION NOTES:
+   *
+   * It looks like the last sentence is wrong.  Testing, with Windows 7 Uniscribe
+   * and Devanagari shows that the behavior is best described as:
+   *
+   * "If ZWJ follows this halant, matra is NOT repositioned after this halant.
+   *  If ZWNJ follows this halant, position is moved after it."
+   *
+   * Test case, with Adobe Devanagari or Nirmala UI:
+   *
+   *   U+091F,U+094D,U+200C,U+092F,U+093F
+   *   (Matra moves to the middle, after ZWNJ.)
+   *
+   *   U+091F,U+094D,U+200D,U+092F,U+093F
+   *   (Matra does NOT move, stays to the left.)
+   *
+   * https://github.com/harfbuzz/harfbuzz/issues/1070
    */
 
   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;
 
     /* Malayalam / Tamil do not have "half" forms or explicit virama forms.
      * The glyphs formed by 'half' are Chillus or ligated explicit viramas.
      * We want to position matra after them.
      */
     if (buffer->props.script != HB_SCRIPT_MALAYALAM && buffer->props.script != HB_SCRIPT_TAMIL)
     {
+    search:
       while (new_pos > start &&
 	     !(is_one_of (info[new_pos], (FLAG (OT_M) | FLAG (OT_H)))))
 	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_halant (info[new_pos]) &&
 	  info[new_pos].indic_position() != POS_PRE_M)
       {
+#if 0 // See comment above
 	/* -> 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++;
+#endif
+	if (new_pos + 1 < end)
+	{
+	  /* -> If ZWJ follows this halant, matra is NOT repositioned after this halant. */
+	  if (info[new_pos + 1].indic_category() == OT_ZWJ)
+	  {
+	    /* Keep searching. */
+	    if (new_pos > start)
+	    {
+	      new_pos--;
+	      goto search;
+	    }
+	  }
+	  /* -> If ZWNJ follows this halant, position is moved after it. */
+	  if (info[new_pos + 1].indic_category() == OT_ZWNJ)
+	    new_pos++;
+	}
       }
       else
         new_pos = start; /* No move. */
     }
 
     if (start < new_pos && info[new_pos].indic_position () != POS_PRE_M)
     {
       /* Now go see if there's actually any matras... */
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-khmer.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-khmer.cc
@@ -367,32 +367,35 @@ initial_reordering_consonant_syllable (c
     base = end;
     for (unsigned int i = start; i < end; i++)
       if (info[i].khmer_position() == POS_BASE_C)
       {
 	base = i;
 	break;
       }
 
-    /* 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)
+    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)
 	{
-	  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;
+	  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);
 	}
-	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 */
 
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-use-table.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use-table.cc
@@ -555,17 +555,17 @@ static const USE_TABLE_ELEMENT_TYPE use_
   /* 112B0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 112C0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 112D0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B, VMAbv,
   /* 112E0 */  VPst,  VPre,  VPst,  VBlw,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv, CMBlw,  VBlw,     O,     O,     O,     O,     O,
   /* 112F0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
 
   /* Grantha */
 
-  /* 11300 */ VMAbv, VMAbv, VMPst, VMPst,     O,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     B,
+  /* 11300 */ VMAbv, VMAbv, VMAbv, VMPst,     O,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     B,
   /* 11310 */     B,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11320 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
   /* 11330 */     B,     O,     B,     B,     O,     B,     B,     B,     B,     B,     O, CMBlw, CMBlw,     B,  VPst,  VPst,
   /* 11340 */  VAbv,  VPst,  VPst,  VPst,  VPst,     O,     O,  VPre,  VPre,     O,     O,  VPre,  VPre,     H,     O,     O,
   /* 11350 */     O,     O,     O,     O,     O,     O,     O,  VPst,     O,     O,     O,     O,     O,     O,     B,     B,
   /* 11360 */     B,     B,  VPst,  VPst,     O,     O, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,     O,     O,     O,
   /* 11370 */ VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,     O,     O,     O,
 
@@ -668,17 +668,17 @@ static const USE_TABLE_ELEMENT_TYPE use_
 
 
   /* Bhaiksuki */
 
   /* 11C00 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
   /* 11C10 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11C20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,  VPst,
   /* 11C30 */  VAbv,  VAbv,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,     O,  VAbv,  VAbv,  VAbv,  VAbv, VMAbv, VMAbv, VMPst,     H,
-  /* 11C40 */     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 11C40 */     B,     O,     O,     O,    GB,    GB,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
   /* 11C50 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11C60 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,
 
   /* Marchen */
 
   /* 11C70 */     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11C80 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11C90 */     O,     O,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,
--- a/gfx/harfbuzz/src/hb-ot-shape.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape.cc
@@ -824,22 +824,22 @@ hb_propagate_flags (hb_buffer_t *buffer)
 
 /* Pull it all together! */
 
 static void
 hb_ot_shape_internal (hb_ot_shape_context_t *c)
 {
   c->buffer->deallocate_var_all ();
   c->buffer->scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT;
-  if (likely (!_hb_unsigned_int_mul_overflows (c->buffer->len, HB_BUFFER_MAX_LEN_FACTOR)))
+  if (likely (!hb_unsigned_mul_overflows (c->buffer->len, HB_BUFFER_MAX_LEN_FACTOR)))
   {
     c->buffer->max_len = MAX (c->buffer->len * HB_BUFFER_MAX_LEN_FACTOR,
 			      (unsigned) HB_BUFFER_MAX_LEN_MIN);
   }
-  if (likely (!_hb_unsigned_int_mul_overflows (c->buffer->len, HB_BUFFER_MAX_OPS_FACTOR)))
+  if (likely (!hb_unsigned_mul_overflows (c->buffer->len, HB_BUFFER_MAX_OPS_FACTOR)))
   {
     c->buffer->max_ops = MAX (c->buffer->len * HB_BUFFER_MAX_OPS_FACTOR,
 			      (unsigned) HB_BUFFER_MAX_OPS_MIN);
   }
 
   bool disable_otl = c->plan->shaper->disable_otl && c->plan->shaper->disable_otl (c->plan);
   //c->fallback_substitute     = disable_otl || !hb_ot_layout_has_substitution (c->face);
   c->fallback_positioning    = disable_otl || !hb_ot_layout_has_positioning (c->face);
--- a/gfx/harfbuzz/src/hb-private.hh
+++ b/gfx/harfbuzz/src/hb-private.hh
@@ -53,56 +53,84 @@
 
 #if (defined(_MSC_VER) && _MSC_VER >= 1500) || defined(__MINGW32__)
 #include <intrin.h>
 #endif
 
 #define HB_PASTE1(a,b) a##b
 #define HB_PASTE(a,b) HB_PASTE1(a,b)
 
+
 /* Compile-time custom allocator support. */
 
 #if defined(hb_malloc_impl) \
  && defined(hb_calloc_impl) \
  && defined(hb_realloc_impl) \
  && defined(hb_free_impl)
 extern "C" void* hb_malloc_impl(size_t size);
 extern "C" void* hb_calloc_impl(size_t nmemb, size_t size);
 extern "C" void* hb_realloc_impl(void *ptr, size_t size);
 extern "C" void  hb_free_impl(void *ptr);
 #define malloc hb_malloc_impl
 #define calloc hb_calloc_impl
 #define realloc hb_realloc_impl
 #define free hb_free_impl
+
+#if defined(hb_memalign_impl)
+extern "C" int hb_memalign_impl(void **memptr, size_t alignment, size_t size);
+#define posix_memalign hb_memalign_impl
+#else
+#undef HAVE_POSIX_MEMALIGN
+#endif
+
 #endif
 
 
 /* Compiler attributes */
 
 
+template <typename T>
+struct _hb_alignof
+{
+  struct s
+  {
+    char c;
+    T t;
+  };
+  static constexpr unsigned int value = offsetof (s, t);
+};
+
 #if __cplusplus < 201103L
 
 #ifndef nullptr
 #define nullptr NULL
 #endif
 
+#ifndef constexpr
+#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]
 #endif // static_assert
 
 #ifdef __GNUC__
 #if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8))
 #define thread_local __thread
 #endif
 #else
 #define thread_local
 #endif
 
+#ifndef alignof
+#define alignof(x) (_hb_alignof<x>::value)
+#endif // alignof
+
 #endif // __cplusplus < 201103L
 
 #if (defined(__GNUC__) || defined(__clang__)) && defined(__OPTIMIZE__)
 #define likely(expr) (__builtin_expect (!!(expr), 1))
 #define unlikely(expr) (__builtin_expect (!!(expr), 0))
 #else
 #define likely(expr) (expr)
 #define unlikely(expr) (expr)
@@ -246,36 +274,16 @@ static int errno = 0; /* Use something b
  */
 #    define HB_USE_ATEXIT 1
 #  endif
 #endif
 #ifdef HB_NO_ATEXIT
 #  undef HB_USE_ATEXIT
 #endif
 
-/* Basics */
-
-#undef MIN
-template <typename Type>
-static inline Type MIN (const Type &a, const Type &b) { return a < b ? a : b; }
-
-#undef MAX
-template <typename Type>
-static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; }
-
-static inline unsigned int DIV_CEIL (const unsigned int a, unsigned int b)
-{ return (a + (b - 1)) / b; }
-
-
-#undef  ARRAY_LENGTH
-template <typename Type, unsigned int n>
-static inline unsigned int ARRAY_LENGTH (const Type (&)[n]) { return n; }
-/* A const version, but does not detect erratically being called on pointers. */
-#define ARRAY_LENGTH_CONST(__array) ((signed int) (sizeof (__array) / sizeof (__array[0])))
-
 #define HB_STMT_START do
 #define HB_STMT_END   while (0)
 
 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>))
 
@@ -317,233 +325,16 @@ static_assert ((sizeof (hb_var_int_t) ==
 /* Check _assertion in a method environment */
 #define _ASSERT_POD1(_line) \
 	HB_UNUSED inline void _static_assertion_on_line_##_line (void) const \
 	{ _ASSERT_INSTANCE_POD1 (_line, *this); /* Make sure it's POD. */ }
 # define _ASSERT_POD0(_line)	_ASSERT_POD1 (_line)
 # define ASSERT_POD()		_ASSERT_POD0 (__LINE__)
 
 
-
-/* Tiny functions */
-
-/*
- * Void!
- */
-typedef const struct _hb_void_t *hb_void_t;
-#define HB_VOID ((const _hb_void_t *) nullptr)
-
-/* Return the number of 1 bits in v. */
-template <typename T>
-static inline HB_CONST_FUNC unsigned int
-_hb_popcount (T v)
-{
-#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) && defined(__OPTIMIZE__)
-  if (sizeof (T) <= sizeof (unsigned int))
-    return __builtin_popcount (v);
-
-  if (sizeof (T) <= sizeof (unsigned long))
-    return __builtin_popcountl (v);
-
-  if (sizeof (T) <= sizeof (unsigned long long))
-    return __builtin_popcountll (v);
-#endif
-
-  if (sizeof (T) <= 4)
-  {
-    /* "HACKMEM 169" */
-    uint32_t y;
-    y = (v >> 1) &033333333333;
-    y = v - y - ((y >>1) & 033333333333);
-    return (((y + (y >> 3)) & 030707070707) % 077);
-  }
-
-  if (sizeof (T) == 8)
-  {
-    unsigned int shift = 32;
-    return _hb_popcount<uint32_t> ((uint32_t) v) + _hb_popcount ((uint32_t) (v >> shift));
-  }
-
-  if (sizeof (T) == 16)
-  {
-    unsigned int shift = 64;
-    return _hb_popcount<uint64_t> ((uint64_t) v) + _hb_popcount ((uint64_t) (v >> shift));
-  }
-
-  assert (0);
-  return 0; /* Shut up stupid compiler. */
-}
-
-/* Returns the number of bits needed to store number */
-template <typename T>
-static inline HB_CONST_FUNC unsigned int
-_hb_bit_storage (T v)
-{
-  if (unlikely (!v)) return 0;
-
-#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__)
-  if (sizeof (T) <= sizeof (unsigned int))
-    return sizeof (unsigned int) * 8 - __builtin_clz (v);
-
-  if (sizeof (T) <= sizeof (unsigned long))
-    return sizeof (unsigned long) * 8 - __builtin_clzl (v);
-
-  if (sizeof (T) <= sizeof (unsigned long long))
-    return sizeof (unsigned long long) * 8 - __builtin_clzll (v);
-#endif
-
-#if (defined(_MSC_VER) && _MSC_VER >= 1500) || defined(__MINGW32__)
-  if (sizeof (T) <= sizeof (unsigned int))
-  {
-    unsigned long where;
-    _BitScanReverse (&where, v);
-    return 1 + where;
-  }
-# if _WIN64
-  if (sizeof (T) <= 8)
-  {
-    unsigned long where;
-    _BitScanReverse64 (&where, v);
-    return 1 + where;
-  }
-# endif
-#endif
-
-  if (sizeof (T) <= 4)
-  {
-    /* "bithacks" */
-    const unsigned int b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000};
-    const unsigned int S[] = {1, 2, 4, 8, 16};
-    unsigned int r = 0;
-    for (int i = 4; i >= 0; i--)
-      if (v & b[i])
-      {
-	v >>= S[i];
-	r |= S[i];
-      }
-    return r + 1;
-  }
-  if (sizeof (T) <= 8)
-  {
-    /* "bithacks" */
-    const uint64_t b[] = {0x2ULL, 0xCULL, 0xF0ULL, 0xFF00ULL, 0xFFFF0000ULL, 0xFFFFFFFF00000000ULL};
-    const unsigned int S[] = {1, 2, 4, 8, 16, 32};
-    unsigned int r = 0;
-    for (int i = 5; i >= 0; i--)
-      if (v & b[i])
-      {
-	v >>= S[i];
-	r |= S[i];
-      }
-    return r + 1;
-  }
-  if (sizeof (T) == 16)
-  {
-    unsigned int shift = 64;
-    return (v >> shift) ? _hb_bit_storage<uint64_t> ((uint64_t) (v >> shift)) + shift :
-			  _hb_bit_storage<uint64_t> ((uint64_t) v);
-  }
-
-  assert (0);
-  return 0; /* Shut up stupid compiler. */
-}
-
-/* Returns the number of zero bits in the least significant side of v */
-template <typename T>
-static inline HB_CONST_FUNC unsigned int
-_hb_ctz (T v)
-{
-  if (unlikely (!v)) return 0;
-
-#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__)
-  if (sizeof (T) <= sizeof (unsigned int))
-    return __builtin_ctz (v);
-
-  if (sizeof (T) <= sizeof (unsigned long))
-    return __builtin_ctzl (v);
-
-  if (sizeof (T) <= sizeof (unsigned long long))
-    return __builtin_ctzll (v);
-#endif
-
-#if (defined(_MSC_VER) && _MSC_VER >= 1500) || defined(__MINGW32__)
-  if (sizeof (T) <= sizeof (unsigned int))
-  {
-    unsigned long where;
-    _BitScanForward (&where, v);
-    return where;
-  }
-# if _WIN64
-  if (sizeof (T) <= 8)
-  {
-    unsigned long where;
-    _BitScanForward64 (&where, v);
-    return where;
-  }
-# endif
-#endif
-
-  if (sizeof (T) <= 4)
-  {
-    /* "bithacks" */
-    unsigned int c = 32;
-    v &= - (int32_t) v;
-    if (v) c--;
-    if (v & 0x0000FFFF) c -= 16;
-    if (v & 0x00FF00FF) c -= 8;
-    if (v & 0x0F0F0F0F) c -= 4;
-    if (v & 0x33333333) c -= 2;
-    if (v & 0x55555555) c -= 1;
-    return c;
-  }
-  if (sizeof (T) <= 8)
-  {
-    /* "bithacks" */
-    unsigned int c = 64;
-    v &= - (int64_t) (v);
-    if (v) c--;
-    if (v & 0x00000000FFFFFFFFULL) c -= 32;
-    if (v & 0x0000FFFF0000FFFFULL) c -= 16;
-    if (v & 0x00FF00FF00FF00FFULL) c -= 8;
-    if (v & 0x0F0F0F0F0F0F0F0FULL) c -= 4;
-    if (v & 0x3333333333333333ULL) c -= 2;
-    if (v & 0x5555555555555555ULL) c -= 1;
-    return c;
-  }
-  if (sizeof (T) == 16)
-  {
-    unsigned int shift = 64;
-    return (uint64_t) v ? _hb_bit_storage<uint64_t> ((uint64_t) v) :
-			  _hb_bit_storage<uint64_t> ((uint64_t) v >> shift) + shift;
-  }
-
-  assert (0);
-  return 0; /* Shut up stupid compiler. */
-}
-
-static inline bool
-_hb_unsigned_int_mul_overflows (unsigned int count, unsigned int size)
-{
-  return (size > 0) && (count >= ((unsigned int) -1) / size);
-}
-
-static inline unsigned int
-_hb_ceil_to_4 (unsigned int v)
-{
-  return ((v - 1) | 3) + 1;
-}
-
-
-
-/*
- *
- * Utility types
- *
- */
-
 #define HB_DISALLOW_COPY_AND_ASSIGN(TypeName) \
   TypeName(const TypeName&); \
   void operator=(const TypeName&)
 
 /*
  * Static pools
  */
 
@@ -615,331 +406,16 @@ struct CrapOrNull {
 };
 template <typename Type>
 struct CrapOrNull<const Type> {
   static inline Type const & get (void) { return Null(Type); }
 };
 #define CrapOrNull(Type) CrapOrNull<Type>::get ()
 
 
-
-/* arrays and maps */
-
-
-#define HB_PREALLOCED_ARRAY_INIT {0, 0, 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_int_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;
-  }
-};
-
-template <typename Type>
-struct hb_auto_t : Type
-{
-  hb_auto_t (void) { Type::init (); }
-  ~hb_auto_t (void) { Type::fini (); }
-  private: /* Hide */
-  void init (void) {}
-  void fini (void) {}
-};
-template <typename Type>
-struct hb_auto_array_t : hb_auto_t <hb_vector_t <Type> > {};
-
-
-#define HB_LOCKABLE_SET_INIT {HB_PREALLOCED_ARRAY_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 ();
-  }
-
-};
-
-
 /* 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'; }
@@ -1022,154 +498,38 @@ hb_in_ranges (T u, T lo1, T hi1, T lo2, 
  * For example, for testing "x ∈ {x1, x2, x3}" use:
  * (FLAG_UNSAFE(x) & (FLAG(x1) | FLAG(x2) | FLAG(x3)))
  */
 #define FLAG(x) (ASSERT_STATIC_EXPR_ZERO ((unsigned int)(x) < 32) + (1U << (unsigned int)(x)))
 #define FLAG_UNSAFE(x) ((unsigned int)(x) < 32 ? (1U << (unsigned int)(x)) : 0)
 #define FLAG_RANGE(x,y) (ASSERT_STATIC_EXPR_ZERO ((x) < (y)) + FLAG(y+1) - FLAG(x))
 
 
-template <typename T, typename T2> static inline void
-hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *), T2 *array2)
-{
-  for (unsigned int i = 1; i < len; i++)
-  {
-    unsigned int j = i;
-    while (j && compar (&array[j - 1], &array[i]) > 0)
-      j--;
-    if (i == j)
-      continue;
-    /* Move item i to occupy place for item j, shift what's in between. */
-    {
-      T t = array[i];
-      memmove (&array[j + 1], &array[j], (i - j) * sizeof (T));
-      array[j] = t;
-    }
-    if (array2)
-    {
-      T2 t = array2[i];
-      memmove (&array2[j + 1], &array2[j], (i - j) * sizeof (T2));
-      array2[j] = t;
-    }
-  }
-}
-
-template <typename T> static inline void
-hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *))
-{
-  hb_stable_sort (array, len, compar, (int *) nullptr);
-}
-
-static inline hb_bool_t
-hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out)
-{
-  /* Pain because we don't know whether s is nul-terminated. */
-  char buf[64];
-  len = MIN (ARRAY_LENGTH (buf) - 1, len);
-  strncpy (buf, s, len);
-  buf[len] = '\0';
-
-  char *end;
-  errno = 0;
-  unsigned long v = strtoul (buf, &end, base);
-  if (errno) return false;
-  if (*end) return false;
-  *out = v;
-  return true;
-}
-
-
-/* Vectorization */
-
-struct HbOpOr
-{
-  static const bool passthru_left = true;
-  static const bool passthru_right = true;
-  template <typename T> static void process (T &o, const T &a, const T &b) { o = a | b; }
-};
-struct HbOpAnd
-{
-  static const bool passthru_left = false;
-  static const bool passthru_right = false;
-  template <typename T> static void process (T &o, const T &a, const T &b) { o = a & b; }
-};
-struct HbOpMinus
-{
-  static const bool passthru_left = true;
-  static const bool passthru_right = false;
-  template <typename T> static void process (T &o, const T &a, const T &b) { o = a & ~b; }
-};
-struct HbOpXor
-{
-  static const bool passthru_left = true;
-  static const bool passthru_right = true;
-  template <typename T> static void process (T &o, const T &a, const T &b) { o = a ^ b; }
-};
-
 
 /* Compiler-assisted vectorization. */
 
-/* The `vector_size' attribute was introduced in gcc 3.1. */
-#if defined( __GNUC__ ) && ( __GNUC__ >= 4 )
-#define HB_VECTOR_SIZE 128
-#elif !defined(HB_VECTOR_SIZE)
-#define HB_VECTOR_SIZE 0
+/*
+ * 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
 
-/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))). */
-template <typename elt_t, unsigned int byte_size>
-struct hb_vector_size_t
-{
-  elt_t& operator [] (unsigned int i) { return u.v[i]; }
-  const elt_t& operator [] (unsigned int i) const { return u.v[i]; }
-
-  template <class Op>
-  inline hb_vector_size_t process (const hb_vector_size_t &o) const
-  {
-    hb_vector_size_t r;
-#if HB_VECTOR_SIZE
-    if (HB_VECTOR_SIZE && 0 == (byte_size * 8) % HB_VECTOR_SIZE)
-      for (unsigned int i = 0; i < ARRAY_LENGTH (u.vec); i++)
-	Op::process (r.u.vec[i], u.vec[i], o.u.vec[i]);
-    else
+/* 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
-      for (unsigned int i = 0; i < ARRAY_LENGTH (u.v); i++)
-	Op::process (r.u.v[i], u.v[i], o.u.v[i]);
-    return r;
-  }
-  inline hb_vector_size_t operator | (const hb_vector_size_t &o) const
-  { return process<HbOpOr> (o); }
-  inline hb_vector_size_t operator & (const hb_vector_size_t &o) const
-  { return process<HbOpAnd> (o); }
-  inline hb_vector_size_t operator ^ (const hb_vector_size_t &o) const
-  { return process<HbOpXor> (o); }
-  inline hb_vector_size_t operator ~ () const
-  {
-    hb_vector_size_t r;
-#if HB_VECTOR_SIZE && 0
-    if (HB_VECTOR_SIZE && 0 == (byte_size * 8) % HB_VECTOR_SIZE)
-      for (unsigned int i = 0; i < ARRAY_LENGTH (u.vec); i++)
-	r.u.vec[i] = ~u.vec[i];
-    else
-#endif
-    for (unsigned int i = 0; i < ARRAY_LENGTH (u.v); i++)
-      r.u.v[i] = ~u.v[i];
-    return r;
-  }
-
-  private:
-  static_assert (byte_size / sizeof (elt_t) * sizeof (elt_t) == byte_size, "");
-  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)];
-#endif
-  } u;
-};
 
 
 /* Global runtime options. */
 
 struct hb_options_t
 {
   unsigned int initialized : 1;
   unsigned int uniscribe_bug_compatible : 1;
@@ -1194,48 +554,57 @@ hb_options (void)
 
   return _hb_options.opts;
 }
 
 /* Size signifying variable-sized array */
 #define VAR 1
 
 
-/* String type. */
-
-struct hb_bytes_t
-{
-  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_bytes_t &a) const
-  {
-    if (len != a.len)
-      return (int) a.len - (int) len;
-
-    return memcmp (a.bytes, bytes, len);
-  }
-  static inline int cmp (const void *pa, const void *pb)
-  {
-    hb_bytes_t *a = (hb_bytes_t *) pa;
-    hb_bytes_t *b = (hb_bytes_t *) pb;
-    return b->cmp (*a);
-  }
-
-  const char *bytes;
-  unsigned int len;
-};
-
-
 /* fallback for round() */
-#if !defined (HAVE_ROUND) && !defined (HAVE_DECL_ROUND)
 static inline double
-round (double x)
+_hb_round (double x)
 {
   if (x >= 0)
     return floor (x + 0.5);
   else
     return ceil (x - 0.5);
 }
+#if !defined (HAVE_ROUND) && !defined (HAVE_DECL_ROUND)
+#define round(x) _hb_round(x)
 #endif
 
 
+/* fallback for posix_memalign() */
+static inline int
+_hb_memalign(void **memptr, size_t alignment, size_t size)
+{
+  if (unlikely (0 != (alignment & (alignment - 1)) ||
+		!alignment ||
+		0 != (alignment & (sizeof (void *) - 1))))
+    return EINVAL;
+
+  char *p = (char *) malloc (size + alignment - 1);
+  if (unlikely (!p))
+    return ENOMEM;
+
+  size_t off = (size_t) p & (alignment - 1);
+  if (off)
+    p += alignment - off;
+
+  *memptr = (void *) p;
+
+  return 0;
+}
+#if !defined(posix_memalign) && !defined(HAVE_POSIX_MEMALIGN)
+#define posix_memalign _hb_memalign
+#endif
+
+
+/* Headers we include for everyone.  Keep sorted.  They express dependency amongst
+ * themselves, but no other file should include them.*/
+#include "hb-atomic-private.hh"
+#include "hb-debug.hh"
+#include "hb-dsalgs.hh"
+#include "hb-mutex-private.hh"
+#include "hb-object-private.hh"
+
 #endif /* HB_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-set-private.hh
+++ b/gfx/harfbuzz/src/hb-set-private.hh
@@ -23,17 +23,16 @@
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_SET_PRIVATE_HH
 #define HB_SET_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-object-private.hh"
 
 
 /*
  * hb_set_t
  */
 
 /* TODO Keep a free-list so we can free pages that are completely zeroed.  At that
  * point maybe also use a sentinel value for "all-1" pages? */
@@ -89,17 +88,17 @@ struct hb_set_t
     {
       return 0 == memcmp (&v, &other->v, sizeof (v));
     }
 
     inline unsigned int get_population (void) const
     {
       unsigned int pop = 0;
       for (unsigned int i = 0; i < len (); i++)
-        pop += _hb_popcount (v[i]);
+        pop += hb_popcount (v[i]);
       return pop;
     }
 
     inline bool next (hb_codepoint_t *codepoint) const
     {
       unsigned int m = (*codepoint + 1) & MASK;
       if (!m)
       {
@@ -156,18 +155,18 @@ struct hb_set_t
 	  return i * ELT_BITS + elt_get_max (v[i]);
       return 0;
     }
 
     typedef unsigned long long elt_t;
     static const unsigned int PAGE_BITS = 512;
     static_assert ((PAGE_BITS & ((PAGE_BITS) - 1)) == 0, "");
 
-    static inline unsigned int elt_get_min (const elt_t &elt) { return _hb_ctz (elt); }
-    static inline unsigned int elt_get_max (const elt_t &elt) { return _hb_bit_storage (elt) - 1; }
+    static inline unsigned int elt_get_min (const elt_t &elt) { return hb_ctz (elt); }
+    static inline unsigned int elt_get_max (const elt_t &elt) { return hb_bit_storage (elt) - 1; }
 
     typedef hb_vector_size_t<elt_t, PAGE_BITS / 8> vector_t;
 
     static const unsigned int ELT_BITS = sizeof (elt_t) * 8;
     static const unsigned int ELT_MASK = ELT_BITS - 1;
     static const unsigned int BITS = sizeof (vector_t) * 8;
     static const unsigned int MASK = BITS - 1;
     static_assert (PAGE_BITS == BITS, "");
@@ -400,16 +399,17 @@ struct hb_set_t
     return true;
   }
 
   inline bool is_subset (const hb_set_t *larger_set) const
   {
     if (get_population () > larger_set->get_population ())
       return false;
 
+    /* TODO Optimize to use pages. */
     hb_codepoint_t c = INVALID;
     while (next (&c))
       if (!larger_set->has (c))
         return false;
 
     return true;
   }
 
--- a/gfx/harfbuzz/src/hb-shape-plan-private.hh
+++ b/gfx/harfbuzz/src/hb-shape-plan-private.hh
@@ -23,17 +23,16 @@
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_SHAPE_PLAN_PRIVATE_HH
 #define HB_SHAPE_PLAN_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-object-private.hh"
 #include "hb-shaper-private.hh"
 
 
 struct hb_shape_plan_t
 {
   hb_object_header_t header;
   ASSERT_POD ();
 
--- a/gfx/harfbuzz/src/hb-shape-plan.cc
+++ b/gfx/harfbuzz/src/hb-shape-plan.cc
@@ -20,17 +20,16 @@
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
 #include "hb-shape-plan-private.hh"
 #include "hb-shaper-private.hh"
 #include "hb-font-private.hh"
 #include "hb-buffer-private.hh"
 
 
 static void
 hb_shape_plan_plan (hb_shape_plan_t    *shape_plan,
--- a/gfx/harfbuzz/src/hb-subset-input.cc
+++ b/gfx/harfbuzz/src/hb-subset-input.cc
@@ -19,17 +19,16 @@
  * 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): Garret Rieger, Rod Sheeter, Behdad Esfahbod
  */
 
-#include "hb-object-private.hh"
 #include "hb-subset-private.hh"
 #include "hb-set-private.hh"
 
 /**
  * hb_subset_input_create_or_fail:
  *
  * Return value: New subset input.
  *
--- a/gfx/harfbuzz/src/hb-subset-plan.hh
+++ b/gfx/harfbuzz/src/hb-subset-plan.hh
@@ -27,17 +27,16 @@
 #ifndef HB_SUBSET_PLAN_HH
 #define HB_SUBSET_PLAN_HH
 
 #include "hb-private.hh"
 
 #include "hb-subset.h"
 #include "hb-subset-private.hh"
 
-#include "hb-object-private.hh"
 #include "hb-map-private.hh"
 
 struct hb_subset_plan_t
 {
   hb_object_header_t header;
   ASSERT_POD ();
 
   hb_bool_t drop_hints;
--- a/gfx/harfbuzz/src/hb-subset.cc
+++ b/gfx/harfbuzz/src/hb-subset.cc
@@ -20,17 +20,16 @@
  * 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): Garret Rieger, Rod Sheeter, Behdad Esfahbod
  */
 
 #include "hb-private.hh"
-#include "hb-object-private.hh"
 #include "hb-open-type-private.hh"
 
 #include "hb-subset-glyf.hh"
 #include "hb-subset-private.hh"
 #include "hb-subset-plan.hh"
 
 #include "hb-open-file-private.hh"
 #include "hb-ot-cmap-table.hh"
@@ -149,17 +148,17 @@ static void
 static hb_blob_t *
 _hb_subset_face_data_reference_blob (hb_subset_face_data_t *data)
 {
 
   unsigned int table_count = data->tables.len;
   unsigned int face_length = table_count * 16 + 12;
 
   for (unsigned int i = 0; i < table_count; i++)
-    face_length += _hb_ceil_to_4 (hb_blob_get_length (data->tables.arrayZ[i].blob));
+    face_length += hb_ceil_to_4 (hb_blob_get_length (data->tables.arrayZ[i].blob));
 
   char *buf = (char *) malloc (face_length);
   if (unlikely (!buf))
     return nullptr;
 
   OT::hb_serialize_context_t c (buf, face_length);
   OT::OpenTypeFontFile *f = c.start_serialize<OT::OpenTypeFontFile> ();
 
--- a/gfx/harfbuzz/src/hb-unicode-private.hh
+++ b/gfx/harfbuzz/src/hb-unicode-private.hh
@@ -27,17 +27,16 @@
  * Codethink Author(s): Ryan Lortie
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_UNICODE_PRIVATE_HH
 #define HB_UNICODE_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-object-private.hh"
 
 
 extern HB_INTERNAL const uint8_t _hb_modified_combining_class[256];
 
 /*
  * hb_unicode_funcs_t
  */
 
--- a/gfx/harfbuzz/src/hb-uniscribe.cc
+++ b/gfx/harfbuzz/src/hb-uniscribe.cc
@@ -20,17 +20,16 @@
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
 #define HB_SHAPER uniscribe
 #include "hb-shaper-impl-private.hh"
 
 #include <windows.h>
 #include <usp10.h>
 #include <rpc.h>
 
 #include "hb-uniscribe.h"
--- a/gfx/harfbuzz/src/hb-version.h
+++ b/gfx/harfbuzz/src/hb-version.h
@@ -33,19 +33,19 @@
 
 #include "hb-common.h"
 
 HB_BEGIN_DECLS
 
 
 #define HB_VERSION_MAJOR 1
 #define HB_VERSION_MINOR 8
-#define HB_VERSION_MICRO 1
+#define HB_VERSION_MICRO 3
 
-#define HB_VERSION_STRING "1.8.1"
+#define HB_VERSION_STRING "1.8.3"
 
 #define HB_VERSION_ATLEAST(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) <= \
 	 HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO)
 
 
 HB_EXTERN void
 hb_version (unsigned int *major,
--- a/gfx/harfbuzz/src/hb-warning.cc
+++ b/gfx/harfbuzz/src/hb-warning.cc
@@ -19,19 +19,17 @@
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-atomic-private.hh"
-#include "hb-mutex-private.hh"
-
+#include "hb-private.hh"
 
 #if defined(HB_ATOMIC_INT_NIL)
 #error "Could not find any system to define atomic_int macros, library WILL NOT be thread-safe"
 #error "Check hb-atomic-private.hh for possible resolutions."
 #endif
 
 #if defined(HB_MUTEX_IMPL_NIL)
 #error "Could not find any system to define mutex macros, library WILL NOT be thread-safe"
--- a/gfx/harfbuzz/src/main.cc
+++ b/gfx/harfbuzz/src/main.cc
@@ -73,16 +73,19 @@ main (int argc, char **argv)
     printf ("TrueType Collection of OpenType fonts\n");
     break;
   case OpenTypeFontFile::TrueTag:
     printf ("Obsolete Apple TrueType font\n");
     break;
   case OpenTypeFontFile::Typ1Tag:
     printf ("Obsolete Apple Type1 font in SFNT container\n");
     break;
+  case OpenTypeFontFile::DFontTag:
+    printf ("DFont Mac Resource Fork\n");
+    break;
   default:
     printf ("Unknown font format\n");
     break;
   }
 
   int num_fonts = ot.get_face_count ();
   printf ("%d font(s) found in file\n", num_fonts);
   for (int n_font = 0; n_font < num_fonts; n_font++) {
new file mode 100755
--- /dev/null
+++ b/gfx/harfbuzz/update.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+# Script to update the mozilla in-tree copy of the HarfBuzz library.
+# Run this within the /gfx/harfbuzz directory of the source tree.
+
+MY_TEMP_DIR=`mktemp -d -t harfbuzz_update.XXXXXX` || exit 1
+
+VERSION=1.8.3
+
+git clone https://github.com/harfbuzz/harfbuzz ${MY_TEMP_DIR}/harfbuzz
+git -C ${MY_TEMP_DIR}/harfbuzz checkout ${VERSION}
+
+COMMIT=$(git -C ${MY_TEMP_DIR}/harfbuzz rev-parse HEAD)
+perl -p -i -e "s/(\d+\.)(\d+\.)(\d+)/${VERSION}/" README-mozilla;
+perl -p -i -e "s/\[commit [0-9a-f]{40}\]/[commit ${COMMIT}]/" README-mozilla;
+
+FILES="AUTHORS autogen.sh configure.ac COPYING git.mk harfbuzz.doap Makefile.am NEWS README src THANKS TODO"
+
+for f in $FILES; do
+	rm -rf $f
+	mv ${MY_TEMP_DIR}/harfbuzz/$f $f
+done
+rm -rf src/hb-ucdn
+rm -rf ${MY_TEMP_DIR}
+
+hg revert -r . src/moz.build
+hg addremove
+
+echo "###"
+echo "### Updated HarfBuzz to $COMMIT."
+echo "### Remember to verify and commit the changes to source control!"
+echo "###"