summaryrefslogtreecommitdiff
path: root/thirdparty/harfbuzz/src/hb-cff-interp-common.hh
blob: 5c2cb060a412bdd58476dbad0efc0083f50c2a90 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
/*
 * Copyright © 2018 Adobe Inc.
 *
 *  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_CFF_INTERP_COMMON_HH
#define HB_CFF_INTERP_COMMON_HH

namespace CFF {

using namespace OT;

typedef unsigned int op_code_t;


/* === Dict operators === */

/* One byte operators (0-31) */
#define OpCode_version		  0 /* CFF Top */
#define OpCode_Notice		  1 /* CFF Top */
#define OpCode_FullName		  2 /* CFF Top */
#define OpCode_FamilyName	  3 /* CFF Top */
#define OpCode_Weight		  4 /* CFF Top */
#define OpCode_FontBBox		  5 /* CFF Top */
#define OpCode_BlueValues	  6 /* CFF Private, CFF2 Private */
#define OpCode_OtherBlues	  7 /* CFF Private, CFF2 Private */
#define OpCode_FamilyBlues	  8 /* CFF Private, CFF2 Private */
#define OpCode_FamilyOtherBlues	  9 /* CFF Private, CFF2 Private */
#define OpCode_StdHW		 10 /* CFF Private, CFF2 Private */
#define OpCode_StdVW		 11 /* CFF Private, CFF2 Private */
#define OpCode_escape		 12 /* All. Shared with CS */
#define OpCode_UniqueID		 13 /* CFF Top */
#define OpCode_XUID		 14 /* CFF Top */
#define OpCode_charset		 15 /* CFF Top (0) */
#define OpCode_Encoding		 16 /* CFF Top (0) */
#define OpCode_CharStrings	 17 /* CFF Top, CFF2 Top */
#define OpCode_Private		 18 /* CFF Top, CFF2 FD */
#define OpCode_Subrs		 19 /* CFF Private, CFF2 Private */
#define OpCode_defaultWidthX	 20 /* CFF Private (0) */
#define OpCode_nominalWidthX	 21 /* CFF Private (0) */
#define OpCode_vsindexdict	 22 /* CFF2 Private/CS */
#define OpCode_blenddict	 23 /* CFF2 Private/CS */
#define OpCode_vstore		 24 /* CFF2 Top */
#define OpCode_reserved25	 25
#define OpCode_reserved26	 26
#define OpCode_reserved27	 27

/* Numbers */
#define OpCode_shortint		 28 /* 16-bit integer, All */
#define OpCode_longintdict	 29 /* 32-bit integer, All */
#define OpCode_BCD		 30 /* Real number, CFF2 Top/FD */
#define OpCode_reserved31	 31

/* 1-byte integers */
#define OpCode_OneByteIntFirst	 32 /* All. beginning of the range of first byte ints */
#define OpCode_OneByteIntLast	246 /* All. ending of the range of first byte int */

/* 2-byte integers */
#define OpCode_TwoBytePosInt0	247 /* All. first byte of two byte positive int (+108 to +1131) */
#define OpCode_TwoBytePosInt1	248
#define OpCode_TwoBytePosInt2	249
#define OpCode_TwoBytePosInt3	250

#define OpCode_TwoByteNegInt0	251 /* All. first byte of two byte negative int (-1131 to -108) */
#define OpCode_TwoByteNegInt1	252
#define OpCode_TwoByteNegInt2	253
#define OpCode_TwoByteNegInt3	254

/* Two byte escape operators 12, (0-41) */
#define OpCode_ESC_Base		256
#define Make_OpCode_ESC(byte2)	((op_code_t)(OpCode_ESC_Base + (byte2)))

inline op_code_t Unmake_OpCode_ESC (op_code_t op)  { return (op_code_t)(op - OpCode_ESC_Base); }
inline bool Is_OpCode_ESC (op_code_t op) { return op >= OpCode_ESC_Base; }
inline unsigned int OpCode_Size (op_code_t op) { return Is_OpCode_ESC (op) ? 2: 1; }

