first push of CFF/CFF2 work

Index, Dict structs
hooked up to hb-subset (takes CFF2, outputs empty CFF2)
diff --git a/src/hb-ot-cff2-table.hh b/src/hb-ot-cff2-table.hh
new file mode 100644
index 0000000..2f49591
--- /dev/null
+++ b/src/hb-ot-cff2-table.hh
@@ -0,0 +1,386 @@
+/*
+ * Copyright © 2018 Adobe Systems Incorporated.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#ifndef HB_OT_CFF2_TABLE_HH
+#define HB_OT_CFF2_TABLE_HH
+
+#include "hb-ot-cff-common-private.hh"
+#include "hb-subset-cff2.hh"
+
+namespace CFF {
+
+/*
+ * CFF2 -- Compact Font Format (CFF) Version 2
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/cff2
+ */
+#define HB_OT_TAG_cff2 HB_TAG('C','F','F','2')
+
+struct CFF2TopDictValues
+{
+  inline void init ()
+  {
+    charStringsOffset.set (0);
+    vstoreOffset.set (0);
+    FDArrayOffset.set (0);
+    FDSelectOffset.set (0);
+    FontMatrix[0] = FontMatrix[3] = 0.001f;
+    FontMatrix[1] = FontMatrix[2] = FontMatrix[4] = FontMatrix[5] = 0.0f;
+  }
+
+  LOffsetTo<CharStrings>    charStringsOffset;
+  LOffsetTo<VariationStore> vstoreOffset;
+  LOffsetTo<FDArray>        FDArrayOffset;
+  LOffsetTo<FDSelect>       FDSelectOffset;
+  float     FontMatrix[6];
+};
+
+struct CFF2TopDictOpSet
+{
+  static inline bool process_op (const ByteStr& str, unsigned int& offset, unsigned char byte, Stack& stack, CFF2TopDictValues& val)
+  {
+    switch (byte) {
+      case OpCode_CharStrings:
+        if (unlikely (!check_pop_offset (stack, val.charStringsOffset)))
+          return false;
+        break;
+      case OpCode_vstore:
+        if (unlikely (!check_pop_offset (stack, val.vstoreOffset)))
+          return false;
+        break;
+      case OpCode_FDArray:
+        if (unlikely (!check_pop_offset (stack, val.FDArrayOffset)))
+          return false;
+        break;
+      case OpCode_FDSelect:
+        if (unlikely (!check_pop_offset (stack, val.FDSelectOffset)))
+          return false;
+        break;
+      case OpCode_FontMatrix:
+        if (unlikely (!stack.check_underflow (6)))
+          return false;
+        for (int i = 0; i < 6; i++)
+          val.FontMatrix[i] = stack.pop ().to_real ();
+        break;
+      case OpCode_longint:  /* 5-byte integer */
+        if (unlikely (!str.check_limit (offset, 5) || !stack.check_overflow (1)))
+          return false;
+        stack.push ((int32_t)((str[offset + 1] << 24) | (str[offset + 2] << 16) | (str[offset + 3] << 8) | str[offset + 4]));
+        offset += 4;
+        break;
+      
+      case OpCode_BCD:  /* real number */
+        float v;
+        if (unlikely (stack.check_overflow (1) || !parse_bcd (str, offset, v)))
+          return false;
+        stack.push (v);
+        break;
+    
+      default:
+        /* XXX: invalid */
+        stack.clear ();
+        return false;
+    }
+    return true;
+  }
+};
+
+struct CFF2FontDictValues
+{
+  inline void init ()
+  {
+    privateDictSize = 0;
+    privateDictOffset.set (0);
+  }
+
+  unsigned int    privateDictSize;
+  OffsetTo<Dict>  privateDictOffset;
+};
+
+struct CFF2FontDictOpSet
+{
+  static inline bool process_op (const ByteStr& str, unsigned int& offset, unsigned char byte, Stack& stack, CFF2FontDictValues& val)
+  {
+    switch (byte) {
+      case OpCode_Private:
+        if (unlikely (!stack.check_pop_uint (val.privateDictSize)))
+          return false;
+        if (unlikely (!check_pop_offset (stack, val.privateDictOffset)))
+          return false;
+        break;
+      case OpCode_longint:  /* 5-byte integer */
+        if (unlikely (!str.check_limit (offset, 5) || !stack.check_overflow (1)))
+          return false;
+        stack.push ((int32_t)((str[offset + 1] << 24) | (str[offset + 2] << 16) || (str[offset + 3] << 8) || str[offset + 4]));
+        break;
+      case OpCode_BCD:  /* real number */
+        float v;
+        if (unlikely (stack.check_overflow (1) || !parse_bcd (str, offset, v)))
+          return false;
+        stack.push (v);
+        break;
+    
+      default:
+        /* XXX: invalid */
+        stack.clear ();
+        return false;
+    }
+    return true;
+  }
+};
+
+struct CFF2PrivateDictValues
+{
+  inline void init ()
+  {
+    languageGroup = 0;
+    expansionFactor = 0.06f;
+    vsIndex = 0;
+    subrsOffset.set (0);
+    blueScale = 0.039625f;
+    blueShift = 7.0f;
+    blueFuzz = 1.0f;
+    stdHW = UNSET_REAL_VALUE;
+    stdVW = UNSET_REAL_VALUE;
+    blueValues.init ();
+    otherBlues.init ();
+    familyBlues.init ();
+    familyOtherBlues.init ();
+    stemSnapH.init ();
+    stemSnapV.init ();
+  }
+
+  inline void fini ()
+  {
+    blueValues.fini ();
+    otherBlues.fini ();
+    familyBlues.fini ();
+    familyOtherBlues.fini ();
+    stemSnapH.fini ();
+    stemSnapV.fini ();
+  }
+
+  int       languageGroup;
+  float     expansionFactor;
+  int       vsIndex;
+  OffsetTo<Subrs>  subrsOffset;
+  float     blueScale;
+  float     blueShift;
+  float     blueFuzz;
+  float     stdHW;
+  float     stdVW;
+  hb_vector_t <float> blueValues;
+  hb_vector_t <float> otherBlues;
+  hb_vector_t <float> familyBlues;
+  hb_vector_t <float> familyOtherBlues;
+  hb_vector_t <float> stemSnapH;
+  hb_vector_t <float> stemSnapV;
+};
+
+struct CFF2PrivateDictOpSet
+{
+  static inline bool process_op (const ByteStr& str, unsigned int& offset, unsigned char byte, Stack& stack, CFF2PrivateDictValues& val)
+  {
+    switch (byte) {
+      case OpCode_BlueValues:
+        if (unlikely (!stack.check_pop_delta (val.blueValues, true)))
+          return false;
+        break;
+      case OpCode_OtherBlues:
+        if (unlikely (!stack.check_pop_delta (val.otherBlues, true)))
+          return false;
+        break;
+      case OpCode_FamilyBlues:
+        if (unlikely (!stack.check_pop_delta (val.familyBlues, true)))
+          return false;
+        break;
+      case OpCode_FamilyOtherBlues:
+        if (unlikely (!stack.check_pop_delta (val.familyOtherBlues, true)))
+          return false;
+        break;
+      case OpCode_StdHW:
+        if (unlikely (!stack.check_pop_real (val.stdHW)))
+          return false;
+        break;
+      case OpCode_StdVW:
+        if (unlikely (!stack.check_pop_real (val.stdVW)))
+          return false;
+        break;
+      case OpCode_BlueScale:
+        if (unlikely (!stack.check_pop_real (val.blueScale)))
+          return false;
+        break;
+      case OpCode_BlueShift:
+        if (unlikely (!stack.check_pop_real (val.blueShift)))
+          return false;
+        break;
+      case OpCode_BlueFuzz:
+        if (unlikely (!stack.check_pop_real (val.blueFuzz)))
+          return false;
+        break;
+      case OpCode_StemSnapH:
+        if (unlikely (!stack.check_pop_delta (val.stemSnapH)))
+          return false;
+        break;
+      case OpCode_StemSnapV:
+        if (unlikely (!stack.check_pop_delta (val.stemSnapV)))
+          return false;
+        break;
+      case OpCode_LanguageGroup:
+        if (unlikely (!stack.check_pop_int (val.languageGroup)))
+          return false;
+        break;
+      case OpCode_ExpansionFactor:
+        if (unlikely (!stack.check_pop_real (val.expansionFactor)))
+          return false;
+        break;
+      case OpCode_Subrs:
+        if (unlikely (!check_pop_offset (stack, val.subrsOffset)))
+          return false;
+        break;
+      case OpCode_blend:
+        // XXX: TODO
+        break;
+
+      default:
+        return false;
+    }
+    return true;
+  }
+};
+
+typedef Interpreter<CFF2TopDictOpSet, CFF2TopDictValues> CFF2TopDict_Interpreter;
+typedef Interpreter<CFF2FontDictOpSet, CFF2FontDictValues> CFF2FontDict_Interpreter;
+typedef Interpreter<CFF2PrivateDictOpSet, CFF2PrivateDictValues> CFF2PrivateDict_Interpreter;
+
+}; /* namespace CFF */
+
+namespace OT {
+
+using namespace CFF;
+
+struct cff2
+{
+  static const hb_tag_t tableTag        = HB_OT_TAG_cff2;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+                  version.sanitize (c) &&
+                  likely (version.major == 2));
+  }
+
+  struct accelerator_t
+  {
+    inline void init (hb_face_t *face)
+    {
+      this->blob = OT::Sanitizer<OT::cff2>().sanitize (face->reference_table (HB_OT_TAG_cff2));
+      const OT::cff2 *cff2 = this->blob->as<OT::cff2> ();
+
+      if (cff2 == &Null(OT::cff2))
+      {
+        hb_blob_destroy (blob);
+        return;
+      }
+
+      { /* parse top dict */
+        ByteStr topDictStr (cff2 + cff2->topDict, cff2->topDictSize);
+        CFF2TopDict_Interpreter top_interp;
+        top_interp.init ();
+        top_interp.interpret (topDictStr, top);
+        top_interp.fini ();
+      }
+      
+      varStore = &OT::StructAtOffset<VariationStore>(cff2, top.vstoreOffset);
+      charStrings = &OT::StructAtOffset<CharStrings>(cff2, top.charStringsOffset);
+      fdArray = &OT::StructAtOffset<FDArray>(cff2, top.FDArrayOffset);
+      fdSelect = &OT::StructAtOffset<FDSelect>(cff2, top.FDSelectOffset);
+      
+      // XXX: sanitize above?
+      if ((charStrings == &Null(CharStrings)) ||
+          (fdArray == &Null(FDArray)))
+      {
+        hb_blob_destroy (blob);
+        return;
+      }
+
+      num_glyphs = charStrings->count;
+    }
+
+    inline void fini (void)
+    {
+      hb_blob_destroy (blob);
+    }
+
+    inline bool get_extents (hb_codepoint_t glyph,
+           hb_glyph_extents_t *extents) const
+    {
+      // XXX: TODO
+      if (glyph >= num_glyphs)
+        return false;
+      
+      return true;
+    }
+
+    private:
+    hb_blob_t               *blob;
+
+    CFF2TopDictValues       top;
+    const VariationStore    *varStore;
+    const CharStrings       *charStrings;
+    const FDArray           *fdArray;
+    const FDSelect          *fdSelect;
+
+    unsigned int            num_glyphs;
+  };
+
+  inline bool subset (hb_subset_plan_t *plan) const
+  {
+    hb_blob_t *cff2_prime = nullptr;
+
+    bool success = true;
+    if (hb_subset_cff2 (plan, &cff2_prime)) {
+      success = success && plan->add_table (HB_OT_TAG_cff2, cff2_prime);
+    } else {
+      success = false;
+    }
+    hb_blob_destroy (cff2_prime);
+
+    return success;
+  }
+
+  protected:
+  FixedVersion<HBUINT8> version;    /* Version of CFF2 table. set to 0x0200u */
+  OffsetTo<Dict, HBUINT8> topDict;  /* headerSize = Offset to Top DICT. */
+  HBUINT16       topDictSize;       /* Top DICT size */
+
+  public:
+  DEFINE_SIZE_STATIC (5);
+};
+
+} /* namespace OT */
+
+#endif /* HB_OT_CFF2_TABLE_HH */