[subset] WIP convert hdmx subsetting to use iterators.
authorGarret Rieger <grieger@google.com>
Wed, 8 May 2019 00:23:02 +0000 (17:23 -0700)
committerBehdad Esfahbod <behdad@behdad.org>
Wed, 8 May 2019 22:47:24 +0000 (15:47 -0700)
src/hb-ot-hdmx-table.hh
test/api/test-subset-hdmx.c

index abf4440..560fb14 100644 (file)
@@ -41,71 +41,30 @@ namespace OT {
 
 struct DeviceRecord
 {
-  struct SubsetView
-  {
-    const DeviceRecord *source_device_record;
-    unsigned int sizeDeviceRecord;
-    hb_subset_plan_t *subset_plan;
-
-    void init (const DeviceRecord *source_device_record,
-              unsigned int sizeDeviceRecord,
-              hb_subset_plan_t   *subset_plan)
-    {
-      this->source_device_record = source_device_record;
-      this->sizeDeviceRecord = sizeDeviceRecord;
-      this->subset_plan = subset_plan;
-    }
-
-    unsigned int len () const
-    { return this->subset_plan->num_output_glyphs (); }
-
-    const HBUINT8* operator [] (unsigned int new_gid) const
-    {
-      if (unlikely (new_gid >= len ())) return nullptr;
-
-      hb_codepoint_t old_gid;
-      if (!this->subset_plan->old_gid_for_new_gid (new_gid, &old_gid))
-        return &Null(HBUINT8);
-
-      if (old_gid >= sizeDeviceRecord - DeviceRecord::min_size)
-        return nullptr;
-      return &(this->source_device_record->widthsZ[old_gid]);
-    }
-  };
-
-  static unsigned int get_size (unsigned int count)
+  static unsigned int get_size (unsigned count)
   { return hb_ceil_to_4 (min_size + count * HBUINT8::static_size); }
 
-  bool serialize (hb_serialize_context_t *c, const SubsetView &subset_view)
+  template<typename Iterator>
+  bool serialize (hb_serialize_context_t *c, unsigned pixelSize, Iterator it)
   {
     TRACE_SERIALIZE (this);
 
-    unsigned int size = get_size (subset_view.len ());
-    if (unlikely (!c->allocate_size<DeviceRecord> (size)))
-    {
-      DEBUG_MSG(SUBSET, nullptr, "Couldn't allocate enough space for DeviceRecord: %d.",
-                size);
-      return_trace (false);
-    }
-
-    this->pixelSize = subset_view.source_device_record->pixelSize;
-    this->maxWidth = subset_view.source_device_record->maxWidth;
-
-    for (unsigned int i = 0; i < subset_view.len (); i++)
-    {
-      const HBUINT8 *width = subset_view[i];
-      if (!width)
-      {
-       DEBUG_MSG(SUBSET, nullptr, "HDMX width for new gid %d is missing.", i);
-       return_trace (false);
-      }
-      widthsZ[i] = *width;
-    }
+    unsigned length = it.len ();
+
+    if (unlikely (!c->extend (*this, length)))  return_trace (false);
+
+    this->pixelSize = pixelSize;
+    this->maxWidth =
+    + it
+    | hb_reduce (hb_max, 0u);
+
+    + it
+    | hb_sink (widthsZ.as_array (length));
 
     return_trace (true);
   }
 
-  bool sanitize (hb_sanitize_context_t *c, unsigned int sizeDeviceRecord) const
+  bool sanitize (hb_sanitize_context_t *c, unsigned sizeDeviceRecord) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
@@ -135,26 +94,25 @@ struct hdmx
     return StructAtOffset<DeviceRecord> (&this->firstDeviceRecord, i * sizeDeviceRecord);
   }
 
-  bool serialize (hb_serialize_context_t *c, const hdmx *source_hdmx, hb_subset_plan_t *plan)
+  template<typename Iterator>
+  bool serialize (hb_serialize_context_t *c, unsigned version, Iterator it)
   {
     TRACE_SERIALIZE (this);
 
     if (unlikely (!c->extend_min ((*this))))  return_trace (false);
 
-    this->version = source_hdmx->version;
-    this->numRecords = source_hdmx->numRecords;
-    this->sizeDeviceRecord = DeviceRecord::get_size (plan->num_output_glyphs ());
-
-    for (unsigned int i = 0; i < source_hdmx->numRecords; i++)
-    {
-      DeviceRecord::SubsetView subset_view;
-      subset_view.init (&(*source_hdmx)[i], source_hdmx->sizeDeviceRecord, plan);
+    this->version = version;
+    this->numRecords = it.len ();
+    this->sizeDeviceRecord =
+      it ? DeviceRecord::get_size ((*it).second.len ()) : DeviceRecord::get_size (0);
 
-      if (!c->start_embed<DeviceRecord> ()->serialize (c, subset_view))
-       return_trace (false);
-    }
+    using pair_t = decltype (*it);
+    + it
+    | hb_apply ([&] (const pair_t& _) {
+                 c->start_embed<DeviceRecord> ()->serialize (c, _.first, _.second);
+               });
 
-    return_trace (true);
+    return_trace (c->successful);
   }
 
 
@@ -165,10 +123,33 @@ struct hdmx
     hdmx *hdmx_prime = c->serializer->start_embed <hdmx> ();
     if (unlikely (!hdmx_prime)) return_trace (false);
 
-    hdmx_prime->serialize (c->serializer, this, c->plan);
+    auto it =
+    + hb_iota ((unsigned) numRecords)
+    | hb_map ([&] (unsigned _) {
+         const DeviceRecord *device_record =
+           &StructAtOffset<DeviceRecord> (&firstDeviceRecord,
+                                          _ * sizeDeviceRecord);
+         auto row =
+           + hb_iota (c->plan->num_output_glyphs ())
+           | hb_map (c->plan->reverse_glyph_map)
+           | hb_map ([=] (hb_codepoint_t _) {
+                       if (c->plan->is_empty_glyph (_))
+                         return Null(HBUINT8);
+                       return device_record->widthsZ.as_array (get_num_glyphs ()) [_];
+                     })
+           ;
+         return hb_pair ((unsigned) device_record->pixelSize, +row);
+             });
+
+    hdmx_prime->serialize (c->serializer, version, it);
     return_trace (true);
   }
 
