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,6 +275,14 @@ class QPDFObjectHandle | ||
| 275 | QPDF_DLL | 275 | QPDF_DLL |
| 276 | bool isReserved(); | 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 | // This returns true in addition to the query for the specific | 286 | // This returns true in addition to the query for the specific |
| 279 | // type for indirect objects. | 287 | // type for indirect objects. |
| 280 | QPDF_DLL | 288 | QPDF_DLL |
| @@ -926,6 +934,7 @@ class QPDFObjectHandle | @@ -926,6 +934,7 @@ class QPDFObjectHandle | ||
| 926 | friend class QPDF_Dictionary; | 934 | friend class QPDF_Dictionary; |
| 927 | friend class QPDF_Array; | 935 | friend class QPDF_Array; |
| 928 | friend class QPDF_Stream; | 936 | friend class QPDF_Stream; |
| 937 | + friend class SparseOHArray; | ||
| 929 | private: | 938 | private: |
| 930 | static void releaseResolved(QPDFObjectHandle& o) | 939 | static void releaseResolved(QPDFObjectHandle& o) |
| 931 | { | 940 | { |
libqpdf/QPDFObjectHandle.cc
| @@ -248,6 +248,10 @@ class QPDFObjectTypeAccessor | @@ -248,6 +248,10 @@ class QPDFObjectTypeAccessor | ||
| 248 | { | 248 | { |
| 249 | return (o && dynamic_cast<T*>(o)); | 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 | bool | 257 | bool |
| @@ -258,6 +262,12 @@ QPDFObjectHandle::isBool() | @@ -258,6 +262,12 @@ QPDFObjectHandle::isBool() | ||
| 258 | } | 262 | } |
| 259 | 263 | ||
| 260 | bool | 264 | bool |
| 265 | +QPDFObjectHandle::isResolvedNull() const | ||
| 266 | +{ | ||
| 267 | + return QPDFObjectTypeAccessor<QPDF_Null>::check(m->obj.getPointer()); | ||
| 268 | +} | ||
| 269 | + | ||
| 270 | +bool | ||
| 261 | QPDFObjectHandle::isNull() | 271 | QPDFObjectHandle::isNull() |
| 262 | { | 272 | { |
| 263 | dereference(); | 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
| @@ -76,6 +76,7 @@ SRCS_libqpdf = \ | @@ -76,6 +76,7 @@ SRCS_libqpdf = \ | ||
| 76 | libqpdf/QUtil.cc \ | 76 | libqpdf/QUtil.cc \ |
| 77 | libqpdf/RC4.cc \ | 77 | libqpdf/RC4.cc \ |
| 78 | libqpdf/SecureRandomDataProvider.cc \ | 78 | libqpdf/SecureRandomDataProvider.cc \ |
| 79 | + libqpdf/SparseOHArray.cc \ | ||
| 79 | libqpdf/qpdf-c.cc \ | 80 | libqpdf/qpdf-c.cc \ |
| 80 | libqpdf/rijndael.cc \ | 81 | libqpdf/rijndael.cc \ |
| 81 | libqpdf/sha2.c \ | 82 | libqpdf/sha2.c \ |
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
| @@ -22,7 +22,8 @@ BINS_libtests = \ | @@ -22,7 +22,8 @@ BINS_libtests = \ | ||
| 22 | random \ | 22 | random \ |
| 23 | rc4 \ | 23 | rc4 \ |
| 24 | runlength \ | 24 | runlength \ |
| 25 | - sha2 | 25 | + sha2 \ |
| 26 | + sparse_array | ||
| 26 | 27 | ||
| 27 | TARGETS_libtests = $(foreach B,$(BINS_libtests),libtests/$(OUTPUT_DIR)/$(call binname,$(B))) | 28 | TARGETS_libtests = $(foreach B,$(BINS_libtests),libtests/$(OUTPUT_DIR)/$(call binname,$(B))) |
| 28 | 29 |
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 | +} |