Re: [HarfBuzz] Linking to libstdc++ in HarfBuzz

2018-08-02 Thread Richard Wordingham
On Thu, 2 Aug 2018 02:38:00 -0700
Behdad Esfahbod  wrote:

> Just asking, does anybody still object about using C++ standard
> library (ie. linking to it) in HarfBuzz?

My personal experience makes me tremble at the thought.  When I
'upgraded' from Ubuntu 12.04 to 14.04, I was without a libstdc++
library, and it took me a long time to clean up the resulting mess.  The
cause of the problem was that the version of C++ compiler I had been
using was too far in advance of the distributions, and as I upgraded it
was removed and I was left with nothing in its place.  The
installation scripts written in Python largely or entirely failed.  The
prospect of being without a text layout engine is terrifying.

C++ was so far out of sync with the general distribution because I used
to compile LibreOffice, and a key header file there wouldn't compile
with the older versions of gcc.

Richard.
___
HarfBuzz mailing list
HarfBuzz@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/harfbuzz


[HarfBuzz] Linking to libstdc++ in HarfBuzz

2018-08-02 Thread Behdad Esfahbod
Hi,

Just asking, does anybody still object about using C++ standard library
(ie. linking to it) in HarfBuzz?

I know I've been one of the bigger opponents myself.  But I can change too.
:)

-- 
behdad
http://behdad.org/
___
HarfBuzz mailing list
HarfBuzz@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/harfbuzz


[HarfBuzz] harfbuzz: Branch 'master' - 6 commits

2018-08-02 Thread Behdad Esfahbod
 src/hb-face.cc  |1 -
 src/hb-graphite2.cc |2 +-
 src/hb-machinery-private.hh |8 +++-
 src/hb-ot-layout-private.hh |   15 +++
 src/hb-uniscribe.cc |8 
 5 files changed, 19 insertions(+), 15 deletions(-)

New commits:
commit d4d1bf8177b127caa57b146c932f553dca1ad933
Author: Behdad Esfahbod 
Date:   Thu Aug 2 02:04:02 2018 -0700

Fix for recent rename

diff --git a/src/hb-graphite2.cc b/src/hb-graphite2.cc
index da7944b8..2ba905d6 100644
--- a/src/hb-graphite2.cc
+++ b/src/hb-graphite2.cc
@@ -106,7 +106,7 @@ _hb_graphite2_shaper_face_data_create (hb_face_t *face)
   }
   hb_blob_destroy (silf_blob);
 
-  hb_graphite2_face_data_t *data = (hb_graphite2_face_data_t *) calloc (1, 
sizeof (hb_graphite2_shaper_face_data_t));
+  hb_graphite2_face_data_t *data = (hb_graphite2_face_data_t *) calloc (1, 
sizeof (hb_graphite2_face_data_t));
   if (unlikely (!data))
 return nullptr;
 