#define OpCode_Copyright	Make_OpCode_ESC(0) /* CFF Top */
#define OpCode_isFixedPitch	Make_OpCode_ESC(1) /* CFF Top (false) */
#define OpCode_ItalicAngle	Make_OpCode_ESC(2) /* CFF Top (0) */
#define OpCode_UnderlinePosition Make_OpCode_ESC(3) /* CFF Top (-100) */
#define OpCode_UnderlineThickness Make_OpCode_ESC(4) /* CFF Top (50) */
#define OpCode_PaintType	Make_OpCode_ESC(5) /* CFF Top (0) */
#define OpCode_CharstringType	Make_OpCode_ESC(6) /* CFF Top (2) */
#define OpCode_FontMatrix	Make_OpCode_ESC(7) /* CFF Top, CFF2 Top (.001 0 0 .001 0 0)*/
#define OpCode_StrokeWidth	Make_OpCode_ESC(8) /* CFF Top (0) */
#define OpCode_BlueScale	Make_OpCode_ESC(9) /* CFF Private, CFF2 Private (0.039625) */
#define OpCode_BlueShift	Make_OpCode_ESC(10) /* CFF Private, CFF2 Private (7) */
#define OpCode_BlueFuzz		Make_OpCode_ESC(11) /* CFF Private, CFF2 Private (1) */
#define OpCode_StemSnapH	Make_OpCode_ESC(12) /* CFF Private, CFF2 Private */
#define OpCode_StemSnapV	Make_OpCode_ESC(13) /* CFF Private, CFF2 Private */
#define OpCode_ForceBold	Make_OpCode_ESC(14) /* CFF Private (false) */
#define OpCode_reservedESC15	Make_OpCode_ESC(15)
#define OpCode_reservedESC16	Make_OpCode_ESC(16)
#define OpCode_LanguageGroup	Make_OpCode_ESC(17) /* CFF Private, CFF2 Private (0) */
#define OpCode_ExpansionFactor	Make_OpCode_ESC(18) /* CFF Private, CFF2 Private (0.06) */
#define OpCode_initialRandomSeed Make_OpCode_ESC(19) /* CFF Private (0) */
#define OpCode_SyntheticBase	Make_OpCode_ESC(20) /* CFF Top */
#define OpCode_PostScript	Make_OpCode_ESC(21) /* CFF Top */
#define OpCode_BaseFontName	Make_OpCode_ESC(22) /* CFF Top */
#define OpCode_BaseFontBlend	Make_OpCode_ESC(23) /* CFF Top */
#define OpCode_reservedESC24	Make_OpCode_ESC(24)
#define OpCode_reservedESC25	Make_OpCode_ESC(25)
#define OpCode_reservedESC26	Make_OpCode_ESC(26)
#define OpCode_reservedESC27	Make_OpCode_ESC(27)
#define OpCode_reservedESC28	Make_OpCode_ESC(28)
#define OpCode_reservedESC29	Make_OpCode_ESC(29)
#define OpCode_ROS		Make_OpCode_ESC(30) /* CFF Top_CID */
#define OpCode_CIDFontVersion	Make_OpCode_ESC(31) /* CFF Top_CID (0) */
#define OpCode_CIDFontRevision	Make_OpCode_ESC(32) /* CFF Top_CID (0) */
#define OpCode_CIDFontType	Make_OpCode_ESC(33) /* CFF Top_CID (0) */
#define OpCode_CIDCount		Make_OpCode_ESC(34) /* CFF Top_CID (8720) */
#define OpCode_UIDBase		Make_OpCode_ESC(35) /* CFF Top_CID */
#define OpCode_FDArray		Make_OpCode_ESC(36) /* CFF Top_CID, CFF2 Top */
#define OpCode_FDSelect		Make_OpCode_ESC(37) /* CFF Top_CID, CFF2 Top */
#define OpCode_FontName		Make_OpCode_ESC(38) /* CFF Top_CID */


/* === CharString operators === */

