[API] unicode: rework virtual functions for subclassing

Unicode data providers can now be subclassed, including support for
chain-up.  The interface should now be nicely bindable, as well.

Also fix glib unicode funcs that where broken after hb_script_t
changes.  Nicely caught by the test-unicode.c added in this commit.
diff --git a/src/hb-glib.c b/src/hb-glib.c
index 56123f3..1905637 100644
--- a/src/hb-glib.c
+++ b/src/hb-glib.c
@@ -35,22 +35,188 @@
 HB_BEGIN_DECLS
 
 
-static hb_codepoint_t hb_glib_get_mirroring (hb_codepoint_t unicode) { g_unichar_get_mirror_char (unicode, &unicode); return unicode; }
-static hb_unicode_general_category_t hb_glib_get_general_category (hb_codepoint_t unicode) { return g_unichar_type (unicode); }
-static hb_script_t hb_glib_get_script (hb_codepoint_t unicode) { return g_unichar_get_script (unicode); }
-static unsigned int hb_glib_get_combining_class (hb_codepoint_t unicode) { return g_unichar_combining_class (unicode); }
-static unsigned int hb_glib_get_eastasian_width (hb_codepoint_t unicode) { return g_unichar_iswide (unicode); }
+static hb_codepoint_t
+hb_glib_get_mirroring (hb_unicode_funcs_t *ufuncs,
+                       hb_codepoint_t      unicode,
+                       void               *user_data)
+{
+  g_unichar_get_mirror_char (unicode, &unicode);
+  return unicode;
+}
 
