Commit e83f3308fbccd34959d325b830118eafe441fe48

Authored by Jay Berkenbilt
1 parent 04419d7c

SparseOHArray

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 +}