diff --git a/src/hb-uniscribe.cc b/src/hb-uniscribe.cc
index 6d579534..94e6bb55 100644
--- a/src/hb-uniscribe.cc
+++ b/src/hb-uniscribe.cc
@@ -442,7 +442,7 @@ _hb_rename_font (hb_blob_t *blob, wchar_t *new_name)
 hb_uniscribe_face_data_t *
 _hb_uniscribe_shaper_face_data_create (hb_face_t *face)
 {
-  hb_uniscribe_face_data_t *data = (hb_uniscribe_face_data_t *) calloc (1, 
sizeof (hb_uniscribe_shaper_face_data_t));
+  hb_uniscribe_face_data_t *data = (hb_uniscribe_face_data_t *) calloc (1, 
sizeof (hb_uniscribe_face_data_t));
   if (unlikely (!data))
 return nullptr;
 
@@ -520,7 +520,7 @@ _hb_uniscribe_shaper_font_data_create (hb_font_t *font)
 {
   if (unlikely (!hb_uniscribe_shaper_face_data_ensure (font->face))) return 
nullptr;
 
-  hb_uniscribe_font_data_t *data = (hb_uniscribe_font_data_t *) calloc (1, 
sizeof (hb_uniscribe_shaper_font_data_t));
+  hb_uniscribe_font_data_t *data = (hb_uniscribe_font_data_t *) calloc (1, 
sizeof (hb_uniscribe_font_data_t));
   if (unlikely (!data))
 return nullptr;
 
commit 91126aa11a5fa2bff72137df4768ad13fc9b7803
Author: Behdad Esfahbod 
Date:   Thu Aug 2 02:03:13 2018 -0700

[uniscribe] Update for recent changes

diff --git a/src/hb-uniscribe.cc b/src/hb-uniscribe.cc
index 7f7f10d0..6d579534 100644
--- a/src/hb-uniscribe.cc
+++ b/src/hb-uniscribe.cc
@@ -383,7 +383,7 @@ _hb_rename_font (hb_blob_t *blob, wchar_t *new_name)
 
   memcpy(new_sfnt_data, orig_sfnt_data, length);
 
-  OT::name  = OT::StructAtOffset (new_sfnt_data, 
name_table_offset);
+  OT::name  = StructAtOffset (new_sfnt_data, name_table_offset);
   name.format.set (0);
   name.count.set (ARRAY_LENGTH (name_IDs));
   name.stringOffset.set (name.get_size ());
@@ -399,7 +399,7 @@ _hb_rename_font (hb_blob_t *blob, wchar_t *new_name)
   }
 
   /* Copy string data from new_name, converting wchar_t to UTF16BE. */
-  unsigned char *p = ::StructAfter (name);
+  unsigned char *p =  (name);
   for (unsigned int i = 0; i < name_str_len; i++)
   {
 *p++ = new_name[i] >> 8;
commit 66952ec47b5f09d88b83fb6a71b1cdb26c53668d
Author: Behdad Esfahbod 
Date:   Thu Aug 2 01:44:20 2018 -0700

Remove unused table reference

diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh
index fc2e57e7..c8855723 100644
--- a/src/hb-ot-layout-private.hh
+++ b/src/hb-ot-layout-private.hh
@@ -179,11 +179,10 @@ struct hb_ot_layout_t
 
   /* TODO Move the following out of this struct. */
   hb_face_t *face; /* MUST be before the lazy loaders. */
-  hb_table_lazy_loader_t<1, struct OT::BASE> base;
-  hb_table_lazy_loader_t<2, struct OT::MATH> math;
-  hb_table_lazy_loader_t<3, struct OT::fvar> fvar;
-  hb_table_lazy_loader_t<4, struct OT::avar> avar;
-  hb_table_lazy_loader_t<5, struct AAT::morx> morx;
+  hb_table_lazy_loader_t<1, struct OT::MATH> math;
+  hb_table_lazy_loader_t<2, struct OT::fvar> fvar;
+  hb_table_lazy_loader_t<3, struct OT::avar> avar;
+  hb_table_lazy_loader_t<4, struct AAT::morx> morx;
 };
 
 
commit 443de26fa56dd1ef149d3ce4206f4495eceec2eb
Author: Behdad Esfahbod 
Date:   Thu Aug 2 01:41:19 2018 -0700

Minor

diff --git a/src/hb-face.cc b/src/hb-face.cc
index 75dc486e..9d17c4a5 100644
--- a/src/hb-face.cc
+++ b/src/hb-face.cc
@@ -33,7 +33,6 @@
 #include "hb-open-file-private.hh"
 
 
-
 /**
  * hb_face_count: Get number of faces on the blob
  * @blob:
diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh
index 3a99937c..fc2e57e7 100644
--- a/src/hb-ot-layout-private.hh
+++ b/src/hb-ot-layout-private.hh
@@ -171,6 +171,12 @@ struct hb_ot_layout_t
   const struct OT::GSUB *gsub;
   const struct OT::GPOS *gpos;
 
+  unsigned int gsub_lookup_count;
+  unsigned int gpos_lookup_count;
+
+  hb_ot_layout_lookup_accelerator_t *gsub_accels;
+  hb_ot_layout_lookup_accelerator_t *gpos_accels;
+
   /* TODO Move the following out of this struct. */
   hb_face_t *face; /* MUST be before the lazy loaders. */
   hb_table_lazy_loader_t<1, struct OT::BASE> base;
@@ -178,12 +184,6 @@ struct hb_ot_layout_t
   hb_table_lazy_loader_t<3, struct 

[HarfBuzz] harfbuzz: Branch 'master' - 7 commits

2018-08-02 Thread Behdad Esfahbod
 src/dump-emoji.cc  |4 
 src/hb-aat-layout.cc   |2 
 src/hb-blob-private.hh |5 -
 src/hb-coretext.cc |   46 -
 src/hb-directwrite.cc  |   28 ++---
 src/hb-face.cc |   21 ++--
 src/hb-face.h  |   16 +--
 src/hb-fallback-shape.cc   |   24 ++---
 src/hb-graphite2.cc|   26 ++---
 src/hb-machinery-private.hh|  137 ++---
 src/hb-ot-font.cc  |   19 ++--
 src/hb-ot-layout-private.hh|   11 +-
 src/hb-ot-layout.cc|   17 +--
 src/hb-ot-shape-complex-arabic-fallback.hh |   18 +--
 src/hb-ot-shape.cc |   20 ++--
 src/hb-shaper-private.hh   |   10 +-
 src/hb-static.cc   |4 
 src/hb-subset-glyf.cc  |2 
 src/hb-subset.cc   |8 -
 src/hb-uniscribe.cc|   36 +++
 20 files changed, 231 insertions(+), 223 deletions(-)

New commits:
commit ff7826e90bce46985651015059872d1d8559b6ce
Author: Behdad Esfahbod 
Date:   Thu Aug 2 01:27:40 2018 -0700

Reduce storage by sharing face amongst lazy_loaders

diff --git a/src/hb-machinery-private.hh b/src/hb-machinery-private.hh
index dbabeadd..081cbbfd 100644
--- a/src/hb-machinery-private.hh
+++ b/src/hb-machinery-private.hh
@@ -590,93 +590,106 @@ struct BEInt
  * Lazy struct and blob loaders.
  */
 
-template 
 struct hb_base_lazy_loader_t
 {
+  static_assert (WheresFace > 0, "");
+
   /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */
   inline const Subclass* thiz (void) const { return static_cast (this); }
+  inline Subclass* thiz (void) { return static_cast (this); }
 
-  inline void init (hb_face_t *face_)
+  inline void init (void)
   {
-face = face_;
 instance = nullptr;
   }
-
-  inline const Returned * operator-> (void) const
+  inline void fini (void)
   {
-return thiz ()->get ();
+if (instance)
+  thiz ()->destroy (instance);
   }
 
-  protected:
-  hb_face_t *face;
-  mutable Stored *instance;
-};
-
-template 
-struct hb_lazy_loader_t : hb_base_lazy_loader_t, T>
-{
-  inline void fini (void)
+  inline const Returned * operator-> (void) const
   {
-if (this->instance && this->instance != (T))
-{
-  this->instance->fini();
-  free (this->instance);
-}
+return thiz ()->get ();
   }
 
-  inline const T* get (void) const
+  inline Stored * get_stored (void) const
   {
   retry:
-T *p = (T *) hb_atomic_ptr_get (>instance);
+Stored *p = (Stored *) hb_atomic_ptr_get (>instance);
 if (unlikely (!p))
 {
-  p = (T *) calloc (1, sizeof (T));
-  if (unlikely (!p))
-p = const_cast ((T));
-  else
-   p->init (this->face);
-  if (unlikely (!hb_atomic_ptr_cmpexch (const_cast(>instance), 
nullptr, p)))
+  hb_face_t *face = *(((hb_face_t **) this) - WheresFace);
+  p = thiz ()->create (face);
+  if (unlikely (!hb_atomic_ptr_cmpexch (const_cast(>instance), nullptr, p)))
   {
-   if (p != (T))
- p->fini ();
+thiz ()->destroy (p);
goto retry;
   }
 }
 return p;
   }
+
+  inline const Returned * get (void) const
+  {
+return thiz ()->convert (get_stored ());
+  }
+
+  static inline const Returned* convert (const Stored *p)
+  {
+return p;
+  }
+
+  private:
+  /* Must only have one pointer. */
+  mutable Stored *instance;
 };
 
-template 
-struct hb_table_lazy_loader_t : 
hb_base_lazy_loader_t, T, hb_blob_t>
+template 
+struct hb_lazy_loader_t : hb_base_lazy_loader_t, T>
 {
-  inline void fini (void)
+  static inline T *create (hb_face_t *face)
   {
-hb_blob_destroy (this->instance);
+T *p = (T *) calloc (1, sizeof (T));
+if (unlikely (!p))
+  p = const_cast ((T));
+else
+  p->init (face);
+return p;
   }
-
-  inline hb_blob_t* get_blob (void) const
+  static inline void destroy (T *p)
   {
-  retry:
-hb_blob_t *b = (hb_blob_t *) hb_atomic_ptr_get (>instance);
-if (unlikely (!b))
+if (p != (T))
 {
-  b = hb_sanitize_context_t ().reference_table (this->face);
-  if (!hb_atomic_ptr_cmpexch (>instance, nullptr, b))
-  {
-   hb_blob_destroy (b);
-   goto retry;
-  }
-  this->instance = b;
+  p->fini();
+  free (p);
 }
-return b;
   }
+};
 
-  inline const T* get (void) const
+template 
+struct hb_table_lazy_loader_t : hb_base_lazy_loader_t, T, hb_blob_t>
+{
+  static inline hb_blob_t *create (hb_face_t *face)
+  {
+return hb_sanitize_context_t ().reference_table (face);
+  }
+  static inline void destroy (hb_blob_t *p)
+  {
+hb_blob_destroy (p);
+  }
+  static inline const T* convert (const hb_blob_t *blob)
+  {
+return blob->as ();
+  }
+
+  inline hb_blob_t* 

Re: [HarfBuzz] HB_CLOSURE_MAX_STAGES (was: harfbuzz: Branch 'master')

2018-08-02 Thread Richard Wordingham
On Wed, 1 Aug 2018 17:31:06 -0700
Behdad Esfahbod  wrote:

> On Mon, Jul 30, 2018 at 6:21 PM, Richard Wordingham <
> richard.wording...@ntlworld.com> wrote:  
> 
> > On Mon, 30 Jul 2018 17:04:42 -0700
> > Behdad Esfahbod  wrote:

> > > It's impossible to hit that limit...  Ok, it would be impossible
> > > if we increase it to 32.  I'll do that.  

> > That'll probably work, but I'm now intrigued.  Why have a limit that
> > will never be hit?  Are you just catering for HarfBuzz's logic
> > simply going badly wrong in very unusual circumstances?

> Yes, simply as defense against malicious fonts and how the subsetter's
> glyph-closure routine can be tricked to collect (way) more glyphs than
> shaper can actually reach.

But if the limit is never hit, then the defence is never used, and so
it may as well not be there.  Or is it meant to initimidate
designers of malicious fonts who study Harfbuzz?

Richard.
___
HarfBuzz mailing list
HarfBuzz@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/harfbuzz


[HarfBuzz] Fwd: On atomic ops and text stack thread-safety

2018-08-02 Thread Behdad Esfahbod
Since I've been hacking on the atomic stuff past few days (It's over!) and
Werner asked, I wrote this.  Might be of interest to others.

-- Forwarded message --
From: Behdad Esfahbod 
Date: Wed, Aug 1, 2018 at 10:16 PM
Subject: On atomic ops and text stack thread-safety
To: Werner LEMBERG 


Ok let's see if I can explain this succinctly.

For what I'm doing, performance is definitely of utmost important, but only
while maintaining correctness, and portability has been a huge part of it.

There's different issues at play.  Let's discuss them in three parts:

  1. Atomic operations in presence of multiple threads,
  2. Memory ordering in regard to compiler and CPU optimizations,
  3. Memory / cache coherency in presence of multiple CPUs.


== Atomic operations ==

By default, read and write operations on properly-aligned char, short, int,
and long on all current CPUs are atomic.  C11 and C++11 guarantee that
IIRC.  What we mean by atomic, is that, eg, if we have "static x = 0" and
one thread sets it to 42, other threads reading this same variable will
only either see 0 or 42, but no other value.  That's atomicity of read /
write (also known as load / store).

Now, when we get to more involved operations, namely, "read-modify-write",
atomicity is not guaranteed.  Take FT_Reference_Library:

  FT_EXPORT_DEF( FT_Error )
  FT_Reference_Library( FT_Library  library )
  {
if ( !library )
  return FT_THROW( Invalid_Library_Handle );

library->refcount++;

return FT_Err_Ok;
  }

As long as there's only one thread of execution, this code is fine and
correct.  Compiler and CPU make sure that what gets executed has the same
semantics of what you wrote.  But what if there are multiple threads
involved?  Things get out of hand and programmer needs to intervene.  In
this case, the "refcount++" is a read-modify-write operation.  That is, in
machine code / pseudo-assembler, it involves:

  load r, refcount
  inc r, 1
  store refcount, r

Now, what happens if the first thread calling this function performs the
load, reading, eg. refcount of 1, but before performing the rest of the
operations it's pre-empted and second thread runs the same code, also
reading refcount of 1, incrementing it, and storing 2.  Now the first
thread resumes, incrementing r to 2 and store it into refcount.  What just
happened is that we started with a refcount of 1, two threads incremented
refcount by one each, but as a result we got 2 stored in refcount instead
of 3.

That's the first issue.  To resolve it, we need an atomic "fetch_and_add"
operation.  That is, one that does the "load, inc, store" atomically
without another thread interrupting.  That's exactly the kind of thing that
atomic operations in CPUs, compilers, and standard libraries provide.

You can read more at Linux's documentation:

  https://github.com/torvalds/linux/blob/master/
Documentation/core-api/atomic_ops.rst

== Memory ordering and optimizations ==

Imagine this pseudo code:

static int x = 0;
static bool ready = false;

One thread running:

  x = expensive_computation_producing_42 ();
  ready = true;

Other thread:

  if (ready)
use (x);

Now, as discussed, we already know that read and write of int and bool
types *are* atomic.  So *that* is not the issue here.  However, you would
expect that if the second thread get into the body of the "if", then the
value of x read must be 42.  However, that's not what always will happen,
because of legitimate compiler and CPU optimizations.

In particular, the compiler "is within its rights" to reorder the
assignment of x and ready in the first thead.  The CPU, is within its
rights to execute them out of order, or in parallel.  These freedoms /
rights allow for tons of optimizations / parallelism.  They are allowed
because to single-threaded programs they don't make any observable
difference.  The program results are always the same.  But if you have
another thread reading those, this can wreak havoc.  What's worse, the
second thread's compiler / CPU is also within its rights to, eg, read "x"
before / in parallel to reading "ready".  Because to a single-threaded
program those do NOT make a visible difference, but allow for faster code.

Imagine the code above being transformed in machine code to:

Thread 1:

  ready = true;
  x = expensive_computation_producing_42 ();

Other thread:

  r = x;
  if (ready)
use (r);

Obviously there's a race condition there now.  We might be reading old
value of x and using it because "ready" is true.  That's a problem.

To fix this problem, we use what's called memory-barriers.  There's many
types of them, offering different guarantees.  For now, let's just discuss
what's known as a full memory barrier, or simply memory-barrier.  What a
memory barrier does, in this, context, is to make sure compiler / CPU do
NOT reorder reads / writes from before and after it.  Ie. a read / write
after the memory barrier will be executed *after* a read / write before the
memory