+static hb_unicode_general_category_t
+hb_glib_get_general_category (hb_unicode_funcs_t *ufuncs,
+                              hb_codepoint_t      unicode,
+                              void               *user_data)
+
+{
+  return g_unichar_type (unicode);
+}
+
+static hb_script_t
+hb_glib_get_script (hb_unicode_funcs_t *ufuncs,
+                    hb_codepoint_t      unicode,
+                    void               *user_data)
+{
+  GUnicodeScript script = g_unichar_get_script (unicode);
+  switch (script)
+  {
+#define MATCH_SCRIPT(C) case G_UNICODE_SCRIPT_##C: return HB_SCRIPT_##C
+#define MATCH_SCRIPT2(C1, C2) case G_UNICODE_SCRIPT_##C1: return HB_SCRIPT_##C2
+
+  MATCH_SCRIPT2(INVALID_CODE, INVALID);
+
+  MATCH_SCRIPT (COMMON);             /* Zyyy */
+  MATCH_SCRIPT (INHERITED);          /* Qaai */
+  MATCH_SCRIPT (ARABIC);             /* Arab */
+  MATCH_SCRIPT (ARMENIAN);           /* Armn */
+  MATCH_SCRIPT (BENGALI);            /* Beng */
+  MATCH_SCRIPT (BOPOMOFO);           /* Bopo */
+  MATCH_SCRIPT (CHEROKEE);           /* Cher */
+  MATCH_SCRIPT (COPTIC);             /* Qaac */
+  MATCH_SCRIPT (CYRILLIC);           /* Cyrl (Cyrs) */
+  MATCH_SCRIPT (DESERET);            /* Dsrt */
+  MATCH_SCRIPT (DEVANAGARI);         /* Deva */
+  MATCH_SCRIPT (ETHIOPIC);           /* Ethi */
+  MATCH_SCRIPT (GEORGIAN);           /* Geor (Geon); Geoa) */
+  MATCH_SCRIPT (GOTHIC);             /* Goth */
+  MATCH_SCRIPT (GREEK);              /* Grek */
+  MATCH_SCRIPT (GUJARATI);           /* Gujr */
+  MATCH_SCRIPT (GURMUKHI);           /* Guru */
+  MATCH_SCRIPT (HAN);                /* Hani */
+  MATCH_SCRIPT (HANGUL);             /* Hang */
+  MATCH_SCRIPT (HEBREW);             /* Hebr */
+  MATCH_SCRIPT (HIRAGANA);           /* Hira */
+  MATCH_SCRIPT (KANNADA);            /* Knda */
+  MATCH_SCRIPT (KATAKANA);           /* Kana */
+  MATCH_SCRIPT (KHMER);              /* Khmr */
+  MATCH_SCRIPT (LAO);                /* Laoo */
+  MATCH_SCRIPT (LATIN);              /* Latn (Latf); Latg) */
+  MATCH_SCRIPT (MALAYALAM);          /* Mlym */
+  MATCH_SCRIPT (MONGOLIAN);          /* Mong */
+  MATCH_SCRIPT (MYANMAR);            /* Mymr */
+  MATCH_SCRIPT (OGHAM);              /* Ogam */
+  MATCH_SCRIPT (OLD_ITALIC);         /* Ital */
+  MATCH_SCRIPT (ORIYA);              /* Orya */
+  MATCH_SCRIPT (RUNIC);              /* Runr */
+  MATCH_SCRIPT (SINHALA);            /* Sinh */
+  MATCH_SCRIPT (SYRIAC);             /* Syrc (Syrj, Syrn); Syre) */
+  MATCH_SCRIPT (TAMIL);              /* Taml */
+  MATCH_SCRIPT (TELUGU);             /* Telu */
+  MATCH_SCRIPT (THAANA);             /* Thaa */
+  MATCH_SCRIPT (THAI);               /* Thai */
+  MATCH_SCRIPT (TIBETAN);            /* Tibt */
+  MATCH_SCRIPT (CANADIAN_ABORIGINAL);/* Cans */
+  MATCH_SCRIPT (YI);                 /* Yiii */
+  MATCH_SCRIPT (TAGALOG);            /* Tglg */
+  MATCH_SCRIPT (HANUNOO);            /* Hano */
+  MATCH_SCRIPT (BUHID);              /* Buhd */
+  MATCH_SCRIPT (TAGBANWA);           /* Tagb */
+
+  /* Unicode-4.0 additions */
+  MATCH_SCRIPT (BRAILLE);            /* Brai */
+  MATCH_SCRIPT (CYPRIOT);            /* Cprt */
+  MATCH_SCRIPT (LIMBU);              /* Limb */
+  MATCH_SCRIPT (OSMANYA);            /* Osma */
+  MATCH_SCRIPT (SHAVIAN);            /* Shaw */
+  MATCH_SCRIPT (LINEAR_B);           /* Linb */
+  MATCH_SCRIPT (TAI_LE);             /* Tale */
+  MATCH_SCRIPT (UGARITIC);           /* Ugar */
+
+  /* Unicode-4.1 additions */
+  MATCH_SCRIPT (NEW_TAI_LUE);        /* Talu */
+  MATCH_SCRIPT (BUGINESE);           /* Bugi */
+  MATCH_SCRIPT (GLAGOLITIC);         /* Glag */
+  MATCH_SCRIPT (TIFINAGH);           /* Tfng */
+  MATCH_SCRIPT (SYLOTI_NAGRI);       /* Sylo */
+  MATCH_SCRIPT (OLD_PERSIAN);        /* Xpeo */
+  MATCH_SCRIPT (KHAROSHTHI);         /* Khar */
+
+  /* Unicode-5.0 additions */
+  MATCH_SCRIPT (UNKNOWN);            /* Zzzz */
+  MATCH_SCRIPT (BALINESE);           /* Bali */
+  MATCH_SCRIPT (CUNEIFORM);          /* Xsux */
+  MATCH_SCRIPT (PHOENICIAN);         /* Phnx */
+  MATCH_SCRIPT (PHAGS_PA);           /* Phag */
+  MATCH_SCRIPT (NKO);                /* Nkoo */
+
+  /* Unicode-5.1 additions */
+  MATCH_SCRIPT (KAYAH_LI);           /* Kali */
+  MATCH_SCRIPT (LEPCHA);             /* Lepc */
+  MATCH_SCRIPT (REJANG);             /* Rjng */
+  MATCH_SCRIPT (SUNDANESE);          /* Sund */
+  MATCH_SCRIPT (SAURASHTRA);         /* Saur */
+  MATCH_SCRIPT (CHAM);               /* Cham */
+  MATCH_SCRIPT (OL_CHIKI);           /* Olck */
+  MATCH_SCRIPT (VAI);                /* Vaii */
+  MATCH_SCRIPT (CARIAN);             /* Cari */
+  MATCH_SCRIPT (LYCIAN);             /* Lyci */
+  MATCH_SCRIPT (LYDIAN);             /* Lydi */
+
+  /* Unicode-5.2 additions */
+#if GLIB_CHECK_VERSION(2,26,0)
+  MATCH_SCRIPT (AVESTAN);                /* Avst */
+  MATCH_SCRIPT (BAMUM);                  /* Bamu */
+  MATCH_SCRIPT (EGYPTIAN_HIEROGLYPHS);   /* Egyp */
+  MATCH_SCRIPT (IMPERIAL_ARAMAIC);       /* Armi */
+  MATCH_SCRIPT (INSCRIPTIONAL_PAHLAVI);  /* Phli */
+  MATCH_SCRIPT (INSCRIPTIONAL_PARTHIAN); /* Prti */
+  MATCH_SCRIPT (JAVANESE);               /* Java */
+  MATCH_SCRIPT (KAITHI);                 /* Kthi */
+  MATCH_SCRIPT (TAI_THAM);               /* Lana */
+  MATCH_SCRIPT (LISU);                   /* Lisu */
+  MATCH_SCRIPT (MEETEI_MAYEK);           /* Mtei */
+  MATCH_SCRIPT (OLD_SOUTH_ARABIAN);      /* Sarb */
+#if GLIB_CHECK_VERSION(2,28,0)
+  MATCH_SCRIPT (OLD_TURKIC);             /* Orkh */
+#else
+  MATCH_SCRIPT2(OLD_TURKISH, OLD_TURKIC);/* Orkh */
+#endif
+  MATCH_SCRIPT (SAMARITAN);              /* Samr */
+  MATCH_SCRIPT (TAI_VIET);               /* Tavt */
+#endif
+
+  /* Unicode-6.0 additions */
+#if GLIB_CHECK_VERSION(2,28,0)
+  MATCH_SCRIPT (BATAK);                  /* Batk */
+  MATCH_SCRIPT (BRAHMI);                 /* Brah */
+  MATCH_SCRIPT (MANDAIC);                /* Mand */
+#endif
+
+#undef MATCH_SCRIPT
+#undef MATCH_SCRIPT2
+  }
+
+  return HB_SCRIPT_UNKNOWN;
+}
+
+static unsigned int
+hb_glib_get_combining_class (hb_unicode_funcs_t *ufuncs,
+                             hb_codepoint_t      unicode,
+                             void               *user_data)
+
+{
+  return g_unichar_combining_class (unicode);
+}
+
+static unsigned int
+hb_glib_get_eastasian_width (hb_unicode_funcs_t *ufuncs,
+                             hb_codepoint_t      unicode,
+                             void               *user_data)
+{
+  return g_unichar_iswide (unicode) ? 2 : 1;
+}
 
 static hb_unicode_funcs_t glib_ufuncs = {
   HB_REFERENCE_COUNT_INVALID, /* ref_count */
+  NULL,
   TRUE, /* immutable */
   {
-    hb_glib_get_general_category,
-    hb_glib_get_combining_class,
-    hb_glib_get_mirroring,
-    hb_glib_get_script,
-    hb_glib_get_eastasian_width
+    hb_glib_get_general_category, NULL, NULL,
+    hb_glib_get_combining_class, NULL, NULL,
+    hb_glib_get_mirroring, NULL, NULL,
+    hb_glib_get_script, NULL, NULL,
+    hb_glib_get_eastasian_width, NULL, NULL
   }
 };
 
