blob: bba96d9b345bb671cb4626eeefe7c56631510609 [file] [log] [blame]
// Copyright 2016 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#include "core/fpdfapi/parser/cpdf_syntax_parser.h"
#include <algorithm>
#include <sstream>
#include <utility>
#include <vector>
#include "core/fpdfapi/parser/cpdf_array.h"
#include "core/fpdfapi/parser/cpdf_boolean.h"
#include "core/fpdfapi/parser/cpdf_crypto_handler.h"
#include "core/fpdfapi/parser/cpdf_dictionary.h"
#include "core/fpdfapi/parser/cpdf_name.h"
#include "core/fpdfapi/parser/cpdf_null.h"
#include "core/fpdfapi/parser/cpdf_number.h"
#include "core/fpdfapi/parser/cpdf_read_validator.h"
#include "core/fpdfapi/parser/cpdf_reference.h"
#include "core/fpdfapi/parser/cpdf_stream.h"
#include "core/fpdfapi/parser/cpdf_string.h"
#include "core/fpdfapi/parser/fpdf_parser_decode.h"
#include "core/fpdfapi/parser/fpdf_parser_utility.h"
#include "core/fxcrt/autorestorer.h"
#include "core/fxcrt/cfx_binarybuf.h"
#include "core/fxcrt/fx_extension.h"
#include "third_party/base/numerics/safe_math.h"
#include "third_party/base/ptr_util.h"
namespace {
enum class ReadStatus { Normal, Backslash, Octal, FinishOctal, CarriageReturn };
class ReadableSubStream final : public IFX_SeekableReadStream {
public:
ReadableSubStream(const RetainPtr<IFX_SeekableReadStream>& pFileRead,
FX_FILESIZE part_offset,
FX_FILESIZE part_size)
: m_pFileRead(pFileRead),
m_PartOffset(part_offset),
m_PartSize(part_size) {}
~ReadableSubStream() override = default;
// IFX_SeekableReadStream overrides:
bool ReadBlock(void* buffer, FX_FILESIZE offset, size_t size) override {
FX_SAFE_FILESIZE safe_end = offset;
safe_end += size;
// Check that requested range is valid, to prevent calling of ReadBlock
// of original m_pFileRead with incorrect params.
if (!safe_end.IsValid() || safe_end.ValueOrDie() > m_PartSize)
return false;
return m_pFileRead->ReadBlock(buffer, m_PartOffset + offset, size);
}
FX_FILESIZE GetSize() override { return m_PartSize; }
private:
RetainPtr<IFX_SeekableReadStream> m_pFileRead;
FX_FILESIZE m_PartOffset;
FX_FILESIZE m_PartSize;
};
} // namespace
// static
int CPDF_SyntaxParser::s_CurrentRecursionDepth = 0;
// static
std::unique_ptr<CPDF_SyntaxParser> CPDF_SyntaxParser::CreateForTesting(
const RetainPtr<IFX_SeekableReadStream>& pFileAccess,
FX_FILESIZE HeaderOffset) {
return pdfium::MakeUnique<CPDF_SyntaxParser>(
pdfium::MakeRetain<CPDF_ReadValidator>(pFileAccess, nullptr),
HeaderOffset);
}
CPDF_SyntaxParser::CPDF_SyntaxParser(
const RetainPtr<IFX_SeekableReadStream>& pFileAccess)
: CPDF_SyntaxParser(
pdfium::MakeRetain<CPDF_ReadValidator>(pFileAccess, nullptr),
0) {}
CPDF_SyntaxParser::CPDF_SyntaxParser(
const RetainPtr<CPDF_ReadValidator>& validator,
FX_FILESIZE HeaderOffset)
: m_pFileAccess(validator),
m_HeaderOffset(HeaderOffset),
m_FileLen(m_pFileAccess->GetSize()) {
ASSERT(m_HeaderOffset <= m_FileLen);
}
CPDF_SyntaxParser::~CPDF_SyntaxParser() = default;
bool CPDF_SyntaxParser::GetCharAt(FX_FILESIZE pos, uint8_t& ch) {
AutoRestorer<FX_FILESIZE> save_pos(&m_Pos);
m_Pos = pos;
return GetNextChar(ch);
}
bool CPDF_SyntaxParser::ReadBlockAt(FX_FILESIZE read_pos) {
if (read_pos >= m_FileLen)
return false;
size_t read_size = m_ReadBufferSize;
FX_SAFE_FILESIZE safe_end = read_pos;
safe_end += read_size;
if (!safe_end.IsValid() || safe_end.ValueOrDie() > m_FileLen)
read_size = m_FileLen - read_pos;
m_pFileBuf.resize(read_size);
if (!m_pFileAccess->ReadBlock(m_pFileBuf.data(), read_pos, read_size)) {
m_pFileBuf.clear();
return false;
}
m_BufOffset = read_pos;
return true;
}
bool CPDF_SyntaxParser::GetNextChar(uint8_t& ch) {
FX_FILESIZE pos = m_Pos + m_HeaderOffset;
if (pos >= m_FileLen)
return false;
if (!IsPositionRead(pos) && !ReadBlockAt(pos))
return false;
ch = m_pFileBuf[pos - m_BufOffset];
m_Pos++;
return true;
}
FX_FILESIZE CPDF_SyntaxParser::GetDocumentSize() const {
return m_FileLen - m_HeaderOffset;
}
bool CPDF_SyntaxParser::GetCharAtBackward(FX_FILESIZE pos, uint8_t* ch) {
pos += m_HeaderOffset;
if (pos >= m_FileLen)
return false;
if (!IsPositionRead(pos)) {
FX_FILESIZE block_start = 0;
if (pos >= CPDF_ModuleMgr::kFileBufSize)
block_start = pos - CPDF_ModuleMgr::kFileBufSize + 1;
if (!ReadBlockAt(block_start) || !IsPositionRead(pos))
return false;
}
*ch = m_pFileBuf[pos - m_BufOffset];
return true;
}
bool CPDF_SyntaxParser::ReadBlock(uint8_t* pBuf, uint32_t size) {
if (!m_pFileAccess->ReadBlock(pBuf, m_Pos + m_HeaderOffset, size))
return false;
m_Pos += size;
return true;
}
void CPDF_SyntaxParser::GetNextWordInternal(bool* bIsNumber) {
m_WordSize = 0;
if (bIsNumber)
*bIsNumber = true;
ToNextWord();
uint8_t ch;
if (!GetNextChar(ch))
return;
if (PDFCharIsDelimiter(ch)) {
if (bIsNumber)
*bIsNumber = false;
m_WordBuffer[m_WordSize++] = ch;
if (ch == '/') {
while (1) {
if (!GetNextChar(ch))
return;
if (!PDFCharIsOther(ch) && !PDFCharIsNumeric(ch)) {
m_Pos--;
return;
}
if (m_WordSize < sizeof(m_WordBuffer) - 1)
m_WordBuffer[m_WordSize++] = ch;
}
} else if (ch == '<') {
if (!GetNextChar(ch))
return;
if (ch == '<')
m_WordBuffer[m_WordSize++] = ch;
else
m_Pos--;
} else if (ch == '>') {
if (!GetNextChar(ch))
return;
if (ch == '>')
m_WordBuffer[m_WordSize++] = ch;
else
m_Pos--;
}
return;
}
while (1) {
if (m_WordSize < sizeof(m_WordBuffer) - 1)
m_WordBuffer[m_WordSize++] = ch;
if (!PDFCharIsNumeric(ch)) {
if (bIsNumber)
*bIsNumber = false;
}
if (!GetNextChar(ch))
return;
if (PDFCharIsDelimiter(ch) || PDFCharIsWhitespace(ch)) {
m_Pos--;
break;
}
}
}
ByteString CPDF_SyntaxParser::ReadString() {
uint8_t ch;
if (!GetNextChar(ch))
return ByteString();
std::ostringstream buf;
int32_t parlevel = 0;
ReadStatus status = ReadStatus::Normal;
int32_t iEscCode = 0;
while (1) {
switch (status) {
case ReadStatus::Normal:
if (ch == ')') {
if (parlevel == 0)
return ByteString(buf);
parlevel--;
} else if (ch == '(') {
parlevel++;
}
if (ch == '\\')
status = ReadStatus::Backslash;
else
buf << static_cast<char>(ch);
break;
case ReadStatus::Backslash:
if (ch >= '0' && ch <= '7') {
iEscCode = FXSYS_DecimalCharToInt(static_cast<wchar_t>(ch));
status = ReadStatus::Octal;
break;
}
if (ch == '\r') {
status = ReadStatus::CarriageReturn;
break;
}
if (ch == 'n') {
buf << '\n';
} else if (ch == 'r') {
buf << '\r';
} else if (ch == 't') {
buf << '\t';
} else if (ch == 'b') {
buf << '\b';
} else if (ch == 'f') {
buf << '\f';
} else if (ch != '\n') {
buf << static_cast<char>(ch);
}
status = ReadStatus::Normal;
break;
case ReadStatus::Octal:
if (ch >= '0' && ch <= '7') {
iEscCode =
iEscCode * 8 + FXSYS_DecimalCharToInt(static_cast<wchar_t>(ch));
status = ReadStatus::FinishOctal;
} else {
buf << static_cast<char>(iEscCode);
status = ReadStatus::Normal;
continue;
}
break;
case ReadStatus::FinishOctal:
status = ReadStatus::Normal;
if (ch >= '0' && ch <= '7') {
iEscCode =
iEscCode * 8 + FXSYS_DecimalCharToInt(static_cast<wchar_t>(ch));
buf << static_cast<char>(iEscCode);
} else {
buf << static_cast<char>(iEscCode);
continue;
}
break;
case ReadStatus::CarriageReturn:
status = ReadStatus::Normal;
if (ch != '\n')
continue;
break;
}
if (!GetNextChar(ch))
break;
}
GetNextChar(ch);
return ByteString(buf);
}
ByteString CPDF_SyntaxParser::ReadHexString() {
uint8_t ch;
if (!GetNextChar(ch))
return ByteString();
std::ostringstream buf;
bool bFirst = true;
uint8_t code = 0;
while (1) {
if (ch == '>')
break;
if (std::isxdigit(ch)) {
int val = FXSYS_HexCharToInt(ch);
if (bFirst) {
code = val * 16;
} else {
code += val;
buf << static_cast<char>(code);
}
bFirst = !bFirst;
}
if (!GetNextChar(ch))
break;
}
if (!bFirst)
buf << static_cast<char>(code);
return ByteString(buf);
}
void CPDF_SyntaxParser::ToNextLine() {
uint8_t ch;
while (GetNextChar(ch)) {
if (ch == '\n')
break;
if (ch == '\r') {
GetNextChar(ch);
if (ch != '\n')
--m_Pos;
break;
}
}
}
void CPDF_SyntaxParser::ToNextWord() {
uint8_t ch;
if (!GetNextChar(ch))
return;
while (1) {
while (PDFCharIsWhitespace(ch)) {
if (!GetNextChar(ch))
return;
}
if (ch != '%')
break;
while (1) {
if (!GetNextChar(ch))
return;
if (PDFCharIsLineEnding(ch))
break;
}
}
m_Pos--;
}
ByteString CPDF_SyntaxParser::GetNextWord(bool* bIsNumber) {
const CPDF_ReadValidator::Session read_session(GetValidator().Get());
GetNextWordInternal(bIsNumber);
ByteString ret;
if (!GetValidator()->has_read_problems())
ret = ByteString(m_WordBuffer, m_WordSize);
return ret;
}
ByteString CPDF_SyntaxParser::PeekNextWord(bool* bIsNumber) {
AutoRestorer<FX_FILESIZE> save_pos(&m_Pos);
return GetNextWord(bIsNumber);
}
ByteString CPDF_SyntaxParser::GetKeyword() {
return GetNextWord(nullptr);
}
std::unique_ptr<CPDF_Object> CPDF_SyntaxParser::GetObjectBody(
CPDF_IndirectObjectHolder* pObjList) {
const CPDF_ReadValidator::Session read_session(GetValidator().Get());
auto result = GetObjectBodyInternal(pObjList, ParseType::kLoose);
if (GetValidator()->has_read_problems())
return nullptr;
return result;
}
std::unique_ptr<CPDF_Object> CPDF_SyntaxParser::GetObjectBodyInternal(
CPDF_IndirectObjectHolder* pObjList,
ParseType parse_type) {
AutoRestorer<int> depth_restorer(&s_CurrentRecursionDepth);
if (++s_CurrentRecursionDepth > kParserMaxRecursionDepth)
return nullptr;
FX_FILESIZE SavedObjPos = m_Pos;
bool bIsNumber;
ByteString word = GetNextWord(&bIsNumber);
if (word.GetLength() == 0)
return nullptr;
if (bIsNumber) {
AutoRestorer<FX_FILESIZE> pos_restorer(&m_Pos);
ByteString nextword = GetNextWord(&bIsNumber);
if (!bIsNumber)
return pdfium::MakeUnique<CPDF_Number>(word.AsStringView());
ByteString nextword2 = GetNextWord(nullptr);
if (nextword2 != "R")
return pdfium::MakeUnique<CPDF_Number>(word.AsStringView());
pos_restorer.AbandonRestoration();
uint32_t refnum = FXSYS_atoui(word.c_str());
if (refnum == CPDF_Object::kInvalidObjNum)
return nullptr;
return pdfium::MakeUnique<CPDF_Reference>(pObjList, refnum);
}
if (word == "true" || word == "false")
return pdfium::MakeUnique<CPDF_Boolean>(word == "true");
if (word == "null")
return pdfium::MakeUnique<CPDF_Null>();
if (word == "(") {
ByteString str = ReadString();
return pdfium::MakeUnique<CPDF_String>(m_pPool, str, false);
}
if (word == "<") {
ByteString str = ReadHexString();
return pdfium::MakeUnique<CPDF_String>(m_pPool, str, true);
}
if (word == "[") {
auto pArray = pdfium::MakeUnique<CPDF_Array>();
while (std::unique_ptr<CPDF_Object> pObj =
GetObjectBodyInternal(pObjList, ParseType::kLoose)) {
pArray->Add(std::move(pObj));
}
return (parse_type == ParseType::kLoose || m_WordBuffer[0] == ']')
? std::move(pArray)
: nullptr;
}
if (word[0] == '/') {
return pdfium::MakeUnique<CPDF_Name>(
m_pPool,
PDF_NameDecode(ByteStringView(m_WordBuffer + 1, m_WordSize - 1)));
}
if (word == "<<") {
std::unique_ptr<CPDF_Dictionary> pDict =
pdfium::MakeUnique<CPDF_Dictionary>(m_pPool);
while (1) {
ByteString inner_word = GetNextWord(nullptr);
if (inner_word.IsEmpty())
return nullptr;
FX_FILESIZE SavedPos = m_Pos - inner_word.GetLength();
if (inner_word == ">>")
break;
if (inner_word == "endobj") {
m_Pos = SavedPos;
break;
}
if (inner_word[0] != '/')
continue;
ByteString key = PDF_NameDecode(inner_word.AsStringView());
if (key.IsEmpty() && parse_type == ParseType::kLoose)
continue;
std::unique_ptr<CPDF_Object> pObj =
GetObjectBodyInternal(pObjList, ParseType::kLoose);
if (!pObj) {
if (parse_type == ParseType::kLoose)
continue;
ToNextLine();
return nullptr;
}
if (!key.IsEmpty()) {
ByteString keyNoSlash(key.raw_str() + 1, key.GetLength() - 1);
pDict->SetFor(keyNoSlash, std::move(pObj));
}
}
AutoRestorer<FX_FILESIZE> pos_restorer(&m_Pos);
if (GetNextWord(nullptr) != "stream")
return std::move(pDict);
pos_restorer.AbandonRestoration();
return ReadStream(std::move(pDict));
}
if (word == ">>")
m_Pos = SavedObjPos;
return nullptr;
}
std::unique_ptr<CPDF_Object> CPDF_SyntaxParser::GetIndirectObject(
CPDF_IndirectObjectHolder* pObjList,
ParseType parse_type) {
const CPDF_ReadValidator::Session read_session(GetValidator().Get());
const FX_FILESIZE saved_pos = GetPos();
bool is_number = false;
ByteString word = GetNextWord(&is_number);
if (!is_number || word.IsEmpty()) {
SetPos(saved_pos);
return nullptr;
}
const uint32_t parser_objnum = FXSYS_atoui(word.c_str());
word = GetNextWord(&is_number);
if (!is_number || word.IsEmpty()) {
SetPos(saved_pos);
return nullptr;
}
const uint32_t parser_gennum = FXSYS_atoui(word.c_str());
if (GetKeyword() != "obj") {
SetPos(saved_pos);
return nullptr;
}
std::unique_ptr<CPDF_Object> pObj =
GetObjectBodyInternal(pObjList, parse_type);
if (pObj) {
pObj->SetObjNum(parser_objnum);
pObj->SetGenNum(parser_gennum);
}
return GetValidator()->has_read_problems() ? nullptr : std::move(pObj);
}
unsigned int CPDF_SyntaxParser::ReadEOLMarkers(FX_FILESIZE pos) {
unsigned char byte1 = 0;
unsigned char byte2 = 0;
GetCharAt(pos, byte1);
GetCharAt(pos + 1, byte2);
if (byte1 == '\r' && byte2 == '\n')
return 2;
if (byte1 == '\r' || byte1 == '\n')
return 1;
return 0;
}
FX_FILESIZE CPDF_SyntaxParser::FindWordPos(const ByteStringView& word) {
AutoRestorer<FX_FILESIZE> pos_restorer(&m_Pos);
FX_FILESIZE end_offset = FindTag(word);
while (end_offset >= 0) {
// Stop searching when word is found.
if (IsWholeWord(GetPos() - word.GetLength(), m_FileLen, word, true))
return GetPos() - word.GetLength();
end_offset = FindTag(word);
}
return -1;
}
FX_FILESIZE CPDF_SyntaxParser::FindStreamEndPos() {
const ByteStringView kEndStreamStr("endstream");
const ByteStringView kEndObjStr("endobj");
FX_FILESIZE endStreamWordOffset = FindWordPos(kEndStreamStr);
FX_FILESIZE endObjWordOffset = FindWordPos(kEndObjStr);
// Can't find "endstream" or "endobj".
if (endStreamWordOffset < 0 && endObjWordOffset < 0) {
return -1;
}
if (endStreamWordOffset < 0 && endObjWordOffset >= 0) {
// Correct the position of end stream.
endStreamWordOffset = endObjWordOffset;
} else if (endStreamWordOffset >= 0 && endObjWordOffset < 0) {
// Correct the position of end obj.
endObjWordOffset = endStreamWordOffset;
} else if (endStreamWordOffset > endObjWordOffset) {
endStreamWordOffset = endObjWordOffset;
}
int numMarkers = ReadEOLMarkers(endStreamWordOffset - 2);
if (numMarkers == 2) {
endStreamWordOffset -= 2;
} else {
numMarkers = ReadEOLMarkers(endStreamWordOffset - 1);
if (numMarkers == 1) {
endStreamWordOffset -= 1;
}
}
if (endStreamWordOffset < GetPos()) {
return -1;
}
return endStreamWordOffset;
}
std::unique_ptr<CPDF_Stream> CPDF_SyntaxParser::ReadStream(
std::unique_ptr<CPDF_Dictionary> pDict) {
const CPDF_Number* pLenObj = ToNumber(pDict->GetDirectObjectFor("Length"));
FX_FILESIZE len = pLenObj ? pLenObj->GetInteger() : -1;
// Locate the start of stream.
ToNextLine();
const FX_FILESIZE streamStartPos = GetPos();
if (len > 0) {
FX_SAFE_FILESIZE pos = GetPos();
pos += len;
if (!pos.IsValid() || pos.ValueOrDie() >= m_FileLen)
len = -1;
}
RetainPtr<IFX_SeekableReadStream> data;
if (len > 0) {
// Check data availability first to allow the Validator to request data
// smoothly, without jumps.
if (!GetValidator()->CheckDataRangeAndRequestIfUnavailable(
m_HeaderOffset + GetPos(), len)) {
return nullptr;
}
data = pdfium::MakeRetain<ReadableSubStream>(
GetValidator(), m_HeaderOffset + GetPos(), len);
SetPos(GetPos() + len);
}
const ByteStringView kEndStreamStr("endstream");
const ByteStringView kEndObjStr("endobj");
// Note, we allow zero length streams as we need to pass them through when we
// are importing pages into a new document.
if (len >= 0) {
const CPDF_ReadValidator::Session read_session(GetValidator().Get());
m_Pos += ReadEOLMarkers(GetPos());
memset(m_WordBuffer, 0, kEndStreamStr.GetLength() + 1);
GetNextWordInternal(nullptr);
if (GetValidator()->has_read_problems())
return nullptr;
// Earlier version of PDF specification doesn't require EOL marker before
// 'endstream' keyword. If keyword 'endstream' follows the bytes in
// specified length, it signals the end of stream.
if (memcmp(m_WordBuffer, kEndStreamStr.raw_str(),
kEndStreamStr.GetLength()) != 0) {
data.Reset();
len = -1;
SetPos(streamStartPos);
}
}
if (len < 0) {
// If len is not available or incorrect, len needs to be calculated
// by searching the keywords "endstream" or "endobj".
const FX_FILESIZE streamEndPos = FindStreamEndPos();
if (streamEndPos < 0)
return nullptr;
len = streamEndPos - streamStartPos;
ASSERT(len >= 0);
if (len > 0) {
SetPos(streamStartPos);
// Check data availability first to allow the Validator to request data
// smoothly, without jumps.
if (!GetValidator()->CheckDataRangeAndRequestIfUnavailable(
m_HeaderOffset + GetPos(), len)) {
return nullptr;
}
data = pdfium::MakeRetain<ReadableSubStream>(
GetValidator(), m_HeaderOffset + GetPos(), len);
SetPos(GetPos() + len);
}
}
auto pStream = pdfium::MakeUnique<CPDF_Stream>();
if (data) {
pStream->InitStreamFromFile(data, std::move(pDict));
} else {
DCHECK(!len);
pStream->InitStream({}, std::move(pDict)); // Empty stream
}
const FX_FILESIZE end_stream_offset = GetPos();
memset(m_WordBuffer, 0, kEndObjStr.GetLength() + 1);
GetNextWordInternal(nullptr);
int numMarkers = ReadEOLMarkers(GetPos());
if (m_WordSize == static_cast<unsigned int>(kEndObjStr.GetLength()) &&
numMarkers != 0 &&
memcmp(m_WordBuffer, kEndObjStr.raw_str(), kEndObjStr.GetLength()) == 0) {
SetPos(end_stream_offset);
}
return pStream;
}
uint32_t CPDF_SyntaxParser::GetDirectNum() {
bool bIsNumber;
GetNextWordInternal(&bIsNumber);
if (!bIsNumber)
return 0;
m_WordBuffer[m_WordSize] = 0;
return FXSYS_atoui(reinterpret_cast<const char*>(m_WordBuffer));
}
bool CPDF_SyntaxParser::IsWholeWord(FX_FILESIZE startpos,
FX_FILESIZE limit,
const ByteStringView& tag,
bool checkKeyword) {
const uint32_t taglen = tag.GetLength();
bool bCheckLeft = !PDFCharIsDelimiter(tag[0]) && !PDFCharIsWhitespace(tag[0]);
bool bCheckRight = !PDFCharIsDelimiter(tag[taglen - 1]) &&
!PDFCharIsWhitespace(tag[taglen - 1]);
uint8_t ch;
if (bCheckRight && startpos + (int32_t)taglen <= limit &&
GetCharAt(startpos + (int32_t)taglen, ch)) {
if (PDFCharIsNumeric(ch) || PDFCharIsOther(ch) ||
(checkKeyword && PDFCharIsDelimiter(ch))) {
return false;
}
}
if (bCheckLeft && startpos > 0 && GetCharAt(startpos - 1, ch)) {
if (PDFCharIsNumeric(ch) || PDFCharIsOther(ch) ||
(checkKeyword && PDFCharIsDelimiter(ch))) {
return false;
}
}
return true;
}
bool CPDF_SyntaxParser::BackwardsSearchToWord(const ByteStringView& tag,
FX_FILESIZE limit) {
int32_t taglen = tag.GetLength();
if (taglen == 0)
return false;
FX_FILESIZE pos = m_Pos;
int32_t offset = taglen - 1;
while (1) {
if (limit && pos <= m_Pos - limit)
return false;
uint8_t byte;
if (!GetCharAtBackward(pos, &byte))
return false;
if (byte == tag[offset]) {
offset--;
if (offset >= 0) {
pos--;
continue;
}
if (IsWholeWord(pos, limit, tag, false)) {
m_Pos = pos;
return true;
}
}
offset = byte == tag[taglen - 1] ? taglen - 2 : taglen - 1;
pos--;
if (pos < 0)
return false;
}
}
FX_FILESIZE CPDF_SyntaxParser::FindTag(const ByteStringView& tag) {
const FX_FILESIZE startpos = GetPos();
const int32_t taglen = tag.GetLength();
ASSERT(taglen > 0);
int32_t match = 0;
while (1) {
uint8_t ch;
if (!GetNextChar(ch))
return -1;
if (ch == tag[match]) {
match++;
if (match == taglen)
return GetPos() - startpos - taglen;
} else {
match = ch == tag[0] ? 1 : 0;
}
}
return -1;
}
bool CPDF_SyntaxParser::IsPositionRead(FX_FILESIZE pos) const {
return m_BufOffset <= pos &&
pos < static_cast<FX_FILESIZE>(m_BufOffset + m_pFileBuf.size());
}