1 // Copyright 2016 The PDFium Authors
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_array.h"
6
7 #include <iterator>
8 #include <memory>
9 #include <utility>
10
11 #include "core/fpdfapi/parser/cpdf_boolean.h"
12 #include "core/fpdfapi/parser/cpdf_dictionary.h"
13 #include "core/fpdfapi/parser/cpdf_number.h"
14 #include "core/fpdfapi/parser/cpdf_reference.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
TEST(ArrayTest,GetBooleanAt)17 TEST(ArrayTest, GetBooleanAt) {
18 auto arr = pdfium::MakeRetain<CPDF_Array>();
19 arr->AppendNew<CPDF_Boolean>(true);
20 arr->AppendNew<CPDF_Boolean>(false);
21 arr->AppendNew<CPDF_Number>(100);
22 arr->AppendNew<CPDF_Number>(0);
23
24 ASSERT_EQ(4u, arr->size());
25 EXPECT_TRUE(arr->GetBooleanAt(0, true));
26 EXPECT_TRUE(arr->GetBooleanAt(0, false));
27 EXPECT_FALSE(arr->GetBooleanAt(1, true));
28 EXPECT_FALSE(arr->GetBooleanAt(1, false));
29 EXPECT_TRUE(arr->GetBooleanAt(2, true));
30 EXPECT_FALSE(arr->GetBooleanAt(2, false));
31 EXPECT_TRUE(arr->GetBooleanAt(3, true));
32 EXPECT_FALSE(arr->GetBooleanAt(3, false));
33 EXPECT_TRUE(arr->GetBooleanAt(99, true));
34 EXPECT_FALSE(arr->GetBooleanAt(99, false));
35 }
36
TEST(ArrayTest,RemoveAt)37 TEST(ArrayTest, RemoveAt) {
38 {
39 auto arr = pdfium::MakeRetain<CPDF_Array>();
40 for (const int elem : {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
41 arr->AppendNew<CPDF_Number>(elem);
42 }
43 for (size_t i = 0; i < 3; ++i)
44 arr->RemoveAt(3);
45 constexpr std::array<int, 7> expected = {{1, 2, 3, 7, 8, 9, 10}};
46 ASSERT_EQ(expected.size(), arr->size());
47 for (size_t i = 0; i < expected.size(); ++i) {
48 EXPECT_EQ(expected[i], arr->GetIntegerAt(i));
49 }
50 arr->RemoveAt(4);
51 arr->RemoveAt(4);
52 constexpr std::array<int, 5> expected2 = {{1, 2, 3, 7, 10}};
53 ASSERT_EQ(std::size(expected2), arr->size());
54 for (size_t i = 0; i < std::size(expected2); ++i)
55 EXPECT_EQ(expected2[i], arr->GetIntegerAt(i));
56 }
57 {
58 // When the range is out of bound, RemoveAt() has no effect.
59 auto arr = pdfium::MakeRetain<CPDF_Array>();
60 for (const int elem : {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
61 arr->AppendNew<CPDF_Number>(elem);
62 }
63 EXPECT_EQ(10u, arr->size());
64 arr->RemoveAt(11);
65 EXPECT_EQ(10u, arr->size());
66 }
67 }
68
TEST(ArrayTest,Clear)69 TEST(ArrayTest, Clear) {
70 auto arr = pdfium::MakeRetain<CPDF_Array>();
71 for (const int elem : {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
72 arr->AppendNew<CPDF_Number>(elem);
73 }
74 EXPECT_EQ(10u, arr->size());
75 arr->Clear();
76 EXPECT_EQ(0U, arr->size());
77 }
78
TEST(ArrayTest,SetAtBeyond)79 TEST(ArrayTest, SetAtBeyond) {
80 auto arr = pdfium::MakeRetain<CPDF_Array>();
81 EXPECT_FALSE(arr->SetNewAt<CPDF_Number>(0, 0));
82 EXPECT_TRUE(arr->InsertNewAt<CPDF_Number>(0, 0));
83 EXPECT_FALSE(arr->SetNewAt<CPDF_Number>(1, 0));
84 }
85
TEST(ArrayTest,InsertAt)86 TEST(ArrayTest, InsertAt) {
87 constexpr std::array<int, 10> elems = {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}};
88 auto arr = pdfium::MakeRetain<CPDF_Array>();
89 for (size_t i = 0; i < std::size(elems); ++i) {
90 arr->InsertNewAt<CPDF_Number>(i, elems[i]);
91 }
92 ASSERT_EQ(std::size(elems), arr->size());
93 for (size_t i = 0; i < std::size(elems); ++i) {
94 EXPECT_EQ(elems[i], arr->GetIntegerAt(i));
95 }
96 arr->InsertNewAt<CPDF_Number>(3, 33);
97 arr->InsertNewAt<CPDF_Number>(6, 55);
98 arr->InsertNewAt<CPDF_Number>(12, 12);
99 constexpr std::array<int, 13> expected = {
100 {1, 2, 3, 33, 4, 5, 55, 6, 7, 8, 9, 10, 12}};
101 ASSERT_EQ(expected.size(), arr->size());
102 for (size_t i = 0; i < expected.size(); ++i) {
103 EXPECT_EQ(expected[i], arr->GetIntegerAt(i));
104 }
105 }
106
TEST(ArrayTest,InsertAtBeyond)107 TEST(ArrayTest, InsertAtBeyond) {
108 auto arr = pdfium::MakeRetain<CPDF_Array>();
109 EXPECT_FALSE(arr->InsertNewAt<CPDF_Number>(1, 0));
110 EXPECT_TRUE(arr->InsertNewAt<CPDF_Number>(0, 0));
111 EXPECT_FALSE(arr->InsertNewAt<CPDF_Number>(2, 0));
112 }
113
TEST(ArrayTest,Clone)114 TEST(ArrayTest, Clone) {
115 {
116 // Basic case.
117 constexpr std::array<int, 10> elems = {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}};
118 auto arr = pdfium::MakeRetain<CPDF_Array>();
119 for (size_t i = 0; i < std::size(elems); ++i) {
120 arr->InsertNewAt<CPDF_Number>(i, elems[i]);
121 }
122 RetainPtr<CPDF_Array> arr2 = ToArray(arr->Clone());
123 ASSERT_EQ(arr->size(), arr2->size());
124 for (size_t i = 0; i < std::size(elems); ++i) {
125 // Clone() always create new objects.
126 EXPECT_NE(arr->GetObjectAt(i), arr2->GetObjectAt(i));
127 EXPECT_EQ(arr->GetIntegerAt(i), arr2->GetIntegerAt(i));
128 }
129 }
130 {
131 // Clone() with and without dereferencing reference objects.
132 static const size_t kNumOfRows = 3;
133 static const size_t kNumOfColumns = 5;
134 using ElemRow = std::array<int, kNumOfColumns>;
135 constexpr std::array<ElemRow, kNumOfRows> elems = {{
136 {{1, 2, 3, 4, 5}},
137 {{10, 9, 8, 7, 6}},
138 {{11, 12, 13, 14, 15}},
139 }};
140 auto arr = pdfium::MakeRetain<CPDF_Array>();
141 // Indirect references to indirect objects.
142 auto obj_holder = std::make_unique<CPDF_IndirectObjectHolder>();
143 for (size_t i = 0; i < kNumOfRows; ++i) {
144 auto arr_elem = pdfium::MakeRetain<CPDF_Array>();
145 for (size_t j = 0; j < kNumOfColumns; ++j) {
146 auto obj = pdfium::MakeRetain<CPDF_Number>(elems[i][j]);
147 // Starts object number from 1.
148 int obj_num = i * kNumOfColumns + j + 1;
149 obj_holder->ReplaceIndirectObjectIfHigherGeneration(obj_num,
150 std::move(obj));
151 arr_elem->InsertNewAt<CPDF_Reference>(j, obj_holder.get(), obj_num);
152 }
153 arr->InsertAt(i, std::move(arr_elem));
154 }
155 ASSERT_EQ(kNumOfRows, arr->size());
156 // Not dereferencing reference objects means just creating new references
157 // instead of new copies of direct objects.
158 RetainPtr<CPDF_Array> arr1 = ToArray(arr->Clone());
159 ASSERT_EQ(arr->size(), arr1->size());
160 // Dereferencing reference objects creates new copies of direct objects.
161 RetainPtr<CPDF_Array> arr2 = ToArray(arr->CloneDirectObject());
162 ASSERT_EQ(arr->size(), arr2->size());
163 for (size_t i = 0; i < kNumOfRows; ++i) {
164 const CPDF_Array* arr_elem = arr->GetObjectAt(i)->AsArray();
165 const CPDF_Array* arr1_elem = arr1->GetObjectAt(i)->AsArray();
166 const CPDF_Array* arr2_elem = arr2->GetObjectAt(i)->AsArray();
167 EXPECT_NE(arr_elem, arr1_elem);
168 EXPECT_NE(arr_elem, arr2_elem);
169 for (size_t j = 0; j < kNumOfColumns; ++j) {
170 auto elem_obj = arr_elem->GetObjectAt(j);
171 auto elem_obj1 = arr1_elem->GetObjectAt(j);
172 auto elem_obj2 = arr2_elem->GetObjectAt(j);
173 // Results from not deferencing reference objects.
174 EXPECT_NE(elem_obj, elem_obj1);
175 EXPECT_TRUE(elem_obj1->IsReference());
176 EXPECT_EQ(elem_obj->GetDirect(), elem_obj1->GetDirect());
177 EXPECT_EQ(elem_obj->GetInteger(), elem_obj1->GetInteger());
178 // Results from deferencing reference objects.
179 EXPECT_NE(elem_obj, elem_obj2);
180 EXPECT_TRUE(elem_obj2->IsNumber());
181 EXPECT_NE(elem_obj->GetDirect(), elem_obj2);
182 EXPECT_EQ(elem_obj->GetObjNum(), elem_obj2->GetObjNum());
183 EXPECT_EQ(elem_obj->GetInteger(), elem_obj2->GetInteger());
184 }
185 }
186 arr.Reset();
187 ASSERT_EQ(kNumOfRows, arr1->size());
188 for (size_t i = 0; i < kNumOfRows; ++i) {
189 for (size_t j = 0; j < kNumOfColumns; ++j) {
190 // Results from not deferencing reference objects.
191 auto elem_obj1 = arr1->GetObjectAt(i)->AsArray()->GetObjectAt(j);
192 EXPECT_TRUE(elem_obj1->IsReference());
193 EXPECT_EQ(elems[i][j], elem_obj1->GetInteger());
194 // Results from deferencing reference objects.
195 EXPECT_EQ(elems[i][j],
196 arr2->GetObjectAt(i)->AsArray()->GetIntegerAt(j));
197 }
198 }
199 }
200 }
201
TEST(ArrayTest,Find)202 TEST(ArrayTest, Find) {
203 auto arr = pdfium::MakeRetain<CPDF_Array>();
204 auto dict0 = pdfium::MakeRetain<CPDF_Dictionary>();
205 auto dict1 = pdfium::MakeRetain<CPDF_Dictionary>();
206 auto dict2 = pdfium::MakeRetain<CPDF_Dictionary>();
207 arr->Append(dict0);
208 arr->Append(dict1);
209
210 std::optional<size_t> maybe_found = arr->Find(nullptr);
211 EXPECT_FALSE(maybe_found.has_value());
212
213 maybe_found = arr->Find(dict0.Get());
214 ASSERT_TRUE(maybe_found.has_value());
215 EXPECT_EQ(0u, maybe_found.value());
216
217 maybe_found = arr->Find(dict1.Get());
218 ASSERT_TRUE(maybe_found.has_value());
219 EXPECT_EQ(1u, maybe_found.value());
220
221 maybe_found = arr->Find(dict2.Get());
222 EXPECT_FALSE(maybe_found.has_value());
223 }
224
TEST(ArrayTest,Contains)225 TEST(ArrayTest, Contains) {
226 auto arr = pdfium::MakeRetain<CPDF_Array>();
227 auto dict0 = pdfium::MakeRetain<CPDF_Dictionary>();
228 auto dict1 = pdfium::MakeRetain<CPDF_Dictionary>();
229 auto dict2 = pdfium::MakeRetain<CPDF_Dictionary>();
230 arr->Append(dict0);
231 arr->Append(dict1);
232 EXPECT_TRUE(arr->Contains(dict0.Get()));
233 EXPECT_TRUE(arr->Contains(dict1.Get()));
234 EXPECT_FALSE(arr->Contains(dict2.Get()));
235 }
236
TEST(ArrayTest,Iterator)237 TEST(ArrayTest, Iterator) {
238 constexpr std::array<int, 10> elems = {
239 {-23, -11, 3, 455, 2345877, 0, 7895330, -12564334, 10000, -100000}};
240 auto arr = pdfium::MakeRetain<CPDF_Array>();
241 for (size_t i = 0; i < std::size(elems); ++i) {
242 arr->InsertNewAt<CPDF_Number>(i, elems[i]);
243 }
244 size_t index = 0;
245 CPDF_ArrayLocker locker(arr);
246 for (const auto& it : locker) {
247 EXPECT_EQ(elems[index++], it->AsNumber()->GetInteger());
248 }
249 EXPECT_EQ(std::size(elems), index);
250 }
251