diff --git a/src/hb-icu.c b/src/hb-icu.c
index 0ebc9c5..6bc3339 100644
--- a/src/hb-icu.c
+++ b/src/hb-icu.c
@@ -38,11 +38,27 @@
 HB_BEGIN_DECLS
 
 
-static hb_codepoint_t hb_icu_get_mirroring (hb_codepoint_t unicode) { return u_charMirror(unicode); }
-static unsigned int hb_icu_get_combining_class (hb_codepoint_t unicode) { return u_getCombiningClass (unicode); }
+static hb_codepoint_t
+hb_icu_get_mirroring (hb_unicode_funcs_t *ufuncs,
+		      hb_codepoint_t      unicode,
+		      void               *user_data)
+{
+  return u_charMirror(unicode);
+}
 
 static unsigned int
-hb_icu_get_eastasian_width (hb_codepoint_t unicode)
+hb_icu_get_combining_class (hb_unicode_funcs_t *ufuncs,
+			    hb_codepoint_t      unicode,
+			    void               *user_data)
+
+{
+  return u_getCombiningClass (unicode);
+}
+
+static unsigned int
+hb_icu_get_eastasian_width (hb_unicode_funcs_t *ufuncs,
+			    hb_codepoint_t      unicode,
+			    void               *user_data)
 {
   switch (u_getIntPropertyValue(unicode, UCHAR_EAST_ASIAN_WIDTH))
   {
@@ -59,7 +75,9 @@
 }
 
 static hb_unicode_general_category_t
-hb_icu_get_general_category (hb_codepoint_t unicode)
+hb_icu_get_general_category (hb_unicode_funcs_t *ufuncs,
+			     hb_codepoint_t      unicode,
+			     void               *user_data)
 {
   switch (u_getIntPropertyValue(unicode, UCHAR_GENERAL_CATEGORY))
   {
@@ -108,7 +126,9 @@
 }
 
 static hb_script_t
-hb_icu_get_script (hb_codepoint_t unicode)
+hb_icu_get_script (hb_unicode_funcs_t *ufuncs,
+		   hb_codepoint_t      unicode,
+		   void               *user_data)
 {
   UErrorCode status = U_ZERO_ERROR;
   UScriptCode scriptCode = uscript_getScript(unicode, &status);
@@ -234,19 +254,24 @@
   MATCH_SCRIPT (BRAHMI);                 /* Brah */
   MATCH_SCRIPT2(MANDAEAN, MANDAIC);      /* Mand */
 
+#undef CHECK_ICU_VERSION
+#undef MATCH_SCRIPT
+#undef MATCH_SCRIPT2
   }
+
   return HB_SCRIPT_UNKNOWN;
 }
 
 static hb_unicode_funcs_t icu_ufuncs = {
   HB_REFERENCE_COUNT_INVALID, /* ref_count */
+  NULL,
   TRUE, /* immutable */
   {
-    hb_icu_get_general_category,
-    hb_icu_get_combining_class,
-    hb_icu_get_mirroring,
-    hb_icu_get_script,
-    hb_icu_get_eastasian_width
+    hb_icu_get_general_category, NULL, NULL,
+    hb_icu_get_combining_class, NULL, NULL,
+    hb_icu_get_mirroring, NULL, NULL,
+    hb_icu_get_script, NULL, NULL,
+    hb_icu_get_eastasian_width, NULL, NULL
   }
 };
 
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 2364d3c..2ffb076 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -149,8 +149,10 @@
 
   unsigned int count = c->buffer->len;
   for (unsigned int i = 1; i < count; i++) {
-    info[i].general_category() = get_general_category (info[i].codepoint);
-    info[i].combining_class() = get_combining_class (info[i].codepoint);
+    info[i].general_category() = get_general_category (c->buffer->unicode, info[i].codepoint,
+                                                       c->buffer->unicode->v.get_general_category_data);
+    info[i].combining_class() = get_combining_class (c->buffer->unicode, info[i].codepoint,
+                                                     c->buffer->unicode->v.get_combining_class_data);
   }
 }
 