#define OpCode_hstem		  1 /* CFF, CFF2 */
#define OpCode_Reserved2	  2
#define OpCode_vstem		  3 /* CFF, CFF2 */
#define OpCode_vmoveto		  4 /* CFF, CFF2 */
#define OpCode_rlineto		  5 /* CFF, CFF2 */
#define OpCode_hlineto		  6 /* CFF, CFF2 */
#define OpCode_vlineto		  7 /* CFF, CFF2 */
#define OpCode_rrcurveto	  8 /* CFF, CFF2 */
#define OpCode_Reserved9	  9
#define OpCode_callsubr		 10 /* CFF, CFF2 */
#define OpCode_return		 11 /* CFF */
//#define OpCode_escape		 12 /* CFF, CFF2 */
#define OpCode_Reserved13	 13
#define OpCode_endchar		 14 /* CFF */
#define OpCode_vsindexcs	 15 /* CFF2 */
#define OpCode_blendcs		 16 /* CFF2 */
#define OpCode_Reserved17	 17
#define OpCode_hstemhm		 18 /* CFF, CFF2 */
#define OpCode_hintmask		 19 /* CFF, CFF2 */
#define OpCode_cntrmask		 20 /* CFF, CFF2 */
#define OpCode_rmoveto		 21 /* CFF, CFF2 */
#define OpCode_hmoveto		 22 /* CFF, CFF2 */
#define OpCode_vstemhm		 23 /* CFF, CFF2 */
#define OpCode_rcurveline	 24 /* CFF, CFF2 */
#define OpCode_rlinecurve	 25 /* CFF, CFF2 */
#define OpCode_vvcurveto	 26 /* CFF, CFF2 */
#define OpCode_hhcurveto	 27 /* CFF, CFF2 */
//#define OpCode_shortint	 28 /* CFF, CFF2 */
#define OpCode_callgsubr	 29 /* CFF, CFF2 */
#define OpCode_vhcurveto	 30 /* CFF, CFF2 */
#define OpCode_hvcurveto	 31 /* CFF, CFF2 */

#define OpCode_fixedcs		255 /* 32-bit fixed */

/* Two byte escape operators 12, (0-41) */
#define OpCode_dotsection	Make_OpCode_ESC(0) /* CFF (obsoleted) */
#define OpCode_ReservedESC1	Make_OpCode_ESC(1)
#define OpCode_ReservedESC2	Make_OpCode_ESC(2)
#define OpCode_and		Make_OpCode_ESC(3) /* CFF */
#define OpCode_or		Make_OpCode_ESC(4) /* CFF */
#define OpCode_not		Make_OpCode_ESC(5) /* CFF */
#define OpCode_ReservedESC6	Make_OpCode_ESC(6)
#define OpCode_ReservedESC7	Make_OpCode_ESC(7)
#define OpCode_ReservedESC8	Make_OpCode_ESC(8)
#define OpCode_abs		Make_OpCode_ESC(9) /* CFF */
#define OpCode_add		Make_OpCode_ESC(10) /* CFF */
#define OpCode_sub		Make_OpCode_ESC(11) /* CFF */
#define OpCode_div		Make_OpCode_ESC(12) /* CFF */
#define OpCode_ReservedESC13	Make_OpCode_ESC(13)
#define OpCode_neg		Make_OpCode_ESC(14) /* CFF */
#define OpCode_eq		Make_OpCode_ESC(15) /* CFF */
#define OpCode_ReservedESC16	Make_OpCode_ESC(16)
#define OpCode_ReservedESC17	Make_OpCode_ESC(17)
#define OpCode_drop		Make_OpCode_ESC(18) /* CFF */
#define OpCode_ReservedESC19	Make_OpCode_ESC(19)
#define OpCode_put		Make_OpCode_ESC(20) /* CFF */
#define OpCode_get		Make_OpCode_ESC(21) /* CFF */
#define OpCode_ifelse		Make_OpCode_ESC(22) /* CFF */
#define OpCode_random		Make_OpCode_ESC(23) /* CFF */
#define OpCode_mul		Make_OpCode_ESC(24) /* CFF */
//#define OpCode_reservedESC25	Make_OpCode_ESC(25)
#define OpCode_sqrt		Make_OpCode_ESC(26) /* CFF */
#define OpCode_dup		Make_OpCode_ESC(27) /* CFF */
#define OpCode_exch		Make_OpCode_ESC(28) /* CFF */
#define OpCode_index		Make_OpCode_ESC(29) /* CFF */
#define OpCode_roll		Make_OpCode_ESC(30) /* CFF */
#define OpCode_reservedESC31	Make_OpCode_ESC(31)
#define OpCode_reservedESC32	Make_OpCode_ESC(32)
#define OpCode_reservedESC33	Make_OpCode_ESC(33)
#define OpCode_hflex		Make_OpCode_ESC(34) /* CFF, CFF2 */
#define OpCode_flex		Make_OpCode_ESC(35) /* CFF, CFF2 */
#define OpCode_hflex1		Make_OpCode_ESC(36) /* CFF, CFF2 */
#define OpCode_flex1		Make_OpCode_ESC(37) /* CFF, CFF2 */


