XRef: Read complete entry instead of byte-wise The entries have a fixed size per section and an implementation-defined size limit. Reading each entry in one go saves the per byte virtual function call overhead.
diff --git a/poppler/XRef.cc b/poppler/XRef.cc index 851f39b..2f3e809 100644 --- a/poppler/XRef.cc +++ b/poppler/XRef.cc
@@ -749,6 +749,7 @@ return false; } } + constexpr int intNBytes = sizeof(int); constexpr int longLongNBytes = sizeof(long long); if (w[0] > intNBytes || w[1] > longLongNBytes || w[2] > longLongNBytes) { @@ -804,7 +805,6 @@ { unsigned long long offset, gen; const unsigned long long offsetMax = GoffsetMax(); - int type, c, i, j; if (first > INT_MAX - n) { return false; @@ -822,31 +822,36 @@ return false; } } - for (i = first; i < first + n; ++i) { - if (w[0] == 0) { - type = 1; - } else { - for (type = 0, j = 0; j < w[0]; ++j) { - if ((c = xrefStr->getChar()) == EOF) { - return false; - } + + constexpr int intNBytes = sizeof(int); + constexpr int longLongNBytes = sizeof(long long); + const auto nChars = w[0] + w[1] + w[2]; + std::array<unsigned char, intNBytes + 2 * longLongNBytes> xrefEntryBuffer; + + for (int i = first; i < first + n; ++i) { + if (xrefStr->doGetChars(nChars, xrefEntryBuffer.data()) < nChars) { + return false; + } + + int type = 1; + int j = 0; + + if (w[0] != 0) { + for (type = 0; j < w[0]; ++j) { + auto c = xrefEntryBuffer[j]; type = (type << 8) + c; } } - for (offset = 0, j = 0; j < w[1]; ++j) { - if ((c = xrefStr->getChar()) == EOF) { - return false; - } + for (offset = 0; j < (w[1] + w[0]); ++j) { + auto c = xrefEntryBuffer[j]; offset = (offset << 8) + c; } if (offset > offsetMax) { error(errSyntaxError, -1, "Offset inside xref table too large for fseek"); return false; } - for (gen = 0, j = 0; j < w[2]; ++j) { - if ((c = xrefStr->getChar()) == EOF) { - return false; - } + for (gen = 0; j < (w[2] + w[1] + w[0]); ++j) { + auto c = xrefEntryBuffer[j]; gen = (gen << 8) + c; } if (gen > INT_MAX) {