tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

silf.cc (35589B)


      1 // Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "silf.h"
      6 
      7 #include "name.h"
      8 #include "mozilla/Compression.h"
      9 #include <cmath>
     10 #include <memory>
     11 
     12 namespace ots {
     13 
     14 bool OpenTypeSILF::Parse(const uint8_t* data, size_t length,
     15                         bool prevent_decompression) {
     16  Buffer table(data, length);
     17 
     18  if (!table.ReadU32(&this->version)) {
     19    return DropGraphite("Failed to read version");
     20  }
     21  if (this->version >> 16 != 1 &&
     22      this->version >> 16 != 2 &&
     23      this->version >> 16 != 3 &&
     24      this->version >> 16 != 4 &&
     25      this->version >> 16 != 5) {
     26    return DropGraphite("Unsupported table version: %u", this->version >> 16);
     27  }
     28  if (this->version >> 16 >= 3 && !table.ReadU32(&this->compHead)) {
     29    return DropGraphite("Failed to read compHead");
     30  }
     31  if (this->version >> 16 >= 5) {
     32    switch ((this->compHead & SCHEME) >> 27) {
     33      case 0:  // uncompressed
     34        break;
     35      case 1: {  // lz4
     36        if (prevent_decompression) {
     37          return DropGraphite("Illegal nested compression");
     38        }
     39        size_t decompressed_size = this->compHead & FULL_SIZE;
     40        if (decompressed_size < length) {
     41          return DropGraphite("Decompressed size is less than compressed size");
     42        }
     43        if (decompressed_size == 0) {
     44          return DropGraphite("Decompressed size is set to 0");
     45        }
     46        // decompressed table must be <= OTS_MAX_DECOMPRESSED_TABLE_SIZE
     47        if (decompressed_size > OTS_MAX_DECOMPRESSED_TABLE_SIZE) {
     48          return DropGraphite("Decompressed size exceeds %gMB: %gMB",
     49                              OTS_MAX_DECOMPRESSED_TABLE_SIZE / (1024.0 * 1024.0),
     50                              decompressed_size / (1024.0 * 1024.0));
     51        }
     52        std::unique_ptr<uint8_t> decompressed(new uint8_t[decompressed_size]());
     53        size_t outputSize = 0;
     54        bool ret = mozilla::Compression::LZ4::decompressPartial(
     55            reinterpret_cast<const char*>(data + table.offset()),
     56            table.remaining(),  // input buffer size (input size + padding)
     57            reinterpret_cast<char*>(decompressed.get()),
     58            decompressed_size,  // target output size
     59            &outputSize);   // return output size
     60        if (!ret || outputSize != decompressed_size) {
     61          return DropGraphite("Decompression failed");
     62        }
     63        return this->Parse(decompressed.get(), decompressed_size, true);
     64      }
     65      default:
     66        return DropGraphite("Unknown compression scheme");
     67    }
     68  }
     69  if (!table.ReadU16(&this->numSub)) {
     70    return DropGraphite("Failed to read numSub");
     71  }
     72  if (this->version >> 16 >= 2 && !table.ReadU16(&this->reserved)) {
     73    return DropGraphite("Failed to read reserved");
     74  }
     75  if (this->version >> 16 >= 2 && this->reserved != 0) {
     76    Warning("Nonzero reserved");
     77  }
     78 
     79  unsigned long last_offset = 0;
     80  //this->offset.resize(this->numSub);
     81  for (unsigned i = 0; i < this->numSub; ++i) {
     82    this->offset.emplace_back();
     83    if (!table.ReadU32(&this->offset[i]) || this->offset[i] < last_offset) {
     84      return DropGraphite("Failed to read offset[%u]", i);
     85    }
     86    last_offset = this->offset[i];
     87  }
     88 
     89  for (unsigned i = 0; i < this->numSub; ++i) {
     90    if (table.offset() != this->offset[i]) {
     91      return DropGraphite("Offset check failed for tables[%lu]", i);
     92    }
     93    SILSub subtable(this);
     94    if (!subtable.ParsePart(table)) {
     95      return DropGraphite("Failed to read tables[%u]", i);
     96    }
     97    tables.push_back(subtable);
     98  }
     99 
    100  if (table.remaining()) {
    101    return Warning("%zu bytes unparsed", table.remaining());
    102  }
    103  return true;
    104 }
    105 
    106 bool OpenTypeSILF::Serialize(OTSStream* out) {
    107  if (!out->WriteU32(this->version) ||
    108      (this->version >> 16 >= 3 && !out->WriteU32(this->compHead)) ||
    109      !out->WriteU16(this->numSub) ||
    110      (this->version >> 16 >= 2 && !out->WriteU16(this->reserved)) ||
    111      !SerializeParts(this->offset, out) ||
    112      !SerializeParts(this->tables, out)) {
    113    return Error("Failed to write table");
    114  }
    115  return true;
    116 }
    117 
    118 bool OpenTypeSILF::SILSub::ParsePart(Buffer& table) {
    119  size_t init_offset = table.offset();
    120  if (parent->version >> 16 >= 3) {
    121    if (!table.ReadU32(&this->ruleVersion)) {
    122      return parent->Error("SILSub: Failed to read ruleVersion");
    123    }
    124    if (!table.ReadU16(&this->passOffset)) {
    125      return parent->Error("SILSub: Failed to read passOffset");
    126    }
    127    if (!table.ReadU16(&this->pseudosOffset)) {
    128      return parent->Error("SILSub: Failed to read pseudosOffset");
    129    }
    130  }
    131  if (!table.ReadU16(&this->maxGlyphID)) {
    132    return parent->Error("SILSub: Failed to read maxGlyphID");
    133  }
    134  if (!table.ReadS16(&this->extraAscent)) {
    135    return parent->Error("SILSub: Failed to read extraAscent");
    136  }
    137  if (!table.ReadS16(&this->extraDescent)) {
    138    return parent->Error("SILSub: Failed to read extraDescent");
    139  }
    140  if (!table.ReadU8(&this->numPasses)) {
    141    return parent->Error("SILSub: Failed to read numPasses");
    142  }
    143  if (!table.ReadU8(&this->iSubst) || this->iSubst > this->numPasses) {
    144    return parent->Error("SILSub: Failed to read valid iSubst");
    145  }
    146  if (!table.ReadU8(&this->iPos) || this->iPos > this->numPasses) {
    147    return parent->Error("SILSub: Failed to read valid iPos");
    148  }
    149  if (!table.ReadU8(&this->iJust) || this->iJust > this->numPasses) {
    150    return parent->Error("SILSub: Failed to read valid iJust");
    151  }
    152  if (!table.ReadU8(&this->iBidi) ||
    153      !(iBidi == 0xFF || this->iBidi <= this->iPos)) {
    154    return parent->Error("SILSub: Failed to read valid iBidi");
    155  }
    156  if (!table.ReadU8(&this->flags)) {
    157    return parent->Error("SILSub: Failed to read flags");
    158    // checks omitted
    159  }
    160  if (!table.ReadU8(&this->maxPreContext)) {
    161    return parent->Error("SILSub: Failed to read maxPreContext");
    162  }
    163  if (!table.ReadU8(&this->maxPostContext)) {
    164    return parent->Error("SILSub: Failed to read maxPostContext");
    165  }
    166  if (!table.ReadU8(&this->attrPseudo)) {
    167    return parent->Error("SILSub: Failed to read attrPseudo");
    168  }
    169  if (!table.ReadU8(&this->attrBreakWeight)) {
    170    return parent->Error("SILSub: Failed to read attrBreakWeight");
    171  }
    172  if (!table.ReadU8(&this->attrDirectionality)) {
    173    return parent->Error("SILSub: Failed to read attrDirectionality");
    174  }
    175  if (parent->version >> 16 >= 2) {
    176    if (!table.ReadU8(&this->attrMirroring)) {
    177      return parent->Error("SILSub: Failed to read attrMirroring");
    178    }
    179    if (!table.ReadU8(&this->attrSkipPasses)) {
    180      return parent->Error("SILSub: Failed to read attrSkipPasses");
    181    }
    182 
    183    if (!table.ReadU8(&this->numJLevels)) {
    184      return parent->Error("SILSub: Failed to read numJLevels");
    185    }
    186    //this->jLevels.resize(this->numJLevels, parent);
    187    for (unsigned i = 0; i < this->numJLevels; ++i) {
    188      this->jLevels.emplace_back(parent);
    189      if (!this->jLevels[i].ParsePart(table)) {
    190        return parent->Error("SILSub: Failed to read jLevels[%u]", i);
    191      }
    192    }
    193  }
    194 
    195  if (!table.ReadU16(&this->numLigComp)) {
    196    return parent->Error("SILSub: Failed to read numLigComp");
    197  }
    198  if (!table.ReadU8(&this->numUserDefn)) {
    199    return parent->Error("SILSub: Failed to read numUserDefn");
    200  }
    201  if (!table.ReadU8(&this->maxCompPerLig)) {
    202    return parent->Error("SILSub: Failed to read maxCompPerLig");
    203  }
    204  if (!table.ReadU8(&this->direction)) {
    205    return parent->Error("SILSub: Failed to read direction");
    206  }
    207  if (!table.ReadU8(&this->attrCollisions)) {
    208    return parent->Error("SILSub: Failed to read attrCollisions");
    209  }
    210  if (parent->version < 0x40001 && this->attrCollisions != 0) {
    211    parent->Warning("SILSub: Nonzero attrCollisions (reserved before v4.1)");
    212  }
    213  if (!table.ReadU8(&this->reserved4)) {
    214    return parent->Error("SILSub: Failed to read reserved4");
    215  }
    216  if (this->reserved4 != 0) {
    217    parent->Warning("SILSub: Nonzero reserved4");
    218  }
    219  if (!table.ReadU8(&this->reserved5)) {
    220    return parent->Error("SILSub: Failed to read reserved5");
    221  }
    222  if (this->reserved5 != 0) {
    223    parent->Warning("SILSub: Nonzero reserved5");
    224  }
    225  if (parent->version >> 16 >= 2) {
    226    if (!table.ReadU8(&this->reserved6)) {
    227      return parent->Error("SILSub: Failed to read reserved6");
    228    }
    229    if (this->reserved6 != 0) {
    230      parent->Warning("SILSub: Nonzero reserved6");
    231    }
    232 
    233    if (!table.ReadU8(&this->numCritFeatures)) {
    234      return parent->Error("SILSub: Failed to read numCritFeatures");
    235    }
    236    //this->critFeatures.resize(this->numCritFeatures);
    237    for (unsigned i = 0; i < this->numCritFeatures; ++i) {
    238      this->critFeatures.emplace_back();
    239      if (!table.ReadU16(&this->critFeatures[i])) {
    240        return parent->Error("SILSub: Failed to read critFeatures[%u]", i);
    241      }
    242    }
    243 
    244    if (!table.ReadU8(&this->reserved7)) {
    245      return parent->Error("SILSub: Failed to read reserved7");
    246    }
    247    if (this->reserved7 != 0) {
    248      parent->Warning("SILSub: Nonzero reserved7");
    249    }
    250  }
    251 
    252  if (!table.ReadU8(&this->numScriptTag)) {
    253    return parent->Error("SILSub: Failed to read numScriptTag");
    254  }
    255  //this->scriptTag.resize(this->numScriptTag);
    256  for (unsigned i = 0; i < this->numScriptTag; ++i) {
    257    this->scriptTag.emplace_back();
    258    if (!table.ReadU32(&this->scriptTag[i])) {
    259      return parent->Error("SILSub: Failed to read scriptTag[%u]", i);
    260    }
    261  }
    262 
    263  if (!table.ReadU16(&this->lbGID)) {
    264    return parent->Error("SILSub: Failed to read lbGID");
    265  }
    266  if (this->lbGID > this->maxGlyphID) {
    267    parent->Warning("SILSub: lbGID %u outside range 0..%u, replaced with 0",
    268                    this->lbGID, this->maxGlyphID);
    269    this->lbGID = 0;
    270  }
    271 
    272  if (parent->version >> 16 >= 3 &&
    273      table.offset() != init_offset + this->passOffset) {
    274    return parent->Error("SILSub: passOffset check failed");
    275  }
    276  unsigned long last_oPass = 0;
    277  //this->oPasses.resize(static_cast<unsigned>(this->numPasses) + 1);
    278  for (unsigned i = 0; i <= this->numPasses; ++i) {
    279    this->oPasses.emplace_back();
    280    if (!table.ReadU32(&this->oPasses[i]) || this->oPasses[i] < last_oPass) {
    281      return false;
    282    }
    283    last_oPass = this->oPasses[i];
    284  }
    285 
    286  if (parent->version >> 16 >= 3 &&
    287      table.offset() != init_offset + this->pseudosOffset) {
    288    return parent->Error("SILSub: pseudosOffset check failed");
    289  }
    290  if (!table.ReadU16(&this->numPseudo)) {
    291    return parent->Error("SILSub: Failed to read numPseudo");
    292  }
    293 
    294  // The following three fields are deprecated and ignored. We fix them up here
    295  // just for internal consistency, but the Graphite engine doesn't care.
    296  if (!table.ReadU16(&this->searchPseudo) ||
    297      !table.ReadU16(&this->pseudoSelector) ||
    298      !table.ReadU16(&this->pseudoShift)) {
    299    return parent->Error("SILSub: Failed to read searchPseudo..pseudoShift");
    300  }
    301  if (this->numPseudo == 0) {
    302    if (this->searchPseudo != 0 || this->pseudoSelector != 0 || this->pseudoShift != 0) {
    303      this->searchPseudo = this->pseudoSelector = this->pseudoShift = 0;
    304    }
    305  } else {
    306    unsigned floorLog2 = std::floor(std::log2(this->numPseudo));
    307    if (this->searchPseudo != 6 * (unsigned)std::pow(2, floorLog2) ||
    308        this->pseudoSelector != floorLog2 ||
    309        this->pseudoShift != 6 * this->numPseudo - this->searchPseudo) {
    310      this->searchPseudo = 6 * (unsigned)std::pow(2, floorLog2);
    311      this->pseudoSelector = floorLog2;
    312      this->pseudoShift = 6 * this->numPseudo - this->searchPseudo;
    313    }
    314  }
    315 
    316  //this->pMaps.resize(this->numPseudo, parent);
    317  for (unsigned i = 0; i < numPseudo; i++) {
    318    this->pMaps.emplace_back(parent);
    319    if (!this->pMaps[i].ParsePart(table)) {
    320      return parent->Error("SILSub: Failed to read pMaps[%u]", i);
    321    }
    322  }
    323 
    324  if (!this->classes.ParsePart(table)) {
    325    return parent->Error("SILSub: Failed to read classes");
    326  }
    327 
    328  //this->passes.resize(this->numPasses, parent);
    329  for (unsigned i = 0; i < this->numPasses; ++i) {
    330    this->passes.emplace_back(parent);
    331    if (table.offset() != init_offset + this->oPasses[i]) {
    332      return parent->Error("SILSub: Offset check failed for passes[%u]", i);
    333    }
    334    if (!this->passes[i].ParsePart(table, init_offset, this->oPasses[i+1])) {
    335      return parent->Error("SILSub: Failed to read passes[%u]", i);
    336    }
    337  }
    338  return true;
    339 }
    340 
    341 bool OpenTypeSILF::SILSub::SerializePart(OTSStream* out) const {
    342  if ((parent->version >> 16 >= 3 &&
    343       (!out->WriteU32(this->ruleVersion) ||
    344        !out->WriteU16(this->passOffset) ||
    345        !out->WriteU16(this->pseudosOffset))) ||
    346      !out->WriteU16(this->maxGlyphID) ||
    347      !out->WriteS16(this->extraAscent) ||
    348      !out->WriteS16(this->extraDescent) ||
    349      !out->WriteU8(this->numPasses) ||
    350      !out->WriteU8(this->iSubst) ||
    351      !out->WriteU8(this->iPos) ||
    352      !out->WriteU8(this->iJust) ||
    353      !out->WriteU8(this->iBidi) ||
    354      !out->WriteU8(this->flags) ||
    355      !out->WriteU8(this->maxPreContext) ||
    356      !out->WriteU8(this->maxPostContext) ||
    357      !out->WriteU8(this->attrPseudo) ||
    358      !out->WriteU8(this->attrBreakWeight) ||
    359      !out->WriteU8(this->attrDirectionality) ||
    360      (parent->version >> 16 >= 2 &&
    361       (!out->WriteU8(this->attrMirroring) ||
    362        !out->WriteU8(this->attrSkipPasses) ||
    363        !out->WriteU8(this->numJLevels) ||
    364        !SerializeParts(this->jLevels, out))) ||
    365      !out->WriteU16(this->numLigComp) ||
    366      !out->WriteU8(this->numUserDefn) ||
    367      !out->WriteU8(this->maxCompPerLig) ||
    368      !out->WriteU8(this->direction) ||
    369      !out->WriteU8(this->attrCollisions) ||
    370      !out->WriteU8(this->reserved4) ||
    371      !out->WriteU8(this->reserved5) ||
    372      (parent->version >> 16 >= 2 &&
    373       (!out->WriteU8(this->reserved6) ||
    374        !out->WriteU8(this->numCritFeatures) ||
    375        !SerializeParts(this->critFeatures, out) ||
    376        !out->WriteU8(this->reserved7))) ||
    377      !out->WriteU8(this->numScriptTag) ||
    378      !SerializeParts(this->scriptTag, out) ||
    379      !out->WriteU16(this->lbGID) ||
    380      !SerializeParts(this->oPasses, out) ||
    381      !out->WriteU16(this->numPseudo) ||
    382      !out->WriteU16(this->searchPseudo) ||
    383      !out->WriteU16(this->pseudoSelector) ||
    384      !out->WriteU16(this->pseudoShift) ||
    385      !SerializeParts(this->pMaps, out) ||
    386      !this->classes.SerializePart(out) ||
    387      !SerializeParts(this->passes, out)) {
    388    return parent->Error("SILSub: Failed to write");
    389  }
    390  return true;
    391 }
    392 
    393 bool OpenTypeSILF::SILSub::
    394 JustificationLevel::ParsePart(Buffer& table) {
    395  if (!table.ReadU8(&this->attrStretch)) {
    396    return parent->Error("JustificationLevel: Failed to read attrStretch");
    397  }
    398  if (!table.ReadU8(&this->attrShrink)) {
    399    return parent->Error("JustificationLevel: Failed to read attrShrink");
    400  }
    401  if (!table.ReadU8(&this->attrStep)) {
    402    return parent->Error("JustificationLevel: Failed to read attrStep");
    403  }
    404  if (!table.ReadU8(&this->attrWeight)) {
    405    return parent->Error("JustificationLevel: Failed to read attrWeight");
    406  }
    407  if (!table.ReadU8(&this->runto)) {
    408    return parent->Error("JustificationLevel: Failed to read runto");
    409  }
    410  if (!table.ReadU8(&this->reserved)) {
    411    return parent->Error("JustificationLevel: Failed to read reserved");
    412  }
    413  if (this->reserved != 0) {
    414    parent->Warning("JustificationLevel: Nonzero reserved");
    415  }
    416  if (!table.ReadU8(&this->reserved2)) {
    417    return parent->Error("JustificationLevel: Failed to read reserved2");
    418  }
    419  if (this->reserved2 != 0) {
    420    parent->Warning("JustificationLevel: Nonzero reserved2");
    421  }
    422  if (!table.ReadU8(&this->reserved3)) {
    423    return parent->Error("JustificationLevel: Failed to read reserved3");
    424  }
    425  if (this->reserved3 != 0) {
    426    parent->Warning("JustificationLevel: Nonzero reserved3");
    427  }
    428  return true;
    429 }
    430 
    431 bool OpenTypeSILF::SILSub::
    432 JustificationLevel::SerializePart(OTSStream* out) const {
    433  if (!out->WriteU8(this->attrStretch) ||
    434      !out->WriteU8(this->attrShrink) ||
    435      !out->WriteU8(this->attrStep) ||
    436      !out->WriteU8(this->attrWeight) ||
    437      !out->WriteU8(this->runto) ||
    438      !out->WriteU8(this->reserved) ||
    439      !out->WriteU8(this->reserved2) ||
    440      !out->WriteU8(this->reserved3)) {
    441    return parent->Error("JustificationLevel: Failed to write");
    442  }
    443  return true;
    444 }
    445 
    446 bool OpenTypeSILF::SILSub::
    447 PseudoMap::ParsePart(Buffer& table) {
    448  if (parent->version >> 16 >= 2 && !table.ReadU32(&this->unicode)) {
    449    return parent->Error("PseudoMap: Failed to read unicode");
    450  }
    451  if (parent->version >> 16 == 1) {
    452    uint16_t unicode;
    453    if (!table.ReadU16(&unicode)) {
    454      return parent->Error("PseudoMap: Failed to read unicode");
    455    }
    456    this->unicode = unicode;
    457  }
    458  if (!table.ReadU16(&this->nPseudo)) {
    459    return parent->Error("PseudoMap: Failed to read nPseudo");
    460  }
    461  return true;
    462 }
    463 
    464 bool OpenTypeSILF::SILSub::
    465 PseudoMap::SerializePart(OTSStream* out) const {
    466  if ((parent->version >> 16 >= 2 && !out->WriteU32(this->unicode)) ||
    467      (parent->version >> 16 == 1 &&
    468       !out->WriteU16(static_cast<uint16_t>(this->unicode))) ||
    469      !out->WriteU16(this->nPseudo)) {
    470    return parent->Error("PseudoMap: Failed to write");
    471  }
    472  return true;
    473 }
    474 
    475 bool OpenTypeSILF::SILSub::
    476 ClassMap::ParsePart(Buffer& table) {
    477  size_t init_offset = table.offset();
    478  if (!table.ReadU16(&this->numClass)) {
    479    return parent->Error("ClassMap: Failed to read numClass");
    480  }
    481  if (!table.ReadU16(&this->numLinear) || this->numLinear > this->numClass) {
    482    return parent->Error("ClassMap: Failed to read valid numLinear");
    483  }
    484 
    485  //this->oClass.resize(static_cast<unsigned long>(this->numClass) + 1);
    486  if (parent->version >> 16 >= 4) {
    487    unsigned long last_oClass = 0;
    488    for (unsigned long i = 0; i <= this->numClass; ++i) {
    489      this->oClass.emplace_back();
    490      if (!table.ReadU32(&this->oClass[i]) || this->oClass[i] < last_oClass) {
    491        return parent->Error("ClassMap: Failed to read oClass[%lu]", i);
    492      }
    493      last_oClass = this->oClass[i];
    494    }
    495  }
    496  if (parent->version >> 16 < 4) {
    497    unsigned last_oClass = 0;
    498    for (unsigned long i = 0; i <= this->numClass; ++i) {
    499      uint16_t offset;
    500      if (!table.ReadU16(&offset) || offset < last_oClass) {
    501        return parent->Error("ClassMap: Failed to read oClass[%lu]", i);
    502      }
    503      last_oClass = offset;
    504      this->oClass.push_back(static_cast<uint32_t>(offset));
    505    }
    506  }
    507 
    508  if (table.offset() - init_offset > this->oClass[this->numLinear]) {
    509    return parent->Error("ClassMap: Failed to calculate length of glyphs");
    510  }
    511  unsigned long glyphs_len = (this->oClass[this->numLinear] -
    512                             (table.offset() - init_offset))/2;
    513  //this->glyphs.resize(glyphs_len);
    514  for (unsigned long i = 0; i < glyphs_len; ++i) {
    515    this->glyphs.emplace_back();
    516    if (!table.ReadU16(&this->glyphs[i])) {
    517      return parent->Error("ClassMap: Failed to read glyphs[%lu]", i);
    518    }
    519  }
    520 
    521  unsigned lookups_len = this->numClass - this->numLinear;
    522    // this->numLinear <= this->numClass
    523  //this->lookups.resize(lookups_len, parent);
    524  for (unsigned i = 0; i < lookups_len; ++i) {
    525    this->lookups.emplace_back(parent);
    526    if (table.offset() != init_offset + oClass[this->numLinear + i]) {
    527      return parent->Error("ClassMap: Offset check failed for lookups[%u]", i);
    528    }
    529    if (!this->lookups[i].ParsePart(table)) {
    530      return parent->Error("ClassMap: Failed to read lookups[%u]", i);
    531    }
    532  }
    533  return true;
    534 }
    535 
    536 bool OpenTypeSILF::SILSub::
    537 ClassMap::SerializePart(OTSStream* out) const {
    538  if (!out->WriteU16(this->numClass) ||
    539      !out->WriteU16(this->numLinear) ||
    540      (parent->version >> 16 >= 4 && !SerializeParts(this->oClass, out)) ||
    541      (parent->version >> 16 < 4 &&
    542       ![&] {
    543         for (uint32_t offset : this->oClass) {
    544           if (!out->WriteU16(static_cast<uint16_t>(offset))) {
    545             return false;
    546           }
    547         }
    548         return true;
    549       }()) ||
    550      !SerializeParts(this->glyphs, out) ||
    551      !SerializeParts(this->lookups, out)) {
    552    return parent->Error("ClassMap: Failed to write");
    553  }
    554  return true;
    555 }
    556 
    557 bool OpenTypeSILF::SILSub::ClassMap::
    558 LookupClass::ParsePart(Buffer& table) {
    559  if (!table.ReadU16(&this->numIDs)) {
    560    return parent->Error("LookupClass: Failed to read numIDs");
    561  }
    562  if (!table.ReadU16(&this->searchRange) ||
    563      !table.ReadU16(&this->entrySelector) ||
    564      !table.ReadU16(&this->rangeShift)) {
    565    return parent->Error("LookupClass: Failed to read searchRange..rangeShift");
    566  }
    567  if (this->numIDs == 0) {
    568    if (this->searchRange != 0 || this->entrySelector != 0 || this->rangeShift != 0) {
    569      parent->Warning("LookupClass: Correcting binary-search header for zero-length LookupPair list");
    570      this->searchRange = this->entrySelector = this->rangeShift = 0;
    571    }
    572  } else {
    573    unsigned floorLog2 = std::floor(std::log2(this->numIDs));
    574    if (this->searchRange != (unsigned)std::pow(2, floorLog2) ||
    575        this->entrySelector != floorLog2 ||
    576        this->rangeShift != this->numIDs - this->searchRange) {
    577      parent->Warning("LookupClass: Correcting binary-search header for LookupPair list");
    578      this->searchRange = (unsigned)std::pow(2, floorLog2);
    579      this->entrySelector = floorLog2;
    580      this->rangeShift = this->numIDs - this->searchRange;
    581    }
    582  }
    583 
    584  //this->lookups.resize(this->numIDs, parent);
    585  for (unsigned i = 0; i < numIDs; ++i) {
    586    this->lookups.emplace_back(parent);
    587    if (!this->lookups[i].ParsePart(table)) {
    588      return parent->Error("LookupClass: Failed to read lookups[%u]", i);
    589    }
    590  }
    591  return true;
    592 }
    593 
    594 bool OpenTypeSILF::SILSub::ClassMap::
    595 LookupClass::SerializePart(OTSStream* out) const {
    596  if (!out->WriteU16(this->numIDs) ||
    597      !out->WriteU16(this->searchRange) ||
    598      !out->WriteU16(this->entrySelector) ||
    599      !out->WriteU16(this->rangeShift) ||
    600      !SerializeParts(this->lookups, out)) {
    601    return parent->Error("LookupClass: Failed to write");
    602  }
    603  return true;
    604 }
    605 
    606 bool OpenTypeSILF::SILSub::ClassMap::LookupClass::
    607 LookupPair::ParsePart(Buffer& table) {
    608  if (!table.ReadU16(&this->glyphId)) {
    609    return parent->Error("LookupPair: Failed to read glyphId");
    610  }
    611  if (!table.ReadU16(&this->index)) {
    612    return parent->Error("LookupPair: Failed to read index");
    613  }
    614  return true;
    615 }
    616 
    617 bool OpenTypeSILF::SILSub::ClassMap::LookupClass::
    618 LookupPair::SerializePart(OTSStream* out) const {
    619  if (!out->WriteU16(this->glyphId) ||
    620      !out->WriteU16(this->index)) {
    621    return parent->Error("LookupPair: Failed to write");
    622  }
    623  return true;
    624 }
    625 
    626 bool OpenTypeSILF::SILSub::
    627 SILPass::ParsePart(Buffer& table, const size_t SILSub_init_offset,
    628                                  const size_t next_pass_offset) {
    629  size_t init_offset = table.offset();
    630  if (!table.ReadU8(&this->flags)) {
    631    return parent->Error("SILPass: Failed to read flags");
    632      // checks omitted
    633  }
    634  if (!table.ReadU8(&this->maxRuleLoop)) {
    635    return parent->Error("SILPass: Failed to read valid maxRuleLoop");
    636  }
    637  if (!table.ReadU8(&this->maxRuleContext)) {
    638    return parent->Error("SILPass: Failed to read maxRuleContext");
    639  }
    640  if (!table.ReadU8(&this->maxBackup)) {
    641    return parent->Error("SILPass: Failed to read maxBackup");
    642  }
    643  if (!table.ReadU16(&this->numRules)) {
    644    return parent->Error("SILPass: Failed to read numRules");
    645  }
    646  if (parent->version >> 16 >= 2) {
    647    if (!table.ReadU16(&this->fsmOffset)) {
    648      return parent->Error("SILPass: Failed to read fsmOffset");
    649    }
    650    if (!table.ReadU32(&this->pcCode) ||
    651        (parent->version >= 3 && this->pcCode < this->fsmOffset)) {
    652      return parent->Error("SILPass: Failed to read pcCode");
    653    }
    654  }
    655  if (!table.ReadU32(&this->rcCode) ||
    656      (parent->version >> 16 >= 2 && this->rcCode < this->pcCode)) {
    657    return parent->Error("SILPass: Failed to read valid rcCode");
    658  }
    659  if (!table.ReadU32(&this->aCode) || this->aCode < this->rcCode) {
    660    return parent->Error("SILPass: Failed to read valid aCode");
    661  }
    662  if (!table.ReadU32(&this->oDebug) ||
    663      (this->oDebug && this->oDebug < this->aCode)) {
    664    return parent->Error("SILPass: Failed to read valid oDebug");
    665  }
    666  if (parent->version >> 16 >= 3 &&
    667      table.offset() != init_offset + this->fsmOffset) {
    668    return parent->Error("SILPass: fsmOffset check failed");
    669  }
    670  if (!table.ReadU16(&this->numRows) ||
    671      (this->oDebug && this->numRows < this->numRules)) {
    672    return parent->Error("SILPass: Failed to read valid numRows");
    673  }
    674  if (!table.ReadU16(&this->numTransitional)) {
    675    return parent->Error("SILPass: Failed to read numTransitional");
    676  }
    677  if (!table.ReadU16(&this->numSuccess)) {
    678    return parent->Error("SILPass: Failed to read numSuccess");
    679  }
    680  if (!table.ReadU16(&this->numColumns)) {
    681    return parent->Error("SILPass: Failed to read numColumns");
    682  }
    683  if (!table.ReadU16(&this->numRange)) {
    684    return parent->Error("SILPass: Failed to read numRange");
    685  }
    686 
    687  // The following three fields are deprecated and ignored. We fix them up here
    688  // just for internal consistency, but the Graphite engine doesn't care.
    689  if (!table.ReadU16(&this->searchRange) ||
    690      !table.ReadU16(&this->entrySelector) ||
    691      !table.ReadU16(&this->rangeShift)) {
    692    return parent->Error("SILPass: Failed to read searchRange..rangeShift");
    693  }
    694  if (this->numRange == 0) {
    695    if (this->searchRange != 0 || this->entrySelector != 0 || this->rangeShift != 0) {
    696      this->searchRange = this->entrySelector = this->rangeShift = 0;
    697    }
    698  } else {
    699    unsigned floorLog2 = std::floor(std::log2(this->numRange));
    700    if (this->searchRange != 6 * (unsigned)std::pow(2, floorLog2) ||
    701        this->entrySelector != floorLog2 ||
    702        this->rangeShift != 6 * this->numRange - this->searchRange) {
    703      this->searchRange = 6 * (unsigned)std::pow(2, floorLog2);
    704      this->entrySelector = floorLog2;
    705      this->rangeShift = 6 * this->numRange - this->searchRange;
    706    }
    707  }
    708 
    709  //this->ranges.resize(this->numRange, parent);
    710  for (unsigned i = 0 ; i < this->numRange; ++i) {
    711    this->ranges.emplace_back(parent);
    712    if (!this->ranges[i].ParsePart(table)) {
    713      return parent->Error("SILPass: Failed to read ranges[%u]", i);
    714    }
    715  }
    716  unsigned ruleMap_len = 0;  // maximum value in oRuleMap
    717  //this->oRuleMap.resize(static_cast<unsigned long>(this->numSuccess) + 1);
    718  for (unsigned long i = 0; i <= this->numSuccess; ++i) {
    719    this->oRuleMap.emplace_back();
    720    if (!table.ReadU16(&this->oRuleMap[i])) {
    721      return parent->Error("SILPass: Failed to read oRuleMap[%u]", i);
    722    }
    723    if (oRuleMap[i] > ruleMap_len) {
    724      ruleMap_len = oRuleMap[i];
    725    }
    726  }
    727 
    728  //this->ruleMap.resize(ruleMap_len);
    729  for (unsigned i = 0; i < ruleMap_len; ++i) {
    730    this->ruleMap.emplace_back();
    731    if (!table.ReadU16(&this->ruleMap[i])) {
    732      return parent->Error("SILPass: Failed to read ruleMap[%u]", i);
    733    }
    734  }
    735 
    736  if (!table.ReadU8(&this->minRulePreContext)) {
    737    return parent->Error("SILPass: Failed to read minRulePreContext");
    738  }
    739  if (!table.ReadU8(&this->maxRulePreContext) ||
    740      this->maxRulePreContext < this->minRulePreContext) {
    741    return parent->Error("SILPass: Failed to read valid maxRulePreContext");
    742  }
    743 
    744  unsigned startStates_len = this->maxRulePreContext - this->minRulePreContext
    745                             + 1;
    746    // this->minRulePreContext <= this->maxRulePreContext
    747  //this->startStates.resize(startStates_len);
    748  for (unsigned i = 0; i < startStates_len; ++i) {
    749    this->startStates.emplace_back();
    750    if (!table.ReadS16(&this->startStates[i])) {
    751      return parent->Error("SILPass: Failed to read startStates[%u]", i);
    752    }
    753  }
    754 
    755  //this->ruleSortKeys.resize(this->numRules);
    756  for (unsigned i = 0; i < this->numRules; ++i) {
    757    this->ruleSortKeys.emplace_back();
    758    if (!table.ReadU16(&this->ruleSortKeys[i])) {
    759      return parent->Error("SILPass: Failed to read ruleSortKeys[%u]", i);
    760    }
    761  }
    762 
    763  //this->rulePreContext.resize(this->numRules);
    764  for (unsigned i = 0; i < this->numRules; ++i) {
    765    this->rulePreContext.emplace_back();
    766    if (!table.ReadU8(&this->rulePreContext[i])) {
    767      return parent->Error("SILPass: Failed to read rulePreContext[%u]", i);
    768    }
    769  }
    770 
    771  if (parent->version >> 16 >= 2) {
    772    if (!table.ReadU8(&this->collisionThreshold)) {
    773      return parent->Error("SILPass: Failed to read collisionThreshold");
    774    }
    775    if (!table.ReadU16(&this->pConstraint)) {
    776      return parent->Error("SILPass: Failed to read pConstraint");
    777    }
    778  }
    779 
    780  unsigned long ruleConstraints_len = this->aCode - this->rcCode;
    781    // this->rcCode <= this->aCode
    782  //this->oConstraints.resize(static_cast<unsigned long>(this->numRules) + 1);
    783  for (unsigned long i = 0; i <= this->numRules; ++i) {
    784    this->oConstraints.emplace_back();
    785    if (!table.ReadU16(&this->oConstraints[i]) ||
    786        this->oConstraints[i] > ruleConstraints_len) {
    787      return parent->Error("SILPass: Failed to read valid oConstraints[%lu]",
    788                           i);
    789    }
    790  }
    791 
    792  if (!this->oDebug && this->aCode > next_pass_offset) {
    793    return parent->Error("SILPass: Failed to calculate length of actions");
    794  }
    795  unsigned long actions_len = this->oDebug ? this->oDebug - this->aCode :
    796                                             next_pass_offset - this->aCode;
    797    // if this->oDebug, then this->aCode <= this->oDebug
    798  //this->oActions.resize(static_cast<unsigned long>(this->numRules) + 1);
    799  for (unsigned long i = 0; i <= this->numRules; ++i) {
    800    this->oActions.emplace_back();
    801    if (!table.ReadU16(&this->oActions[i]) ||
    802        (this->oActions[i] > actions_len)) {
    803      return parent->Error("SILPass: Failed to read valid oActions[%lu]", i);
    804    }
    805  }
    806 
    807  //this->stateTrans.resize(this->numTransitional);
    808  for (unsigned i = 0; i < this->numTransitional; ++i) {
    809    this->stateTrans.emplace_back();
    810    //this->stateTrans[i].resize(this->numColumns);
    811    for (unsigned j = 0; j < this->numColumns; ++j) {
    812      this->stateTrans[i].emplace_back();
    813      if (!table.ReadU16(&stateTrans[i][j])) {
    814        return parent->Error("SILPass: Failed to read stateTrans[%u][%u]",
    815                             i, j);
    816      }
    817    }
    818  }
    819 
    820  if (parent->version >> 16 >= 2) {
    821    if (!table.ReadU8(&this->reserved2)) {
    822      return parent->Error("SILPass: Failed to read reserved2");
    823    }
    824    if (this->reserved2 != 0) {
    825      parent->Warning("SILPass: Nonzero reserved2");
    826    }
    827 
    828    if (table.offset() != SILSub_init_offset + this->pcCode) {
    829      return parent->Error("SILPass: pcCode check failed");
    830    }
    831    //this->passConstraints.resize(this->pConstraint);
    832    for (unsigned i = 0; i < this->pConstraint; ++i) {
    833      this->passConstraints.emplace_back();
    834      if (!table.ReadU8(&this->passConstraints[i])) {
    835        return parent->Error("SILPass: Failed to read passConstraints[%u]", i);
    836      }
    837    }
    838  }
    839 
    840  if (table.offset() != SILSub_init_offset + this->rcCode) {
    841    return parent->Error("SILPass: rcCode check failed");
    842  }
    843  //this->ruleConstraints.resize(ruleConstraints_len);  // calculated above
    844  for (unsigned long i = 0; i < ruleConstraints_len; ++i) {
    845    this->ruleConstraints.emplace_back();
    846    if (!table.ReadU8(&this->ruleConstraints[i])) {
    847      return parent->Error("SILPass: Failed to read ruleConstraints[%u]", i);
    848    }
    849  }
    850 
    851  if (table.offset() != SILSub_init_offset + this->aCode) {
    852    return parent->Error("SILPass: aCode check failed");
    853  }
    854  //this->actions.resize(actions_len);  // calculated above
    855  for (unsigned long i = 0; i < actions_len; ++i) {
    856    this->actions.emplace_back();
    857    if (!table.ReadU8(&this->actions[i])) {
    858      return parent->Error("SILPass: Failed to read actions[%u]", i);
    859    }
    860  }
    861 
    862  if (this->oDebug) {
    863    OpenTypeNAME* name = static_cast<OpenTypeNAME*>(
    864        parent->GetFont()->GetTypedTable(OTS_TAG_NAME));
    865    if (!name) {
    866      return parent->Error("SILPass: Required name table is missing");
    867    }
    868 
    869    if (table.offset() != SILSub_init_offset + this->oDebug) {
    870      return parent->Error("SILPass: oDebug check failed");
    871    }
    872    //this->dActions.resize(this->numRules);
    873    for (unsigned i = 0; i < this->numRules; ++i) {
    874      this->dActions.emplace_back();
    875      if (!table.ReadU16(&this->dActions[i]) ||
    876          !name->IsValidNameId(this->dActions[i])) {
    877        return parent->Error("SILPass: Failed to read valid dActions[%u]", i);
    878      }
    879    }
    880 
    881    unsigned dStates_len = this->numRows - this->numRules;
    882      // this->numRules <= this->numRows
    883    //this->dStates.resize(dStates_len);
    884    for (unsigned i = 0; i < dStates_len; ++i) {
    885      this->dStates.emplace_back();
    886      if (!table.ReadU16(&this->dStates[i]) ||
    887          !name->IsValidNameId(this->dStates[i])) {
    888        return parent->Error("SILPass: Failed to read valid dStates[%u]", i);
    889      }
    890    }
    891 
    892    //this->dCols.resize(this->numRules);
    893    for (unsigned i = 0; i < this->numRules; ++i) {
    894      this->dCols.emplace_back();
    895      if (!table.ReadU16(&this->dCols[i]) ||
    896          !name->IsValidNameId(this->dCols[i])) {
    897        return parent->Error("SILPass: Failed to read valid dCols[%u]");
    898      }
    899    }
    900  }
    901  return true;
    902 }
    903 
    904 bool OpenTypeSILF::SILSub::
    905 SILPass::SerializePart(OTSStream* out) const {
    906  if (!out->WriteU8(this->flags) ||
    907      !out->WriteU8(this->maxRuleLoop) ||
    908      !out->WriteU8(this->maxRuleContext) ||
    909      !out->WriteU8(this->maxBackup) ||
    910      !out->WriteU16(this->numRules) ||
    911      (parent->version >> 16 >= 2 &&
    912       (!out->WriteU16(this->fsmOffset) ||
    913        !out->WriteU32(this->pcCode))) ||
    914      !out->WriteU32(this->rcCode) ||
    915      !out->WriteU32(this->aCode) ||
    916      !out->WriteU32(this->oDebug) ||
    917      !out->WriteU16(this->numRows) ||
    918      !out->WriteU16(this->numTransitional) ||
    919      !out->WriteU16(this->numSuccess) ||
    920      !out->WriteU16(this->numColumns) ||
    921      !out->WriteU16(this->numRange) ||
    922      !out->WriteU16(this->searchRange) ||
    923      !out->WriteU16(this->entrySelector) ||
    924      !out->WriteU16(this->rangeShift) ||
    925      !SerializeParts(this->ranges, out) ||
    926      !SerializeParts(this->oRuleMap, out) ||
    927      !SerializeParts(this->ruleMap, out) ||
    928      !out->WriteU8(this->minRulePreContext) ||
    929      !out->WriteU8(this->maxRulePreContext) ||
    930      !SerializeParts(this->startStates, out) ||
    931      !SerializeParts(this->ruleSortKeys, out) ||
    932      !SerializeParts(this->rulePreContext, out) ||
    933      (parent->version >> 16 >= 2 &&
    934       (!out->WriteU8(this->collisionThreshold) ||
    935        !out->WriteU16(this->pConstraint))) ||
    936      !SerializeParts(this->oConstraints, out) ||
    937      !SerializeParts(this->oActions, out) ||
    938      !SerializeParts(this->stateTrans, out) ||
    939      (parent->version >> 16 >= 2 &&
    940       (!out->WriteU8(this->reserved2) ||
    941        !SerializeParts(this->passConstraints, out))) ||
    942      !SerializeParts(this->ruleConstraints, out) ||
    943      !SerializeParts(this->actions, out) ||
    944      !SerializeParts(this->dActions, out) ||
    945      !SerializeParts(this->dStates, out) ||
    946      !SerializeParts(this->dCols, out)) {
    947    return parent->Error("SILPass: Failed to write");
    948  }
    949  return true;
    950 }
    951 
    952 bool OpenTypeSILF::SILSub::SILPass::
    953 PassRange::ParsePart(Buffer& table) {
    954  if (!table.ReadU16(&this->firstId)) {
    955    return parent->Error("PassRange: Failed to read firstId");
    956  }
    957  if (!table.ReadU16(&this->lastId)) {
    958    return parent->Error("PassRange: Failed to read lastId");
    959  }
    960  if (!table.ReadU16(&this->colId)) {
    961    return parent->Error("PassRange: Failed to read colId");
    962  }
    963  return true;
    964 }
    965 
    966 bool OpenTypeSILF::SILSub::SILPass::
    967 PassRange::SerializePart(OTSStream* out) const {
    968  if (!out->WriteU16(this->firstId) ||
    969      !out->WriteU16(this->lastId) ||
    970      !out->WriteU16(this->colId)) {
    971    return parent->Error("PassRange: Failed to write");
    972  }
    973  return true;
    974 }
    975 
    976 }  // namespace ots