#define OpCode_Invalid		0xFFFFu


struct number_t
{
  void set_int (int v)       { value = v; }
  int to_int () const        { return value; }

  void set_fixed (int32_t v) { value = v / 65536.0; }
  int32_t to_fixed () const  { return value * 65536.0; }

  void set_real (double v)   { value = v; }
  double to_real () const    { return value; }

  bool in_int_range () const
  { return ((double) (int16_t) to_int () == value); }

  bool operator >  (const number_t &n) const { return value > n.to_real (); }
  bool operator <  (const number_t &n) const { return n > *this; }
  bool operator >= (const number_t &n) const { return !(*this < n); }
  bool operator <= (const number_t &n) const { return !(*this > n); }

  const number_t &operator += (const number_t &n)
  {
    set_real (to_real () + n.to_real ());

    return *this;
  }

  protected:
  double value = 0.;
};

/* byte string */
struct UnsizedByteStr : UnsizedArrayOf <HBUINT8>
{
  hb_ubytes_t as_ubytes (unsigned l) const
  { return hb_ubytes_t ((const unsigned char *) this, l); }

  // encode 2-byte int (Dict/CharString) or 4-byte int (Dict)
  template <typename T, typename V>
  static bool serialize_int (hb_serialize_context_t *c, op_code_t intOp, V value)
  {
    TRACE_SERIALIZE (this);

    HBUINT8 *p = c->allocate_size<HBUINT8> (1);
    if (unlikely (!p)) return_trace (false);
    *p = intOp;

    T *ip = c->allocate_size<T> (T::static_size);
    if (unlikely (!ip)) return_trace (false);
    return_trace (c->check_assign (*ip, value, HB_SERIALIZE_ERROR_INT_OVERFLOW));
  }

  template <typename V>
  static bool serialize_int4 (hb_serialize_context_t *c, V value)
  { return serialize_int<HBINT32> (c, OpCode_longintdict, value); }

  template <typename V>
  static bool serialize_int2 (hb_serialize_context_t *c, V value)
  { return serialize_int<HBINT16> (c, OpCode_shortint, value); }

  /* Defining null_size allows a Null object may be created. Should be safe because:
   * A descendent struct Dict uses a Null pointer to indicate a missing table,
   * checked before access.
   */
  DEFINE_SIZE_MIN(0);
};

/* A byte string associated with the current offset and an error condition */
struct byte_str_ref_t
{
  byte_str_ref_t () { init (); }

  void init ()
  {
    str = hb_ubytes_t ();
    offset = 0;
    error = false;
  }

  void fini () {}

  byte_str_ref_t (const hb_ubytes_t &str_, unsigned int offset_ = 0)
    : str (str_), offset (offset_), error (false) {}

  void reset (const hb_ubytes_t &str_, unsigned int offset_ = 0)
  {
    str = str_;
    offset = offset_;
    error = false;
  }

  const unsigned char& operator [] (int i) {
    if (unlikely ((unsigned int) (offset + i) >= str.length))
    {
      set_error ();
      return Null (unsigned char);
    }
    return str[offset + i];
  }