+  unsigned get_num_glyphs () const
+  {
+    return sizeDeviceRecord - DeviceRecord::min_size;
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
index 44e579a..7178833 100644 (file)
@@ -92,28 +92,6 @@ test_subset_hdmx_invalid (void)
 }
 
 static void
-test_subset_hdmx_fails_sanitize (void)
-{
-  hb_face_t *face = hb_test_open_font_file ("../fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5609911946838016");
-
-  hb_subset_input_t *input = hb_subset_input_create_or_fail ();
-  hb_set_t *codepoints = hb_subset_input_unicode_set (input);
-  hb_face_t *subset;
-
-  hb_set_add (codepoints, 'a');
-  hb_set_add (codepoints, 'b');
-  hb_set_add (codepoints, 'c');
-
-  subset = hb_subset (face, input);
-  g_assert (subset);
-  g_assert (subset == hb_face_get_empty ());
-
-  hb_subset_input_destroy (input);
-  hb_face_destroy (subset);
-  hb_face_destroy (face);
-}
-
-static void
 test_subset_hdmx_noop (void)
 {
   hb_face_t *face_abc = hb_test_open_font_file ("fonts/Roboto-Regular.abc.ttf");
@@ -140,7 +118,6 @@ main (int argc, char **argv)
   hb_test_add (test_subset_hdmx_simple_subset);
   hb_test_add (test_subset_hdmx_multiple_device_records);
   hb_test_add (test_subset_hdmx_invalid);
-  hb_test_add (test_subset_hdmx_fails_sanitize);
   hb_test_add (test_subset_hdmx_noop);
 
   return hb_test_run();