2 * Copyright © 2014 Google, Inc.
4 * This is part of HarfBuzz, a text shaping library.
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 * Google Author(s): Behdad Esfahbod
27 #ifndef HB_OT_CMAP_TABLE_HH
28 #define HB_OT_CMAP_TABLE_HH
30 #include "hb-open-type-private.hh"
31 #include "hb-subset-plan.hh"
37 * cmap -- Character To Glyph Index Mapping Table
40 #define HB_OT_TAG_cmap HB_TAG('c','m','a','p')
43 struct CmapSubtableFormat0
45 inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
47 hb_codepoint_t gid = codepoint < 256 ? glyphIdArray[codepoint] : 0;
54 inline bool sanitize (hb_sanitize_context_t *c) const
56 TRACE_SANITIZE (this);
57 return_trace (c->check_struct (this));
61 HBUINT16 format; /* Format number is set to 0. */
62 HBUINT16 lengthZ; /* Byte length of this subtable. */
63 HBUINT16 languageZ; /* Ignore. */
64 HBUINT8 glyphIdArray[256];/* An array that maps character
65 * code to glyph index values. */
67 DEFINE_SIZE_STATIC (6 + 256);
70 struct CmapSubtableFormat4
74 inline void init (const CmapSubtableFormat4 *subtable)
76 segCount = subtable->segCountX2 / 2;
77 endCount = subtable->values;
78 startCount = endCount + segCount + 1;
79 idDelta = startCount + segCount;
80 idRangeOffset = idDelta + segCount;
81 glyphIdArray = idRangeOffset + segCount;
82 glyphIdArrayLength = (subtable->length - 16 - 8 * segCount) / 2;
85 static inline bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph)
87 const accelerator_t *thiz = (const accelerator_t *) obj;
89 /* Custom two-array bsearch. */
90 int min = 0, max = (int) thiz->segCount - 1;
91 const HBUINT16 *startCount = thiz->startCount;
92 const HBUINT16 *endCount = thiz->endCount;
96 int mid = (min + max) / 2;
97 if (codepoint < startCount[mid])
99 else if (codepoint > endCount[mid])
111 unsigned int rangeOffset = thiz->idRangeOffset[i];
112 if (rangeOffset == 0)
113 gid = codepoint + thiz->idDelta[i];
116 /* Somebody has been smoking... */
117 unsigned int index = rangeOffset / 2 + (codepoint - thiz->startCount[i]) + i - thiz->segCount;
118 if (unlikely (index >= thiz->glyphIdArrayLength))
120 gid = thiz->glyphIdArray[index];
123 gid += thiz->idDelta[i];
126 *glyph = gid & 0xFFFFu;
130 const HBUINT16 *endCount;
131 const HBUINT16 *startCount;
132 const HBUINT16 *idDelta;
133 const HBUINT16 *idRangeOffset;
134 const HBUINT16 *glyphIdArray;
135 unsigned int segCount;
136 unsigned int glyphIdArrayLength;
139 inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
143 return accel.get_glyph_func (&accel, codepoint, glyph);
146 inline bool sanitize (hb_sanitize_context_t *c) const
148 TRACE_SANITIZE (this);
149 if (unlikely (!c->check_struct (this)))
150 return_trace (false);
152 if (unlikely (!c->check_range (this, length)))
154 /* Some broken fonts have too long of a "length" value.
155 * If that is the case, just change the value to truncate
156 * the subtable at the end of the blob. */
157 uint16_t new_length = (uint16_t) MIN ((uintptr_t) 65535,
158 (uintptr_t) (c->end -
160 if (!c->try_set (&length, new_length))
161 return_trace (false);
164 return_trace (16 + 4 * (unsigned int) segCountX2 <= length);
168 HBUINT16 format; /* Format number is set to 4. */
169 HBUINT16 length; /* This is the length in bytes of the
171 HBUINT16 languageZ; /* Ignore. */
172 HBUINT16 segCountX2; /* 2 x segCount. */
173 HBUINT16 searchRangeZ; /* 2 * (2**floor(log2(segCount))) */
174 HBUINT16 entrySelectorZ; /* log2(searchRange/2) */
175 HBUINT16 rangeShiftZ; /* 2 x segCount - searchRange */
177 HBUINT16 values[VAR];
179 HBUINT16 endCount[segCount]; /* End characterCode for each segment,
181 HBUINT16 reservedPad; /* Set to 0. */
182 HBUINT16 startCount[segCount]; /* Start character code for each segment. */
183 HBINT16 idDelta[segCount]; /* Delta for all character codes in segment. */
184 HBUINT16 idRangeOffset[segCount];/* Offsets into glyphIdArray or 0 */
185 HBUINT16 glyphIdArray[VAR]; /* Glyph index array (arbitrary length) */
189 DEFINE_SIZE_ARRAY (14, values);
192 struct CmapSubtableLongGroup
194 friend struct CmapSubtableFormat12;
195 friend struct CmapSubtableFormat13;
198 int cmp (hb_codepoint_t codepoint) const
200 if (codepoint < startCharCode) return -1;
201 if (codepoint > endCharCode) return +1;
205 inline bool sanitize (hb_sanitize_context_t *c) const
207 TRACE_SANITIZE (this);
208 return_trace (c->check_struct (this));
212 HBUINT32 startCharCode; /* First character code in this group. */
213 HBUINT32 endCharCode; /* Last character code in this group. */
214 HBUINT32 glyphID; /* Glyph index; interpretation depends on
215 * subtable format. */
217 DEFINE_SIZE_STATIC (12);
220 template <typename UINT>
221 struct CmapSubtableTrimmed
223 inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
225 /* Rely on our implicit array bound-checking. */
226 hb_codepoint_t gid = glyphIdArray[codepoint - startCharCode];
233 inline bool sanitize (hb_sanitize_context_t *c) const
235 TRACE_SANITIZE (this);
236 return_trace (c->check_struct (this) && glyphIdArray.sanitize (c));
240 UINT formatReserved; /* Subtable format and (maybe) padding. */
241 UINT lengthZ; /* Byte length of this subtable. */
242 UINT languageZ; /* Ignore. */
243 UINT startCharCode; /* First character code covered. */
244 ArrayOf<GlyphID, UINT>
245 glyphIdArray; /* Array of glyph index values for character
246 * codes in the range. */
248 DEFINE_SIZE_ARRAY (5 * sizeof (UINT), glyphIdArray);
251 struct CmapSubtableFormat6 : CmapSubtableTrimmed<HBUINT16> {};
252 struct CmapSubtableFormat10 : CmapSubtableTrimmed<HBUINT32 > {};
254 template <typename T>
255 struct CmapSubtableLongSegmented
259 inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
261 int i = groups.bsearch (codepoint);
264 *glyph = T::group_get_glyph (groups[i], codepoint);
268 inline bool sanitize (hb_sanitize_context_t *c) const
270 TRACE_SANITIZE (this);
271 return_trace (c->check_struct (this) && groups.sanitize (c));
274 inline bool serialize (hb_serialize_context_t *c,
275 hb_prealloced_array_t<CmapSubtableLongGroup> &group_data)
277 TRACE_SERIALIZE (this);
278 if (unlikely (!c->extend_min (*this))) return_trace (false);
279 Supplier<CmapSubtableLongGroup> supplier (group_data.array, group_data.len);
280 if (unlikely (!groups.serialize (c, supplier, group_data.len))) return_trace (false);
285 HBUINT16 format; /* Subtable format; set to 12. */
286 HBUINT16 reservedZ; /* Reserved; set to 0. */
287 HBUINT32 lengthZ; /* Byte length of this subtable. */
288 HBUINT32 languageZ; /* Ignore. */
289 SortedArrayOf<CmapSubtableLongGroup, HBUINT32>
290 groups; /* Groupings. */
292 DEFINE_SIZE_ARRAY (16, groups);
295 struct CmapSubtableFormat12 : CmapSubtableLongSegmented<CmapSubtableFormat12>
297 static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group,
299 { return group.glyphID + (u - group.startCharCode); }
302 struct CmapSubtableFormat13 : CmapSubtableLongSegmented<CmapSubtableFormat13>
304 static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group,
305 hb_codepoint_t u HB_UNUSED)
306 { return group.glyphID; }
311 GLYPH_VARIANT_NOT_FOUND = 0,
312 GLYPH_VARIANT_FOUND = 1,
313 GLYPH_VARIANT_USE_DEFAULT = 2
316 struct UnicodeValueRange
318 inline int cmp (const hb_codepoint_t &codepoint) const
320 if (codepoint < startUnicodeValue) return -1;
321 if (codepoint > startUnicodeValue + additionalCount) return +1;
325 inline bool sanitize (hb_sanitize_context_t *c) const
327 TRACE_SANITIZE (this);
328 return_trace (c->check_struct (this));
331 UINT24 startUnicodeValue; /* First value in this range. */
332 HBUINT8 additionalCount; /* Number of additional values in this
335 DEFINE_SIZE_STATIC (4);
338 typedef SortedArrayOf<UnicodeValueRange, HBUINT32> DefaultUVS;
342 inline int cmp (const hb_codepoint_t &codepoint) const
344 return unicodeValue.cmp (codepoint);
347 inline bool sanitize (hb_sanitize_context_t *c) const
349 TRACE_SANITIZE (this);
350 return_trace (c->check_struct (this));
353 UINT24 unicodeValue; /* Base Unicode value of the UVS */
354 GlyphID glyphID; /* Glyph ID of the UVS */
356 DEFINE_SIZE_STATIC (5);
359 typedef SortedArrayOf<UVSMapping, HBUINT32> NonDefaultUVS;
361 struct VariationSelectorRecord
363 inline glyph_variant_t get_glyph (hb_codepoint_t codepoint,
364 hb_codepoint_t *glyph,
365 const void *base) const
368 const DefaultUVS &defaults = base+defaultUVS;
369 i = defaults.bsearch (codepoint);
371 return GLYPH_VARIANT_USE_DEFAULT;
372 const NonDefaultUVS &nonDefaults = base+nonDefaultUVS;
373 i = nonDefaults.bsearch (codepoint);
376 *glyph = nonDefaults[i].glyphID;
377 return GLYPH_VARIANT_FOUND;
379 return GLYPH_VARIANT_NOT_FOUND;
382 inline int cmp (const hb_codepoint_t &variation_selector) const
384 return varSelector.cmp (variation_selector);
387 inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
389 TRACE_SANITIZE (this);
390 return_trace (c->check_struct (this) &&
391 defaultUVS.sanitize (c, base) &&
392 nonDefaultUVS.sanitize (c, base));
395 UINT24 varSelector; /* Variation selector. */
396 LOffsetTo<DefaultUVS>
397 defaultUVS; /* Offset to Default UVS Table. May be 0. */
398 LOffsetTo<NonDefaultUVS>
399 nonDefaultUVS; /* Offset to Non-Default UVS Table. May be 0. */
401 DEFINE_SIZE_STATIC (11);
404 struct CmapSubtableFormat14
406 inline glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint,
407 hb_codepoint_t variation_selector,
408 hb_codepoint_t *glyph) const
410 return record[record.bsearch(variation_selector)].get_glyph (codepoint, glyph, this);
413 inline bool sanitize (hb_sanitize_context_t *c) const
415 TRACE_SANITIZE (this);
416 return_trace (c->check_struct (this) &&
417 record.sanitize (c, this));
421 HBUINT16 format; /* Format number is set to 14. */
422 HBUINT32 lengthZ; /* Byte length of this subtable. */
423 SortedArrayOf<VariationSelectorRecord, HBUINT32>
424 record; /* Variation selector records; sorted
425 * in increasing order of `varSelector'. */
427 DEFINE_SIZE_ARRAY (10, record);
432 /* Note: We intentionally do NOT implement subtable formats 2 and 8. */
434 inline bool get_glyph (hb_codepoint_t codepoint,
435 hb_codepoint_t *glyph) const
438 case 0: return u.format0 .get_glyph (codepoint, glyph);
439 case 4: return u.format4 .get_glyph (codepoint, glyph);
440 case 6: return u.format6 .get_glyph (codepoint, glyph);
441 case 10: return u.format10.get_glyph (codepoint, glyph);
442 case 12: return u.format12.get_glyph (codepoint, glyph);
443 case 13: return u.format13.get_glyph (codepoint, glyph);
445 default: return false;
449 inline bool sanitize (hb_sanitize_context_t *c) const
451 TRACE_SANITIZE (this);
452 if (!u.format.sanitize (c)) return_trace (false);
454 case 0: return_trace (u.format0 .sanitize (c));
455 case 4: return_trace (u.format4 .sanitize (c));
456 case 6: return_trace (u.format6 .sanitize (c));
457 case 10: return_trace (u.format10.sanitize (c));
458 case 12: return_trace (u.format12.sanitize (c));
459 case 13: return_trace (u.format13.sanitize (c));
460 case 14: return_trace (u.format14.sanitize (c));
461 default:return_trace (true);
467 HBUINT16 format; /* Format identifier */
468 CmapSubtableFormat0 format0;
469 CmapSubtableFormat4 format4;
470 CmapSubtableFormat6 format6;
471 CmapSubtableFormat10 format10;
472 CmapSubtableFormat12 format12;
473 CmapSubtableFormat13 format13;
474 CmapSubtableFormat14 format14;
477 DEFINE_SIZE_UNION (2, format);
481 struct EncodingRecord
483 inline int cmp (const EncodingRecord &other) const
486 ret = platformID.cmp (other.platformID);
488 ret = encodingID.cmp (other.encodingID);
493 inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
495 TRACE_SANITIZE (this);
496 return_trace (c->check_struct (this) &&
497 subtable.sanitize (c, base));
500 HBUINT16 platformID; /* Platform ID. */
501 HBUINT16 encodingID; /* Platform-specific encoding ID. */
502 LOffsetTo<CmapSubtable>
503 subtable; /* Byte offset from beginning of table to the subtable for this encoding. */
505 DEFINE_SIZE_STATIC (8);
510 static const hb_tag_t tableTag = HB_OT_TAG_cmap;
512 inline bool sanitize (hb_sanitize_context_t *c) const
514 TRACE_SANITIZE (this);
515 return_trace (c->check_struct (this) &&
516 likely (version == 0) &&
517 encodingRecord.sanitize (c, this));
520 inline bool populate_groups (hb_subset_plan_t *plan,
521 hb_prealloced_array_t<CmapSubtableLongGroup> *groups) const
523 CmapSubtableLongGroup *group = nullptr;
524 for (unsigned int i = 0; i < plan->codepoints.len; i++) {
526 hb_codepoint_t cp = plan->codepoints[i];
527 if (!group || cp - 1 != group->endCharCode)
529 group = groups->push ();
530 group->startCharCode.set (cp);
531 group->endCharCode.set (cp);
532 hb_codepoint_t new_gid;
533 if (unlikely (!hb_subset_plan_new_gid_for_codepoint (plan, cp, &new_gid)))
535 DEBUG_MSG(SUBSET, nullptr, "Unable to find new gid for %04x", cp);
538 group->glyphID.set (new_gid);
541 group->endCharCode.set (cp);
545 DEBUG_MSG(SUBSET, nullptr, "cmap");
546 for (unsigned int i = 0; i < groups->len; i++) {
547 CmapSubtableLongGroup& group = (*groups)[i];
548 DEBUG_MSG(SUBSET, nullptr, " %d: U+%04X-U+%04X, gid %d-%d", i, (uint32_t) group.startCharCode, (uint32_t) group.endCharCode, (uint32_t) group.glyphID, (uint32_t) group.glyphID + ((uint32_t) group.endCharCode - (uint32_t) group.startCharCode));
554 inline bool _subset (hb_prealloced_array_t<CmapSubtableLongGroup> &groups,
558 hb_serialize_context_t c (dest, dest_sz);
560 OT::cmap *cmap = c.start_serialize<OT::cmap> ();
561 if (unlikely (!c.extend_min (*cmap)))
566 cmap->version.set (0);
568 if (unlikely (!cmap->encodingRecord.serialize (&c, /* numTables */ 1))) return false;
570 EncodingRecord &rec = cmap->encodingRecord[0];
571 rec.platformID.set (3); // Windows
572 rec.encodingID.set (10); // Unicode UCS-4
574 /* capture offset to subtable */
575 CmapSubtable &subtable = rec.subtable.serialize (&c, cmap);
577 subtable.u.format.set (12);
579 CmapSubtableFormat12 &format12 = subtable.u.format12;
580 if (unlikely (!c.extend_min (format12))) return false;
582 format12.format.set (12);
583 format12.reservedZ.set (0);
584 format12.lengthZ.set (16 + 12 * groups.len);
586 if (unlikely (!format12.serialize (&c, groups))) return false;
593 inline bool subset (hb_subset_plan_t *plan) const
595 hb_auto_array_t<CmapSubtableLongGroup> groups;
597 if (unlikely (!populate_groups (plan, &groups))) return false;
599 // We now know how big our blob needs to be
600 // TODO use APIs from the structs to get size?
601 size_t dest_sz = 4 // header
602 + 8 // 1 EncodingRecord
603 + 16 // Format 12 header
604 + 12 * groups.len; // SequentialMapGroup records
605 void *dest = malloc (dest_sz);
606 if (unlikely (!dest)) {
607 DEBUG_MSG(SUBSET, nullptr, "Unable to alloc %lu for cmap subset output", (unsigned long) dest_sz);
611 if (unlikely (!_subset (groups, dest_sz, dest)))
617 // all done, write the blob into dest
618 hb_blob_t *cmap_prime = hb_blob_create ((const char *)dest,
620 HB_MEMORY_MODE_READONLY,
623 bool result = hb_subset_plan_add_table (plan, HB_OT_TAG_cmap, cmap_prime);
624 hb_blob_destroy (cmap_prime);
630 inline void init (hb_face_t *face)
632 this->blob = OT::Sanitizer<OT::cmap>().sanitize (face->reference_table (HB_OT_TAG_cmap));
633 const OT::cmap *cmap = OT::Sanitizer<OT::cmap>::lock_instance (this->blob);
634 const OT::CmapSubtable *subtable = nullptr;
635 const OT::CmapSubtableFormat14 *subtable_uvs = nullptr;
638 /* 32-bit subtables. */
639 if (!subtable) subtable = cmap->find_subtable (3, 10);
640 if (!subtable) subtable = cmap->find_subtable (0, 6);
641 if (!subtable) subtable = cmap->find_subtable (0, 4);
642 /* 16-bit subtables. */
643 if (!subtable) subtable = cmap->find_subtable (3, 1);
644 if (!subtable) subtable = cmap->find_subtable (0, 3);
645 if (!subtable) subtable = cmap->find_subtable (0, 2);
646 if (!subtable) subtable = cmap->find_subtable (0, 1);
647 if (!subtable) subtable = cmap->find_subtable (0, 0);
650 subtable = cmap->find_subtable (3, 0);
651 if (subtable) symbol = true;
654 if (!subtable) subtable = &OT::Null(OT::CmapSubtable);
659 const OT::CmapSubtable *st = cmap->find_subtable (0, 5);
660 if (st && st->u.format == 14)
661 subtable_uvs = &st->u.format14;
664 if (!subtable_uvs) subtable_uvs = &OT::Null(OT::CmapSubtableFormat14);
666 this->uvs_table = subtable_uvs;
668 this->get_glyph_data = subtable;
669 if (unlikely (symbol))
670 this->get_glyph_func = get_glyph_from_symbol<OT::CmapSubtable>;
672 switch (subtable->u.format) {
673 /* Accelerate format 4 and format 12. */
674 default: this->get_glyph_func = get_glyph_from<OT::CmapSubtable>; break;
675 case 12: this->get_glyph_func = get_glyph_from<OT::CmapSubtableFormat12>; break;
678 this->format4_accel.init (&subtable->u.format4);
679 this->get_glyph_data = &this->format4_accel;
680 this->get_glyph_func = this->format4_accel.get_glyph_func;
686 inline void fini (void)
688 hb_blob_destroy (this->blob);
691 inline bool get_nominal_glyph (hb_codepoint_t unicode,
692 hb_codepoint_t *glyph) const
694 return this->get_glyph_func (this->get_glyph_data, unicode, glyph);
697 inline bool get_variation_glyph (hb_codepoint_t unicode,
698 hb_codepoint_t variation_selector,
699 hb_codepoint_t *glyph) const
701 switch (this->uvs_table->get_glyph_variant (unicode,
705 case OT::GLYPH_VARIANT_NOT_FOUND: return false;
706 case OT::GLYPH_VARIANT_FOUND: return true;
707 case OT::GLYPH_VARIANT_USE_DEFAULT: break;
710 return get_nominal_glyph (unicode, glyph);
714 typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj,
715 hb_codepoint_t codepoint,
716 hb_codepoint_t *glyph);
718 template <typename Type>
719 static inline bool get_glyph_from (const void *obj,
720 hb_codepoint_t codepoint,
721 hb_codepoint_t *glyph)
723 const Type *typed_obj = (const Type *) obj;
724 return typed_obj->get_glyph (codepoint, glyph);
727 template <typename Type>
728 static inline bool get_glyph_from_symbol (const void *obj,
729 hb_codepoint_t codepoint,
730 hb_codepoint_t *glyph)
732 const Type *typed_obj = (const Type *) obj;
733 if (likely (typed_obj->get_glyph (codepoint, glyph)))
736 if (codepoint <= 0x00FFu)
738 /* For symbol-encoded OpenType fonts, we duplicate the
739 * U+F000..F0FF range at U+0000..U+00FF. That's what
740 * Windows seems to do, and that's hinted about at:
741 * http://www.microsoft.com/typography/otspec/recom.htm
742 * under "Non-Standard (Symbol) Fonts". */
743 return typed_obj->get_glyph (0xF000u + codepoint, glyph);
750 hb_cmap_get_glyph_func_t get_glyph_func;
751 const void *get_glyph_data;
752 OT::CmapSubtableFormat4::accelerator_t format4_accel;
754 const OT::CmapSubtableFormat14 *uvs_table;
760 inline const CmapSubtable *find_subtable (unsigned int platform_id,
761 unsigned int encoding_id) const
764 key.platformID.set (platform_id);
765 key.encodingID.set (encoding_id);
767 /* Note: We can use bsearch, but since it has no performance
768 * implications, we use lsearch and as such accept fonts with
769 * unsorted subtable list. */
770 int result = encodingRecord./*bsearch*/lsearch (key);
771 if (result == -1 || !encodingRecord[result].subtable)
774 return &(this+encodingRecord[result].subtable);
778 HBUINT16 version; /* Table version number (0). */
779 SortedArrayOf<EncodingRecord>
780 encodingRecord; /* Encoding tables. */
782 DEFINE_SIZE_ARRAY (4, encodingRecord);
789 #endif /* HB_OT_CMAP_TABLE_HH */