  /* Conversion to hb_ubytes_t */
  operator hb_ubytes_t () const { return str.sub_array (offset, str.length - offset); }

  hb_ubytes_t sub_array (unsigned int offset_, unsigned int len_) const
  { return str.sub_array (offset_, len_); }

  bool avail (unsigned int count=1) const
  { return (!in_error () && offset + count <= str.length); }
  void inc (unsigned int count=1)
  {
    if (likely (!in_error () && (offset <= str.length) && (offset + count <= str.length)))
    {
      offset += count;
    }
    else
    {
      offset = str.length;
      set_error ();
    }
  }

  void set_error ()      { error = true; }
  bool in_error () const { return error; }

  hb_ubytes_t       str;
  unsigned int  offset; /* beginning of the sub-string within str */

  protected:
  bool	  error;
};

using byte_str_array_t = hb_vector_t<hb_ubytes_t>;

/* stack */
template <typename ELEM, int LIMIT>
struct cff_stack_t
{
  ELEM& operator [] (unsigned int i)
  {
    if (unlikely (i >= count))
    {
      set_error ();
      return Crap (ELEM);
    }
    return elements[i];
  }

  void push (const ELEM &v)
  {
    if (likely (count < LIMIT))
      elements[count++] = v;
    else
      set_error ();
  }
  ELEM &push ()
  {
    if (likely (count < LIMIT))
      return elements[count++];
    else
    {
      set_error ();
      return Crap (ELEM);
    }
  }

  ELEM& pop ()
  {
    if (likely (count > 0))
      return elements[--count];
    else
    {
      set_error ();
      return Crap (ELEM);
    }
  }
  void pop (unsigned int n)
  {
    if (likely (count >= n))
      count -= n;
    else
      set_error ();
  }

  const ELEM& peek ()
  {
    if (unlikely (count == 0))
    {
      set_error ();
      return Null (ELEM);
    }
    return elements[count - 1];
  }

  void unpop ()
  {
    if (likely (count < LIMIT))
      count++;
    else
      set_error ();
  }

  void clear () { count = 0; }

  bool in_error () const { return (error); }
  void set_error ()      { error = true; }

  unsigned int get_count () const { return count; }
  bool is_empty () const          { return !count; }

  hb_array_t<const ELEM> sub_array (unsigned start, unsigned length) const
  { return hb_array_t<const ELEM> (elements).sub_array (start, length); }

  private:
  bool error = false;
  unsigned int count = 0;
  ELEM elements[LIMIT];
};

/* argument stack */
template <typename ARG=number_t>
struct arg_stack_t : cff_stack_t<ARG, 513>
{
  void push_int (int v)
  {
    ARG &n = S::push ();
    n.set_int (v);
  }

  void push_fixed (int32_t v)
  {
    ARG &n = S::push ();
    n.set_fixed (v);
  }

  void push_real (double v)
  {
    ARG &n = S::push ();
    n.set_real (v);
  }

  ARG& pop_num () { return this->pop (); }

  int pop_int ()  { return this->pop ().to_int (); }

  unsigned int pop_uint ()
  {
    int i = pop_int ();
    if (unlikely (i < 0))
    {
      i = 0;
      S::set_error ();
    }
    return (unsigned) i;
  }

  void push_longint_from_substr (byte_str_ref_t& str_ref)
  {
    push_int ((str_ref[0] << 24) | (str_ref[1] << 16) | (str_ref[2] << 8) | (str_ref[3]));
    str_ref.inc (4);
  }

  bool push_fixed_from_substr (byte_str_ref_t& str_ref)
  {
    if (unlikely (!str_ref.avail (4)))
      return false;
    push_fixed ((int32_t)*(const HBUINT32*)&str_ref[0]);
    str_ref.inc (4);
    return true;
  }

  private:
  typedef cff_stack_t<ARG, 513> S;
};

/* an operator prefixed by its operands in a byte string */
struct op_str_t
{
  hb_ubytes_t str;
  op_code_t  op;
};