@@ -200,7 +202,8 @@
 
   unsigned int count = c->buffer->len;
   for (unsigned int i = 0; i < count; i++) {
-    hb_codepoint_t codepoint = get_mirroring (c->buffer->info[i].codepoint);
+    hb_codepoint_t codepoint = get_mirroring (c->buffer->unicode, c->buffer->info[i].codepoint,
+                                              c->buffer->unicode->v.get_mirroring_data);
     if (likely (codepoint == c->buffer->info[i].codepoint))
       c->buffer->info[i].mask |= rtlm_mask; /* XXX this should be moved to before setting user-feature masks */
     else
diff --git a/src/hb-shape.cc b/src/hb-shape.cc
index 368abfc..9d9bf25 100644
--- a/src/hb-shape.cc
+++ b/src/hb-shape.cc
@@ -77,7 +77,7 @@
     hb_unicode_get_script_func_t get_script = buffer->unicode->v.get_script;
     unsigned int count = buffer->len;
     for (unsigned int i = 0; i < count; i++) {
-      hb_script_t script = get_script (buffer->info[i].codepoint);
+      hb_script_t script = get_script (buffer->unicode, buffer->info[i].codepoint, buffer->unicode->v.get_script_data);
       if (likely (script != HB_SCRIPT_COMMON &&
 		  script != HB_SCRIPT_INHERITED &&
 		  script != HB_SCRIPT_UNKNOWN)) {
diff --git a/src/hb-unicode-private.h b/src/hb-unicode-private.h
index 52d1b9b..456d8de 100644
--- a/src/hb-unicode-private.h
+++ b/src/hb-unicode-private.h
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2009  Red Hat, Inc.
+ * Copyright © 2011 Codethink Limited
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -22,6 +23,7 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Codethink Author(s): Ryan Lortie
  */
 
 #ifndef HB_UNICODE_PRIVATE_H
@@ -40,15 +42,30 @@
 
 struct _hb_unicode_funcs_t {
   hb_reference_count_t ref_count;
+  hb_unicode_funcs_t *parent;
 
   hb_bool_t immutable;
 
   struct {
     hb_unicode_get_general_category_func_t	get_general_category;
+    void                                       *get_general_category_data;
+    hb_destroy_func_t                           get_general_category_destroy;
+
     hb_unicode_get_combining_class_func_t	get_combining_class;
+    void                                       *get_combining_class_data;
+    hb_destroy_func_t                           get_combining_class_destroy;
+
     hb_unicode_get_mirroring_func_t		get_mirroring;
+    void                                       *get_mirroring_data;
+    hb_destroy_func_t                           get_mirroring_destroy;
+
     hb_unicode_get_script_func_t		get_script;
+    void                                       *get_script_data;
+    hb_destroy_func_t                           get_script_destroy;
+
     hb_unicode_get_eastasian_width_func_t	get_eastasian_width;
+    void                                       *get_eastasian_width_data;
+    hb_destroy_func_t                           get_eastasian_width_destroy;
   } v;
 };
 
diff --git a/src/hb-unicode.c b/src/hb-unicode.c
index 9bb3524..e047ef7 100644
--- a/src/hb-unicode.c
+++ b/src/hb-unicode.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2009  Red Hat, Inc.
+ * Copyright © 2011 Codethink Limited
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -22,6 +23,7 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Codethink Author(s): Ryan Lortie
  */
 
 #include "hb-private.h"
@@ -35,33 +37,86 @@
  * hb_unicode_funcs_t
  */
 
-static hb_codepoint_t hb_unicode_get_mirroring_nil (hb_codepoint_t unicode) { return unicode; }
-static hb_unicode_general_category_t hb_unicode_get_general_category_nil (hb_codepoint_t unicode HB_UNUSED) { return HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER; }
-static hb_script_t hb_unicode_get_script_nil (hb_codepoint_t unicode HB_UNUSED) { return HB_SCRIPT_UNKNOWN; }
-static unsigned int hb_unicode_get_combining_class_nil (hb_codepoint_t unicode HB_UNUSED) { return 0; }
-static unsigned int hb_unicode_get_eastasian_width_nil (hb_codepoint_t unicode HB_UNUSED) { return 1; }
+static hb_codepoint_t
+hb_unicode_get_mirroring_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
+                              hb_codepoint_t      unicode   HB_UNUSED,
+                              void               *user_data HB_UNUSED)
+{
+  return unicode;
+}
+
+static hb_unicode_general_category_t
+hb_unicode_get_general_category_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
+                                     hb_codepoint_t      unicode   HB_UNUSED,
+                                     void               *user_data HB_UNUSED)
+{
+  return HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER;
+}
+
+static hb_script_t
+hb_unicode_get_script_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
+                           hb_codepoint_t      unicode   HB_UNUSED,
+                           void               *user_data HB_UNUSED)
+{
+  return HB_SCRIPT_UNKNOWN;
+}
+
+static unsigned int
+hb_unicode_get_combining_class_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
+                                    hb_codepoint_t      unicode   HB_UNUSED,
+                                    void               *user_data HB_UNUSED)
+{
+  return 0;
+}
+
+static unsigned int
+hb_unicode_get_eastasian_width_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
+                                    hb_codepoint_t      unicode   HB_UNUSED,
+                                    void               *user_data HB_UNUSED)
+{
+  return 1;
+}
 
 hb_unicode_funcs_t _hb_unicode_funcs_nil = {
   HB_REFERENCE_COUNT_INVALID, /* ref_count */
+  NULL, /* parent */
   TRUE, /* immutable */
   {
-    hb_unicode_get_general_category_nil,
-    hb_unicode_get_combining_class_nil,
-    hb_unicode_get_mirroring_nil,
-    hb_unicode_get_script_nil,
-    hb_unicode_get_eastasian_width_nil
+    hb_unicode_get_general_category_nil, NULL, NULL,
+    hb_unicode_get_combining_class_nil, NULL, NULL,
+    hb_unicode_get_mirroring_nil, NULL, NULL,
+    hb_unicode_get_script_nil, NULL, NULL,
+    hb_unicode_get_eastasian_width_nil, NULL, NULL
   }
 };
 
 hb_unicode_funcs_t *
