From 304eefb58759e56be3fb357c78204accd4fa98fc Mon Sep 17 00:00:00 2001 From: Artem Strygin Date: Tue, 29 Aug 2017 00:26:42 +0300 Subject: Implement CPDF_ObjectAvail. This is non recursive replacement for CPDF_DataAvail::AreObjectsAvailable. Also added tests. Change-Id: I546289fc0963d2343253755850f55af8c0bd8e4c Reviewed-on: https://pdfium-review.googlesource.com/11430 Reviewed-by: dsinclair Commit-Queue: Art Snake --- core/fpdfapi/parser/cpdf_object_avail.cpp | 148 ++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 core/fpdfapi/parser/cpdf_object_avail.cpp (limited to 'core/fpdfapi/parser/cpdf_object_avail.cpp') diff --git a/core/fpdfapi/parser/cpdf_object_avail.cpp b/core/fpdfapi/parser/cpdf_object_avail.cpp new file mode 100644 index 0000000000..35e7f45850 --- /dev/null +++ b/core/fpdfapi/parser/cpdf_object_avail.cpp @@ -0,0 +1,148 @@ +// Copyright 2017 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. + +#include "core/fpdfapi/parser/cpdf_object_avail.h" + +#include + +#include "core/fpdfapi/parser/cpdf_dictionary.h" +#include "core/fpdfapi/parser/cpdf_indirect_object_holder.h" +#include "core/fpdfapi/parser/cpdf_object_walker.h" +#include "core/fpdfapi/parser/cpdf_read_validator.h" +#include "core/fpdfapi/parser/cpdf_reference.h" + +CPDF_ObjectAvail::CPDF_ObjectAvail(CPDF_ReadValidator* validator, + CPDF_IndirectObjectHolder* holder, + const CPDF_Object* root) + : validator_(validator), holder_(holder), root_(root) { + ASSERT(validator_); + ASSERT(holder); + ASSERT(root_); + if (!root_->IsInline()) + parsed_objnums_.insert(root_->GetObjNum()); +} + +CPDF_ObjectAvail::CPDF_ObjectAvail(CPDF_ReadValidator* validator, + CPDF_IndirectObjectHolder* holder, + uint32_t obj_num) + : validator_(validator), + holder_(holder), + root_(pdfium::MakeUnique(holder, obj_num)) { + ASSERT(validator_); + ASSERT(holder); +} + +CPDF_ObjectAvail::~CPDF_ObjectAvail() {} + +CPDF_DataAvail::DocAvailStatus CPDF_ObjectAvail::CheckAvail() { + if (!LoadRootObject()) + return CPDF_DataAvail::DocAvailStatus::DataNotAvailable; + + if (CheckObjects()) { + CleanMemory(); + return CPDF_DataAvail::DocAvailStatus::DataAvailable; + } + return CPDF_DataAvail::DocAvailStatus::DataNotAvailable; +} + +bool CPDF_ObjectAvail::LoadRootObject() { + if (!non_parsed_objects_.empty()) + return true; + + while (root_ && root_->IsReference()) { + const uint32_t ref_obj_num = root_->AsReference()->GetRefObjNum(); + if (HasObjectParsed(ref_obj_num)) { + root_ = nullptr; + return true; + } + + const CPDF_ReadValidator::Session parse_session(validator_.Get()); + const CPDF_Object* direct = holder_->GetOrParseIndirectObject(ref_obj_num); + if (validator_->has_read_problems()) + return false; + + parsed_objnums_.insert(ref_obj_num); + root_ = direct; + } + std::stack non_parsed_objects_in_root; + if (AppendObjectSubRefs(root_.Get(), &non_parsed_objects_in_root)) { + non_parsed_objects_ = std::move(non_parsed_objects_in_root); + return true; + } + return false; +} + +bool CPDF_ObjectAvail::CheckObjects() { + std::stack objects_to_check = std::move(non_parsed_objects_); + std::set checked_objects; + while (!objects_to_check.empty()) { + const uint32_t obj_num = objects_to_check.top(); + objects_to_check.pop(); + + if (HasObjectParsed(obj_num)) + continue; + + if (!checked_objects.insert(obj_num).second) + continue; + + const CPDF_ReadValidator::Session parse_session(validator_.Get()); + const CPDF_Object* direct = holder_->GetOrParseIndirectObject(obj_num); + if (direct == root_.Get()) + continue; + + if (validator_->has_read_problems() || + !AppendObjectSubRefs(direct, &objects_to_check)) { + non_parsed_objects_.push(obj_num); + continue; + } + parsed_objnums_.insert(obj_num); + } + return non_parsed_objects_.empty(); +} + +bool CPDF_ObjectAvail::AppendObjectSubRefs(const CPDF_Object* object, + std::stack* refs) const { + ASSERT(refs); + if (!object) + return true; + + CPDF_ObjectWalker walker(object); + while (const CPDF_Object* obj = walker.GetNext()) { + const CPDF_ReadValidator::Session parse_session(validator_.Get()); + + // Skip if this object if it's an inlined root, the parent object or + // explicitily excluded. + const bool skip = (walker.GetParent() && obj == root_.Get()) || + walker.dictionary_key() == "Parent" || + (obj != root_.Get() && ExcludeObject(obj)); + + // We need to parse the object before we can do the exclusion check. + // This is because the exclusion check may check against a referenced + // field of the object which we need to make sure is loaded. + if (validator_->has_read_problems()) + return false; + + if (skip) { + walker.SkipWalkIntoCurrentObject(); + continue; + } + + if (obj->IsReference()) + refs->push(obj->AsReference()->GetRefObjNum()); + } + return true; +} + +void CPDF_ObjectAvail::CleanMemory() { + root_.Reset(); + parsed_objnums_.clear(); +} + +bool CPDF_ObjectAvail::ExcludeObject(const CPDF_Object* object) const { + return false; +} + +bool CPDF_ObjectAvail::HasObjectParsed(uint32_t obj_num) const { + return parsed_objnums_.count(obj_num) > 0; +} -- cgit v1.2.3