/* base of OP_SERIALIZER */
struct op_serializer_t
{
  protected:
  bool copy_opstr (hb_serialize_context_t *c, const op_str_t& opstr) const
  {
    TRACE_SERIALIZE (this);

    HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.length);
    if (unlikely (!d)) return_trace (false);
    memcpy (d, &opstr.str[0], opstr.str.length);
    return_trace (true);
  }
};

template <typename VAL>
struct parsed_values_t
{
  void init ()
  {
    opStart = 0;
    values.init ();
  }
  void fini () { values.fini (); }

  void alloc (unsigned n)
  {
    values.alloc (n);
  }

  void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t ())
  {
    VAL *val = values.push ();
    val->op = op;
    val->str = str_ref.str.sub_array (opStart, str_ref.offset - opStart);
    opStart = str_ref.offset;
  }

  void add_op (op_code_t op, const byte_str_ref_t& str_ref, const VAL &v)
  {
    VAL *val = values.push (v);
    val->op = op;
    val->str = str_ref.sub_array ( opStart, str_ref.offset - opStart);
    opStart = str_ref.offset;
  }

  bool has_op (op_code_t op) const
  {
    for (const auto& v : values)
      if (v.op == op) return true;
    return false;
  }

  unsigned get_count () const { return values.length; }
  const VAL &get_value (unsigned int i)   const { return values[i]; }
  const VAL &operator [] (unsigned int i) const { return get_value (i); }

  unsigned int       opStart;
  hb_vector_t<VAL>   values;
};

template <typename ARG=number_t>
struct interp_env_t
{
  interp_env_t () {}
  interp_env_t (const hb_ubytes_t &str_)
  {
    str_ref.reset (str_);
  }
  bool in_error () const
  { return error || str_ref.in_error () || argStack.in_error (); }

  void set_error () { error = true; }

  op_code_t fetch_op ()
  {
    op_code_t  op = OpCode_Invalid;
    if (unlikely (!str_ref.avail ()))
      return OpCode_Invalid;
    op = (op_code_t)(unsigned char)str_ref[0];
    if (op == OpCode_escape) {
      if (unlikely (!str_ref.avail ()))
	return OpCode_Invalid;
      op = Make_OpCode_ESC(str_ref[1]);
      str_ref.inc ();
    }
    str_ref.inc ();
    return op;
  }

  const ARG& eval_arg (unsigned int i) { return argStack[i]; }

  ARG& pop_arg () { return argStack.pop (); }
  void pop_n_args (unsigned int n) { argStack.pop (n); }

  void clear_args () { pop_n_args (argStack.get_count ()); }

  byte_str_ref_t
		str_ref;
  arg_stack_t<ARG>
		argStack;
  protected:
  bool		error = false;
};

using num_interp_env_t =  interp_env_t<>;

template <typename ARG=number_t>
struct opset_t
{
  static void process_op (op_code_t op, interp_env_t<ARG>& env)
  {
    switch (op) {
      case OpCode_shortint:
	env.argStack.push_int ((int16_t)((env.str_ref[0] << 8) | env.str_ref[1]));
	env.str_ref.inc (2);
	break;

      case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1:
      case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3:
	env.argStack.push_int ((int16_t)((op - OpCode_TwoBytePosInt0) * 256 + env.str_ref[0] + 108));
	env.str_ref.inc ();
	break;

      case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1:
      case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3:
	env.argStack.push_int ((-(int16_t)(op - OpCode_TwoByteNegInt0) * 256 - env.str_ref[0] - 108));
	env.str_ref.inc ();
	break;

      default:
	/* 1-byte integer */
	if (likely ((OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast)))
	{
	  env.argStack.push_int ((int)op - 139);
	} else {
	  /* invalid unknown operator */
	  env.clear_args ();
	  env.set_error ();
	}
	break;
    }
  }
};

template <typename ENV>
struct interpreter_t
{
  interpreter_t (ENV& env_) : env (env_) {}
  ENV& env;
};

} /* namespace CFF */

#endif /* HB_CFF_INTERP_COMMON_HH */