-hb_unicode_funcs_create (void)
+hb_unicode_funcs_create (hb_unicode_funcs_t *parent)
 {
   hb_unicode_funcs_t *ufuncs;
 
   if (!HB_OBJECT_DO_CREATE (hb_unicode_funcs_t, ufuncs))
     return &_hb_unicode_funcs_nil;
 
-  ufuncs->v = _hb_unicode_funcs_nil.v;
+  if (parent != NULL) {
+    ufuncs->parent = hb_unicode_funcs_reference (parent);
+    hb_unicode_funcs_make_immutable (parent);
+    ufuncs->v = parent->v;
+
+    /* Clear out the destroy notifies from our parent.
+     *
+     * We don't want to destroy the user_data twice and since we hold a
+     * reference on our parent then we know that the user_data will
+     * survive for at least as long as we do anyway.
+     */
+    ufuncs->v.get_general_category_destroy = NULL;
+    ufuncs->v.get_combining_class_destroy = NULL;
+    ufuncs->v.get_mirroring_destroy = NULL;
+    ufuncs->v.get_script_destroy = NULL;
+    ufuncs->v.get_eastasian_width_destroy = NULL;
+  } else {
+    ufuncs->v = _hb_unicode_funcs_nil.v;
+  }
 
   return ufuncs;
 }
