1// Copyright 2017 PDFium 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 "core/fpdfapi/parser/cpdf_object_walker.h"
6
7#include <utility>
8
9#include "core/fpdfapi/parser/cpdf_array.h"
10#include "core/fpdfapi/parser/cpdf_dictionary.h"
11#include "core/fpdfapi/parser/cpdf_stream.h"
12
13namespace {
14
15class StreamIterator : public CPDF_ObjectWalker::SubobjectIterator {
16 public:
17  explicit StreamIterator(const CPDF_Stream* stream)
18      : SubobjectIterator(stream) {}
19  ~StreamIterator() override {}
20
21  bool IsFinished() const override { return IsStarted() && is_finished_; }
22
23  const CPDF_Object* IncrementImpl() override {
24    ASSERT(IsStarted());
25    ASSERT(!IsFinished());
26    is_finished_ = true;
27    return object()->GetDict();
28  }
29
30  void Start() override {}
31
32 private:
33  bool is_finished_ = false;
34};
35
36class DictionaryIterator : public CPDF_ObjectWalker::SubobjectIterator {
37 public:
38  explicit DictionaryIterator(const CPDF_Dictionary* dictionary)
39      : SubobjectIterator(dictionary) {}
40  ~DictionaryIterator() override {}
41
42  bool IsFinished() const override {
43    return IsStarted() && dict_iterator_ == object()->GetDict()->end();
44  }
45
46  const CPDF_Object* IncrementImpl() override {
47    ASSERT(IsStarted());
48    ASSERT(!IsFinished());
49    const CPDF_Object* result = dict_iterator_->second.get();
50    dict_key_ = dict_iterator_->first;
51    ++dict_iterator_;
52    return result;
53  }
54
55  void Start() override {
56    ASSERT(!IsStarted());
57    dict_iterator_ = object()->GetDict()->begin();
58  }
59
60  const ByteString& dict_key() const { return dict_key_; }
61
62 private:
63  CPDF_Dictionary::const_iterator dict_iterator_;
64  ByteString dict_key_;
65};
66
67class ArrayIterator : public CPDF_ObjectWalker::SubobjectIterator {
68 public:
69  explicit ArrayIterator(const CPDF_Array* array) : SubobjectIterator(array) {}
70
71  ~ArrayIterator() override {}
72
73  bool IsFinished() const override {
74    return IsStarted() && arr_iterator_ == object()->AsArray()->end();
75  }
76
77  const CPDF_Object* IncrementImpl() override {
78    ASSERT(IsStarted());
79    ASSERT(!IsFinished());
80    const CPDF_Object* result = arr_iterator_->get();
81    ++arr_iterator_;
82    return result;
83  }
84
85  void Start() override { arr_iterator_ = object()->AsArray()->begin(); }
86
87 public:
88  CPDF_Array::const_iterator arr_iterator_;
89};
90
91}  // namespace
92
93CPDF_ObjectWalker::SubobjectIterator::~SubobjectIterator() {}
94
95const CPDF_Object* CPDF_ObjectWalker::SubobjectIterator::Increment() {
96  if (!IsStarted()) {
97    Start();
98    is_started_ = true;
99  }
100  while (!IsFinished()) {
101    const CPDF_Object* result = IncrementImpl();
102    if (result)
103      return result;
104  }
105  return nullptr;
106}
107
108CPDF_ObjectWalker::SubobjectIterator::SubobjectIterator(
109    const CPDF_Object* object)
110    : object_(object) {
111  ASSERT(object_);
112}
113
114// static
115std::unique_ptr<CPDF_ObjectWalker::SubobjectIterator>
116CPDF_ObjectWalker::MakeIterator(const CPDF_Object* object) {
117  if (object->IsStream())
118    return pdfium::MakeUnique<StreamIterator>(object->AsStream());
119  if (object->IsDictionary())
120    return pdfium::MakeUnique<DictionaryIterator>(object->AsDictionary());
121  if (object->IsArray())
122    return pdfium::MakeUnique<ArrayIterator>(object->AsArray());
123  return nullptr;
124}
125
126CPDF_ObjectWalker::CPDF_ObjectWalker(const CPDF_Object* root)
127    : next_object_(root), parent_object_(nullptr), current_depth_(0) {}
128
129CPDF_ObjectWalker::~CPDF_ObjectWalker() {}
130
131const CPDF_Object* CPDF_ObjectWalker::GetNext() {
132  while (!stack_.empty() || next_object_) {
133    if (next_object_) {
134      auto new_iterator = MakeIterator(next_object_);
135      if (new_iterator) {
136        // Schedule walk within composite objects.
137        stack_.push(std::move(new_iterator));
138      }
139      auto* result = next_object_;
140      next_object_ = nullptr;
141      return result;
142    }
143
144    SubobjectIterator* it = stack_.top().get();
145    if (it->IsFinished()) {
146      stack_.pop();
147    } else {
148      next_object_ = it->Increment();
149      parent_object_ = it->object();
150      dict_key_ = parent_object_->IsDictionary()
151                      ? static_cast<DictionaryIterator*>(it)->dict_key()
152                      : ByteString();
153      current_depth_ = stack_.size();
154    }
155  }
156  dict_key_ = ByteString();
157  current_depth_ = 0;
158  return nullptr;
159}
160
161void CPDF_ObjectWalker::SkipWalkIntoCurrentObject() {
162  if (stack_.empty() || stack_.top()->IsStarted())
163    return;
164  stack_.pop();
165}
166