Commit e83f3308fbccd34959d325b830118eafe441fe48
1 parent
04419d7c
SparseOHArray
Showing
8 changed files
with
297 additions
and
1 deletions
include/qpdf/QPDFObjectHandle.hh
| ... | ... | @@ -275,6 +275,14 @@ class QPDFObjectHandle |
| 275 | 275 | QPDF_DLL |
| 276 | 276 | bool isReserved(); |
| 277 | 277 | |
| 278 | + // True for objects that are direct nulls or have previously been | |
| 279 | + // resolved to be nulls. Does not attempt to resolve objects. This | |
| 280 | + // is intended for internal use, but it can be used as an | |
| 281 | + // efficient way to check for nulls if you don't mind unresolved | |
| 282 | + // indirect nulls being false negatives. | |
| 283 | + QPDF_DLL | |
| 284 | + bool isResolvedNull() const; | |
| 285 | + | |
| 278 | 286 | // This returns true in addition to the query for the specific |
| 279 | 287 | // type for indirect objects. |
| 280 | 288 | QPDF_DLL |
| ... | ... | @@ -926,6 +934,7 @@ class QPDFObjectHandle |
| 926 | 934 | friend class QPDF_Dictionary; |
| 927 | 935 | friend class QPDF_Array; |
| 928 | 936 | friend class QPDF_Stream; |
| 937 | + friend class SparseOHArray; | |
| 929 | 938 | private: |
| 930 | 939 | static void releaseResolved(QPDFObjectHandle& o) |
| 931 | 940 | { | ... | ... |
libqpdf/QPDFObjectHandle.cc
| ... | ... | @@ -248,6 +248,10 @@ class QPDFObjectTypeAccessor |
| 248 | 248 | { |
| 249 | 249 | return (o && dynamic_cast<T*>(o)); |
| 250 | 250 | } |
| 251 | + static bool check(QPDFObject const* o) | |
| 252 | + { | |
| 253 | + return (o && dynamic_cast<T const*>(o)); | |
| 254 | + } | |
| 251 | 255 | }; |
| 252 | 256 | |
| 253 | 257 | bool |
| ... | ... | @@ -258,6 +262,12 @@ QPDFObjectHandle::isBool() |
| 258 | 262 | } |
| 259 | 263 | |
| 260 | 264 | bool |
| 265 | +QPDFObjectHandle::isResolvedNull() const | |
| 266 | +{ | |
| 267 | + return QPDFObjectTypeAccessor<QPDF_Null>::check(m->obj.getPointer()); | |
| 268 | +} | |
| 269 | + | |
| 270 | +bool | |
| 261 | 271 | QPDFObjectHandle::isNull() |
| 262 | 272 | { |
| 263 | 273 | dereference(); | ... | ... |
libqpdf/SparseOHArray.cc
0 โ 100644
| 1 | +#include <qpdf/SparseOHArray.hh> | |
| 2 | +#include <stdexcept> | |
| 3 | + | |
| 4 | +SparseOHArray::SparseOHArray() : | |
| 5 | + n_elements(0) | |
| 6 | +{ | |
| 7 | +} | |
| 8 | + | |
| 9 | +size_t | |
| 10 | +SparseOHArray::size() const | |
| 11 | +{ | |
| 12 | + return this->n_elements; | |
| 13 | +} | |
| 14 | + | |
| 15 | +void | |
| 16 | +SparseOHArray::append(QPDFObjectHandle oh) | |
| 17 | +{ | |
| 18 | + if (! oh.isResolvedNull()) | |
| 19 | + { | |
| 20 | + this->elements[this->n_elements] = oh; | |
| 21 | + } | |
| 22 | + ++this->n_elements; | |
| 23 | +} | |
| 24 | + | |
| 25 | +QPDFObjectHandle | |
| 26 | +SparseOHArray::at(size_t idx) const | |
| 27 | +{ | |
| 28 | + if (idx >= this->n_elements) | |
| 29 | + { | |
| 30 | + throw std::logic_error( | |
| 31 | + "INTERNAL ERROR: bounds error accessing SparseOHArray element"); | |
| 32 | + } | |
| 33 | + std::map<size_t, QPDFObjectHandle>::const_iterator iter = | |
| 34 | + this->elements.find(idx); | |
| 35 | + if (iter == this->elements.end()) | |
| 36 | + { | |
| 37 | + return QPDFObjectHandle::newNull(); | |
| 38 | + } | |
| 39 | + else | |
| 40 | + { | |
| 41 | + return (*iter).second; | |
| 42 | + } | |
| 43 | +} | |
| 44 | + | |
| 45 | +void | |
| 46 | +SparseOHArray::remove_last() | |
| 47 | +{ | |
| 48 | + if (this->n_elements == 0) | |
| 49 | + { | |
| 50 | + throw std::logic_error( | |
| 51 | + "INTERNAL ERROR: attempt to remove" | |
| 52 | + " last item from empty SparseOHArray"); | |
| 53 | + } | |
| 54 | + --this->n_elements; | |
| 55 | + this->elements.erase(this->n_elements); | |
| 56 | +} | |
| 57 | + | |
| 58 | +void | |
| 59 | +SparseOHArray::releaseResolved() | |
| 60 | +{ | |
| 61 | + for (std::map<size_t, QPDFObjectHandle>::iterator iter = | |
| 62 | + this->elements.begin(); | |
| 63 | + iter != this->elements.end(); ++iter) | |
| 64 | + { | |
| 65 | + QPDFObjectHandle::ReleaseResolver::releaseResolved((*iter).second); | |
| 66 | + } | |
| 67 | +} | |
| 68 | + | |
| 69 | +void | |
| 70 | +SparseOHArray::setAt(size_t idx, QPDFObjectHandle oh) | |
| 71 | +{ | |
| 72 | + if (idx >= this->n_elements) | |
| 73 | + { | |
| 74 | + throw std::logic_error("bounds error setting item in SparseOHArray"); | |
| 75 | + } | |
| 76 | + if (oh.isResolvedNull()) | |
| 77 | + { | |
| 78 | + this->elements.erase(idx); | |
| 79 | + } | |
| 80 | + else | |
| 81 | + { | |
| 82 | + this->elements[idx] = oh; | |
| 83 | + } | |
| 84 | +} | |
| 85 | + | |
| 86 | +void | |
| 87 | +SparseOHArray::erase(size_t idx) | |
| 88 | +{ | |
| 89 | + if (idx >= this->n_elements) | |
| 90 | + { | |
| 91 | + throw std::logic_error("bounds error erasing item from SparseOHArray"); | |
| 92 | + } | |
| 93 | + std::map<size_t, QPDFObjectHandle> dest; | |
| 94 | + for (std::map<size_t, QPDFObjectHandle>::iterator iter = | |
| 95 | + this->elements.begin(); | |
| 96 | + iter != this->elements.end(); ++iter) | |
| 97 | + { | |
| 98 | + if ((*iter).first < idx) | |
| 99 | + { | |
| 100 | + dest.insert(*iter); | |
| 101 | + } | |
| 102 | + else if ((*iter).first > idx) | |
| 103 | + { | |
| 104 | + dest[(*iter).first - 1] = (*iter).second; | |
| 105 | + } | |
| 106 | + } | |
| 107 | + this->elements = dest; | |
| 108 | + --this->n_elements; | |
| 109 | +} | |
| 110 | + | |
| 111 | +void | |
| 112 | +SparseOHArray::insert(size_t idx, QPDFObjectHandle oh) | |
| 113 | +{ | |
| 114 | + if (idx > this->n_elements) | |
| 115 | + { | |
| 116 | + throw std::logic_error("bounds error inserting item to SparseOHArray"); | |
| 117 | + } | |
| 118 | + else if (idx == this->n_elements) | |
| 119 | + { | |
| 120 | + // Allow inserting to the last position | |
| 121 | + append(oh); | |
| 122 | + } | |
| 123 | + else | |
| 124 | + { | |
| 125 | + std::map<size_t, QPDFObjectHandle> dest; | |
| 126 | + for (std::map<size_t, QPDFObjectHandle>::iterator iter = | |
| 127 | + this->elements.begin(); | |
| 128 | + iter != this->elements.end(); ++iter) | |
| 129 | + { | |
| 130 | + if ((*iter).first < idx) | |
| 131 | + { | |
| 132 | + dest.insert(*iter); | |
| 133 | + } | |
| 134 | + else | |
| 135 | + { | |
| 136 | + dest[(*iter).first + 1] = (*iter).second; | |
| 137 | + } | |
| 138 | + } | |
| 139 | + this->elements = dest; | |
| 140 | + this->elements[idx] = oh; | |
| 141 | + ++this->n_elements; | |
| 142 | + } | |
| 143 | +} | ... | ... |
libqpdf/build.mk
libqpdf/qpdf/SparseOHArray.hh
0 โ 100644
| 1 | +#ifndef QPDF_SPARSEOHARRAY_HH | |
| 2 | +#define QPDF_SPARSEOHARRAY_HH | |
| 3 | + | |
| 4 | +#include <qpdf/QPDFObjectHandle.hh> | |
| 5 | +#include <map> | |
| 6 | + | |
| 7 | +class SparseOHArray | |
| 8 | +{ | |
| 9 | + public: | |
| 10 | + QPDF_DLL | |
| 11 | + SparseOHArray(); | |
| 12 | + QPDF_DLL | |
| 13 | + size_t size() const; | |
| 14 | + QPDF_DLL | |
| 15 | + void append(QPDFObjectHandle oh); | |
| 16 | + QPDF_DLL | |
| 17 | + QPDFObjectHandle at(size_t idx) const; | |
| 18 | + QPDF_DLL | |
| 19 | + void remove_last(); | |
| 20 | + QPDF_DLL | |
| 21 | + void releaseResolved(); | |
| 22 | + QPDF_DLL | |
| 23 | + void setAt(size_t idx, QPDFObjectHandle oh); | |
| 24 | + QPDF_DLL | |
| 25 | + void erase(size_t idx); | |
| 26 | + QPDF_DLL | |
| 27 | + void insert(size_t idx, QPDFObjectHandle oh); | |
| 28 | + | |
| 29 | + private: | |
| 30 | + std::map<size_t, QPDFObjectHandle> elements; | |
| 31 | + size_t n_elements; | |
| 32 | +}; | |
| 33 | + | |
| 34 | +#endif // QPDF_SPARSEOHARRAY_HH | ... | ... |
libtests/build.mk
libtests/qtest/sparse_array.test
0 โ 100644
| 1 | +#!/usr/bin/env perl | |
| 2 | +require 5.008; | |
| 3 | +use warnings; | |
| 4 | +use strict; | |
| 5 | + | |
| 6 | +require TestDriver; | |
| 7 | + | |
| 8 | +my $td = new TestDriver('sparse array'); | |
| 9 | + | |
| 10 | +$td->runtest("sparse_array", | |
| 11 | + {$td->COMMAND => "sparse_array"}, | |
| 12 | + {$td->STRING => "sparse array tests done\n", | |
| 13 | + $td->EXIT_STATUS => 0}, | |
| 14 | + $td->NORMALIZE_NEWLINES); | |
| 15 | + | |
| 16 | +$td->report(1); | ... | ... |
libtests/sparse_array.cc
0 โ 100644
| 1 | +#include <qpdf/SparseOHArray.hh> | |
| 2 | +#include <assert.h> | |
| 3 | +#include <iostream> | |
| 4 | + | |
| 5 | +int main() | |
| 6 | +{ | |
| 7 | + SparseOHArray a; | |
| 8 | + assert(a.size() == 0); | |
| 9 | + | |
| 10 | + a.append(QPDFObjectHandle::parse("1")); | |
| 11 | + a.append(QPDFObjectHandle::parse("(potato)")); | |
| 12 | + a.append(QPDFObjectHandle::parse("null")); | |
| 13 | + a.append(QPDFObjectHandle::parse("null")); | |
| 14 | + a.append(QPDFObjectHandle::parse("/Quack")); | |
| 15 | + assert(a.size() == 5); | |
| 16 | + assert(a.at(0).isInteger() && (a.at(0).getIntValue() == 1)); | |
| 17 | + assert(a.at(1).isString() && (a.at(1).getStringValue() == "potato")); | |
| 18 | + assert(a.at(2).isNull()); | |
| 19 | + assert(a.at(3).isNull()); | |
| 20 | + assert(a.at(4).isName() && (a.at(4).getName() == "/Quack")); | |
| 21 | + | |
| 22 | + a.insert(4, QPDFObjectHandle::parse("/BeforeQuack")); | |
| 23 | + assert(a.size() == 6); | |
| 24 | + assert(a.at(0).isInteger() && (a.at(0).getIntValue() == 1)); | |
| 25 | + assert(a.at(4).isName() && (a.at(4).getName() == "/BeforeQuack")); | |
| 26 | + assert(a.at(5).isName() && (a.at(5).getName() == "/Quack")); | |
| 27 | + | |
| 28 | + a.insert(2, QPDFObjectHandle::parse("/Third")); | |
| 29 | + assert(a.size() == 7); | |
| 30 | + assert(a.at(1).isString() && (a.at(1).getStringValue() == "potato")); | |
| 31 | + assert(a.at(2).isName() && (a.at(2).getName() == "/Third")); | |
| 32 | + assert(a.at(3).isNull()); | |
| 33 | + assert(a.at(6).isName() && (a.at(6).getName() == "/Quack")); | |
| 34 | + | |
| 35 | + a.insert(0, QPDFObjectHandle::parse("/First")); | |
| 36 | + assert(a.size() == 8); | |
| 37 | + assert(a.at(0).isName() && (a.at(0).getName() == "/First")); | |
| 38 | + assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1)); | |
| 39 | + assert(a.at(7).isName() && (a.at(7).getName() == "/Quack")); | |
| 40 | + | |
| 41 | + a.erase(6); | |
| 42 | + assert(a.size() == 7); | |
| 43 | + assert(a.at(0).isName() && (a.at(0).getName() == "/First")); | |
| 44 | + assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1)); | |
| 45 | + assert(a.at(5).isNull()); | |
| 46 | + assert(a.at(6).isName() && (a.at(6).getName() == "/Quack")); | |
| 47 | + | |
| 48 | + a.erase(6); | |
| 49 | + assert(a.size() == 6); | |
| 50 | + assert(a.at(0).isName() && (a.at(0).getName() == "/First")); | |
| 51 | + assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1)); | |
| 52 | + assert(a.at(3).isName() && (a.at(3).getName() == "/Third")); | |
| 53 | + assert(a.at(4).isNull()); | |
| 54 | + assert(a.at(5).isNull()); | |
| 55 | + | |
| 56 | + a.setAt(4, QPDFObjectHandle::parse("12")); | |
| 57 | + assert(a.at(4).isInteger() && (a.at(4).getIntValue() == 12)); | |
| 58 | + a.setAt(4, QPDFObjectHandle::newNull()); | |
| 59 | + assert(a.at(4).isNull()); | |
| 60 | + | |
| 61 | + a.remove_last(); | |
| 62 | + assert(a.size() == 5); | |
| 63 | + assert(a.at(0).isName() && (a.at(0).getName() == "/First")); | |
| 64 | + assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1)); | |
| 65 | + assert(a.at(3).isName() && (a.at(3).getName() == "/Third")); | |
| 66 | + assert(a.at(4).isNull()); | |
| 67 | + | |
| 68 | + a.remove_last(); | |
| 69 | + assert(a.size() == 4); | |
| 70 | + assert(a.at(0).isName() && (a.at(0).getName() == "/First")); | |
| 71 | + assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1)); | |
| 72 | + assert(a.at(3).isName() && (a.at(3).getName() == "/Third")); | |
| 73 | + | |
| 74 | + a.remove_last(); | |
| 75 | + assert(a.size() == 3); | |
| 76 | + assert(a.at(0).isName() && (a.at(0).getName() == "/First")); | |
| 77 | + assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1)); | |
| 78 | + assert(a.at(2).isString() && (a.at(2).getStringValue() == "potato")); | |
| 79 | + | |
| 80 | + std::cout << "sparse array tests done" << std::endl; | |
| 81 | + return 0; | |
| 82 | +} | ... | ... |