@@ -83,20 +138,31 @@
 {
   HB_OBJECT_DO_DESTROY (ufuncs);
 
+  if (ufuncs->parent != NULL)
+    hb_unicode_funcs_destroy (ufuncs->parent);
+
+  if (ufuncs->v.get_general_category_destroy != NULL)
+    ufuncs->v.get_general_category_destroy (ufuncs->v.get_general_category_data);
+
+  if (ufuncs->v.get_combining_class_destroy != NULL)
+    ufuncs->v.get_combining_class_destroy (ufuncs->v.get_combining_class_data);
+
+  if (ufuncs->v.get_mirroring_destroy != NULL)
+    ufuncs->v.get_mirroring_destroy (ufuncs->v.get_mirroring_data);
+
+  if (ufuncs->v.get_script_destroy != NULL)
+    ufuncs->v.get_script_destroy (ufuncs->v.get_script_data);
+
+  if (ufuncs->v.get_eastasian_width_destroy != NULL)
+    ufuncs->v.get_eastasian_width_destroy (ufuncs->v.get_eastasian_width_data);
+
   free (ufuncs);
 }
 
 hb_unicode_funcs_t *
-hb_unicode_funcs_copy (hb_unicode_funcs_t *other_ufuncs)
+hb_unicode_funcs_get_parent (hb_unicode_funcs_t *ufuncs)
 {
-  hb_unicode_funcs_t *ufuncs;
-
-  if (!HB_OBJECT_DO_CREATE (hb_unicode_funcs_t, ufuncs))
-    return &_hb_unicode_funcs_nil;
-
-  ufuncs->v = other_ufuncs->v;
-
-  return ufuncs;
+  return ufuncs->parent;
 }
 
 void
@@ -115,122 +181,75 @@
 }
 
 
-void
-hb_unicode_funcs_set_mirroring_func (hb_unicode_funcs_t *ufuncs,
-				     hb_unicode_get_mirroring_func_t mirroring_func)
-{
-  if (ufuncs->immutable)
-    return;
-
-  ufuncs->v.get_mirroring = mirroring_func ? mirroring_func : hb_unicode_get_mirroring_nil;
+#define SETTER(name) \
+void                                                                           \
+hb_unicode_funcs_set_##name##_func (hb_unicode_funcs_t             *ufuncs,    \
+                                    hb_unicode_get_##name##_func_t  func,      \
+                                    void                           *user_data, \
+                                    hb_destroy_func_t               destroy)   \
+{                                                                              \
+  if (ufuncs->immutable)                                                       \
+    return;                                                                    \
+                                                                               \
+  if (func != NULL) {                                                          \
+    ufuncs->v.get_##name = func;                                               \
+    ufuncs->v.get_##name##_data = user_data;                                   \
+    ufuncs->v.get_##name##_destroy = destroy;                                  \
+  } else if (ufuncs->parent != NULL) {                                         \
+    ufuncs->v.get_##name = ufuncs->parent->v.get_##name;                       \
+    ufuncs->v.get_##name##_data = ufuncs->parent->v.get_##name##_data;;        \
+    ufuncs->v.get_##name##_destroy = NULL;                                     \
+  } else {                                                                     \
+    ufuncs->v.get_##name = hb_unicode_get_##name##_nil;                        \
+    ufuncs->v.get_##name##_data = NULL;                                        \
+    ufuncs->v.get_##name##_destroy = NULL;                                     \
+  }                                                                            \
 }
 
