Repository: mozilla/pdf.js
File: src/core/xref.js (JavaScript)

Code before:
      // The current dictionary is a candidate, but continue searching.
      trailerDict = dict;
    }
    // No trailer with 'ID', taking last one (if exists).
    if (trailerDict) {
      return trailerDict;
    }
    // No trailer dictionary found, taking the "top"-dictionary (if exists).
    if (this.topDict) {
      return this.topDict;
    }

    // When no trailer dictionary candidate exists, try picking the first
    // dictionary that contains a /Root entry (fixes issue18986.pdf).
    if (!trailerDicts.length) {
      // In case, this.entries is a sparse array we don't want to
      // iterate over empty entries so we use the `in` operator instead of
      // using for..of on entries() or a for with the array length.
      // The only drawback of for..in is that the keys are strings, so we need
      // to convert them to numbers when using them as array indices.
      for (const num in this.entries) {
        const entry = this.entries[num];
        if (!entry) {
          continue;
        }
        const ref = Ref.get(parseInt(num, 10), entry.gen);
        let obj;

        try {
          obj = this.fetch(ref);
        } catch {
          continue;
        }

        if (obj instanceof BaseStream) {
          obj = obj.dict;
        }
        if (obj instanceof Dict && obj.has("Root")) {
          return obj;
        }
      }
    }

    // nothing helps
    throw new InvalidPDFException("Invalid PDF structure.");
  }

  readXRef(recoveryMode = false) {
    const stream = this.stream;
    // Keep track of already parsed XRef tables, to prevent an infinite loop
    // when parsing corrupt PDF files where e.g. the /Prev entries create a

Review comment (performance):
I saw on matrix that you were slightly annoyed by having to do the string->number cast -- you could also move it to inside the `Ref` constructor if you prefer, so that we only do it when creating refs and not when looking them up from the cache.

Code after:
      // The current dictionary is a candidate, but continue searching.
      trailerDict = dict;
    }
    // No trailer with 'ID', taking last one (if exists).
    if (trailerDict) {
      return trailerDict;
    }
    // No trailer dictionary found, taking the "top"-dictionary (if exists).
    if (this.topDict) {
      return this.topDict;
    }

    // When no trailer dictionary candidate exists, try picking the first
    // dictionary that contains a /Root entry (fixes issue18986.pdf).
    if (!trailerDicts.length) {
      // In case, this.entries is a sparse array we don't want to
      // iterate over empty entries so we use the `in` operator instead of
      // using for..of on entries() or a for with the array length.
      for (const num in this.entries) {
        if (!Object.hasOwn(this.entries, num)) {
          continue;
        }
        const entry = this.entries[num];
        const ref = Ref.get(parseInt(num), entry.gen);
        let obj;

        try {
          obj = this.fetch(ref);
        } catch {
          continue;
        }

        if (obj instanceof BaseStream) {
          obj = obj.dict;
        }
        if (obj instanceof Dict && obj.has("Root")) {
          return obj;
        }
      }
    }

    // nothing helps
    throw new InvalidPDFException("Invalid PDF structure.");
  }

  readXRef(recoveryMode = false) {
    const stream = this.stream;
    // Keep track of already parsed XRef tables, to prevent an infinite loop
    // when parsing corrupt PDF files where e.g. the /Prev entries create a
    // circular dependency between tables (fixes bug1393476.pdf).
    const startXRefParsedCache = new Set();