-void
-hb_unicode_funcs_set_general_category_func (hb_unicode_funcs_t *ufuncs,
-					    hb_unicode_get_general_category_func_t general_category_func)
-{
-  if (ufuncs->immutable)
-    return;
-
-  ufuncs->v.get_general_category = general_category_func ? general_category_func : hb_unicode_get_general_category_nil;
-}
-
-void
-hb_unicode_funcs_set_script_func (hb_unicode_funcs_t *ufuncs,
-				  hb_unicode_get_script_func_t script_func)
-{
-  if (ufuncs->immutable)
-    return;
-
-  ufuncs->v.get_script = script_func ? script_func : hb_unicode_get_script_nil;
-}
-
-void
-hb_unicode_funcs_set_combining_class_func (hb_unicode_funcs_t *ufuncs,
-					   hb_unicode_get_combining_class_func_t combining_class_func)
-{
-  if (ufuncs->immutable)
-    return;
-
-  ufuncs->v.get_combining_class = combining_class_func ? combining_class_func : hb_unicode_get_combining_class_nil;
-}
-
-void
-hb_unicode_funcs_set_eastasian_width_func (hb_unicode_funcs_t *ufuncs,
-					   hb_unicode_get_eastasian_width_func_t eastasian_width_func)
-{
-  if (ufuncs->immutable)
-    return;
-
-  ufuncs->v.get_eastasian_width = eastasian_width_func ? eastasian_width_func : hb_unicode_get_eastasian_width_nil;
-}
-
-
-hb_unicode_get_mirroring_func_t
-hb_unicode_funcs_get_mirroring_func (hb_unicode_funcs_t *ufuncs)
-{
-  return ufuncs->v.get_mirroring;
-}
-
-hb_unicode_get_general_category_func_t
-hb_unicode_funcs_get_general_category_func (hb_unicode_funcs_t *ufuncs)
-{
-  return ufuncs->v.get_general_category;
-}
-
-hb_unicode_get_script_func_t
-hb_unicode_funcs_get_script_func (hb_unicode_funcs_t *ufuncs)
-{
-  return ufuncs->v.get_script;
-}
-
-hb_unicode_get_combining_class_func_t
-hb_unicode_funcs_get_combining_class_func (hb_unicode_funcs_t *ufuncs)
-{
-  return ufuncs->v.get_combining_class;
-}
-
-hb_unicode_get_eastasian_width_func_t
-hb_unicode_funcs_get_eastasian_width_func (hb_unicode_funcs_t *ufuncs)
-{
-  return ufuncs->v.get_eastasian_width;
-}
-
-
+SETTER(mirroring)
+SETTER(general_category)
+SETTER(script)
+SETTER(combining_class)
+SETTER(eastasian_width)
 
 hb_codepoint_t
 hb_unicode_get_mirroring (hb_unicode_funcs_t *ufuncs,
 			  hb_codepoint_t unicode)
 {
-  return ufuncs->v.get_mirroring (unicode);
+  return ufuncs->v.get_mirroring (ufuncs, unicode,
+				  ufuncs->v.get_mirroring_data);
 }
 
 hb_unicode_general_category_t
 hb_unicode_get_general_category (hb_unicode_funcs_t *ufuncs,
 				 hb_codepoint_t unicode)
 {
-  return ufuncs->v.get_general_category (unicode);
+  return ufuncs->v.get_general_category (ufuncs, unicode,
+					 ufuncs->v.get_general_category_data);
 }
 
 hb_script_t
 hb_unicode_get_script (hb_unicode_funcs_t *ufuncs,
 		       hb_codepoint_t unicode)
 {
-  return ufuncs->v.get_script (unicode);
+  return ufuncs->v.get_script (ufuncs, unicode,
+			       ufuncs->v.get_script_data);
 }
 
 unsigned int
 hb_unicode_get_combining_class (hb_unicode_funcs_t *ufuncs,
 				hb_codepoint_t unicode)
 {
-  return ufuncs->v.get_combining_class (unicode);
+  return ufuncs->v.get_combining_class (ufuncs, unicode,
+					ufuncs->v.get_combining_class_data);
 }
 
 unsigned int
 hb_unicode_get_eastasian_width (hb_unicode_funcs_t *ufuncs,
 				hb_codepoint_t unicode)
 {
-  return ufuncs->v.get_eastasian_width (unicode);
+  return ufuncs->v.get_eastasian_width (ufuncs, unicode,
+					ufuncs->v.get_eastasian_width_data);
 }
 
 
diff --git a/src/hb-unicode.h b/src/hb-unicode.h
index fb8fe97..792ad54 100644
--- a/src/hb-unicode.h
+++ b/src/hb-unicode.h
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2009  Red Hat, Inc.
+ * Copyright © 2011 Codethink Limited
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -22,6 +23,7 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Codethink Author(s): Ryan Lortie
  */
 
 #ifndef HB_UNICODE_H
@@ -39,7 +41,7 @@
 typedef struct _hb_unicode_funcs_t hb_unicode_funcs_t;
 
 hb_unicode_funcs_t *
-hb_unicode_funcs_create (void);
+hb_unicode_funcs_create (hb_unicode_funcs_t *parent_funcs);
 
 hb_unicode_funcs_t *
 hb_unicode_funcs_reference (hb_unicode_funcs_t *ufuncs);
@@ -51,7 +53,7 @@
 hb_unicode_funcs_destroy (hb_unicode_funcs_t *ufuncs);
 
 hb_unicode_funcs_t *
-hb_unicode_funcs_copy (hb_unicode_funcs_t *ufuncs);
+hb_unicode_funcs_get_parent (hb_unicode_funcs_t *ufuncs);
 
 void
 hb_unicode_funcs_make_immutable (hb_unicode_funcs_t *ufuncs);
@@ -63,57 +65,55 @@
  * funcs
  */
 
-
 /* typedefs */
 
-typedef hb_codepoint_t (*hb_unicode_get_mirroring_func_t) (hb_codepoint_t unicode);
-typedef hb_unicode_general_category_t (*hb_unicode_get_general_category_func_t) (hb_codepoint_t unicode);
-typedef hb_script_t (*hb_unicode_get_script_func_t) (hb_codepoint_t unicode);
-typedef unsigned int (*hb_unicode_get_combining_class_func_t) (hb_codepoint_t unicode);
-typedef unsigned int (*hb_unicode_get_eastasian_width_func_t) (hb_codepoint_t unicode);
-
+typedef hb_codepoint_t                (*hb_unicode_get_mirroring_func_t)        (hb_unicode_funcs_t *ufuncs,
+                                                                                 hb_codepoint_t      unicode,
+                                                                                 void               *user_data);
+typedef hb_unicode_general_category_t (*hb_unicode_get_general_category_func_t) (hb_unicode_funcs_t *ufuncs,
+                                                                                 hb_codepoint_t      unicode,
+                                                                                 void               *user_data);
+typedef hb_script_t                   (*hb_unicode_get_script_func_t)           (hb_unicode_funcs_t *ufuncs,
+                                                                                 hb_codepoint_t      unicode,
+                                                                                 void               *user_data);
+typedef unsigned int                  (*hb_unicode_get_combining_class_func_t)  (hb_unicode_funcs_t *ufuncs,
+                                                                                 hb_codepoint_t      unicode,
+                                                                                 void               *user_data);
+typedef unsigned int                  (*hb_unicode_get_eastasian_width_func_t)  (hb_unicode_funcs_t *ufuncs,
+                                                                                 hb_codepoint_t      unicode,
+                                                                                 void               *user_data);
 
 /* setters */
 
 void
 hb_unicode_funcs_set_mirroring_func (hb_unicode_funcs_t *ufuncs,
-				     hb_unicode_get_mirroring_func_t mirroring_func);
+				     hb_unicode_get_mirroring_func_t mirroring_func,
+                                     void *user_data,
+                                     hb_destroy_func_t destroy);
 
 void
 hb_unicode_funcs_set_general_category_func (hb_unicode_funcs_t *ufuncs,
-					    hb_unicode_get_general_category_func_t general_category_func);
+					    hb_unicode_get_general_category_func_t general_category_func,
+                                            void *user_data,
+                                            hb_destroy_func_t destroy);
 
 void
 hb_unicode_funcs_set_script_func (hb_unicode_funcs_t *ufuncs,
-				  hb_unicode_get_script_func_t script_func);
+				  hb_unicode_get_script_func_t script_func,
+                                  void *user_data,
+                                  hb_destroy_func_t destroy);
 
 void
 hb_unicode_funcs_set_combining_class_func (hb_unicode_funcs_t *ufuncs,
-					   hb_unicode_get_combining_class_func_t combining_class_func);
+					   hb_unicode_get_combining_class_func_t combining_class_func,
+                                           void *user_data,
+                                           hb_destroy_func_t destroy);
 
 void
 hb_unicode_funcs_set_eastasian_width_func (hb_unicode_funcs_t *ufuncs,
-					   hb_unicode_get_eastasian_width_func_t eastasian_width_func);
-
-
-/* getters */
-
-/* These never return NULL.  Return fallback defaults instead. */
-
-hb_unicode_get_mirroring_func_t
-hb_unicode_funcs_get_mirroring_func (hb_unicode_funcs_t *ufuncs);
-
-hb_unicode_get_general_category_func_t
-hb_unicode_funcs_get_general_category_func (hb_unicode_funcs_t *ufuncs);
-
-hb_unicode_get_script_func_t
-hb_unicode_funcs_get_script_func (hb_unicode_funcs_t *ufuncs);
-
-hb_unicode_get_combining_class_func_t
-hb_unicode_funcs_get_combining_class_func (hb_unicode_funcs_t *ufuncs);
-
-hb_unicode_get_eastasian_width_func_t
-hb_unicode_funcs_get_eastasian_width_func (hb_unicode_funcs_t *ufuncs);
+					   hb_unicode_get_eastasian_width_func_t eastasian_width_func,
+                                           void *user_data,
+                                           hb_destroy_func_t destroy);
 
 
 /* accessors */